aboutsummaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
authorNeo2003 <none@none>2008-10-02 16:23:55 -0500
committerNeo2003 <none@none>2008-10-02 16:23:55 -0500
commit9b1c0e006f20091f28f3f468cfcab1feb51286bd (patch)
treeb5d1ba94a656e6679f8737f9ea6bed1239b73b14 /src/game
[svn] * Proper SVN structureinit
--HG-- branch : trunk
Diffstat (limited to 'src/game')
-rw-r--r--src/game/AccountMgr.cpp195
-rw-r--r--src/game/AccountMgr.h56
-rw-r--r--src/game/AddonHandler.cpp208
-rw-r--r--src/game/AddonHandler.h37
-rw-r--r--src/game/AggressorAI.cpp155
-rw-r--r--src/game/AggressorAI.h53
-rw-r--r--src/game/AnimalRandomMovementGenerator.h28
-rw-r--r--src/game/ArenaTeam.cpp517
-rw-r--r--src/game/ArenaTeam.h175
-rw-r--r--src/game/ArenaTeamHandler.cpp462
-rw-r--r--src/game/AuctionHouse.cpp747
-rw-r--r--src/game/AuctionHouseObject.h104
-rw-r--r--src/game/Bag.cpp249
-rw-r--r--src/game/Bag.h74
-rw-r--r--src/game/BattleGround.cpp1153
-rw-r--r--src/game/BattleGround.h486
-rw-r--r--src/game/BattleGroundAA.cpp63
-rw-r--r--src/game/BattleGroundAA.h47
-rw-r--r--src/game/BattleGroundAB.cpp649
-rw-r--r--src/game/BattleGroundAB.h284
-rw-r--r--src/game/BattleGroundAV.cpp120
-rw-r--r--src/game/BattleGroundAV.h59
-rw-r--r--src/game/BattleGroundBE.cpp212
-rw-r--r--src/game/BattleGroundBE.h75
-rw-r--r--src/game/BattleGroundEY.cpp909
-rw-r--r--src/game/BattleGroundEY.h372
-rw-r--r--src/game/BattleGroundHandler.cpp621
-rw-r--r--src/game/BattleGroundMgr.cpp867
-rw-r--r--src/game/BattleGroundMgr.h178
-rw-r--r--src/game/BattleGroundNA.cpp181
-rw-r--r--src/game/BattleGroundNA.h69
-rw-r--r--src/game/BattleGroundRL.cpp180
-rw-r--r--src/game/BattleGroundRL.h65
-rw-r--r--src/game/BattleGroundWS.cpp678
-rw-r--r--src/game/BattleGroundWS.h185
-rw-r--r--src/game/Cell.h162
-rw-r--r--src/game/CellImpl.h130
-rw-r--r--src/game/Channel.cpp994
-rw-r--r--src/game/Channel.h291
-rw-r--r--src/game/ChannelHandler.cpp387
-rw-r--r--src/game/ChannelMgr.h101
-rw-r--r--src/game/CharacterHandler.cpp1065
-rw-r--r--src/game/Chat.cpp1103
-rw-r--r--src/game/Chat.h406
-rw-r--r--src/game/ChatHandler.cpp583
-rw-r--r--src/game/CombatHandler.cpp95
-rw-r--r--src/game/ConfusedMovementGenerator.cpp155
-rw-r--r--src/game/ConfusedMovementGenerator.h48
-rw-r--r--src/game/Corpse.cpp211
-rw-r--r--src/game/Corpse.h100
-rw-r--r--src/game/Creature.cpp1993
-rw-r--r--src/game/Creature.h611
-rw-r--r--src/game/CreatureAI.cpp26
-rw-r--r--src/game/CreatureAI.h121
-rw-r--r--src/game/CreatureAIImpl.h30
-rw-r--r--src/game/CreatureAIRegistry.cpp47
-rw-r--r--src/game/CreatureAIRegistry.h26
-rw-r--r--src/game/CreatureAISelector.cpp120
-rw-r--r--src/game/CreatureAISelector.h31
-rw-r--r--src/game/DestinationHolder.cpp19
-rw-r--r--src/game/DestinationHolder.h62
-rw-r--r--src/game/DestinationHolderImp.h243
-rw-r--r--src/game/DuelHandler.cpp90
-rw-r--r--src/game/DynamicObject.cpp152
-rw-r--r--src/game/DynamicObject.h72
-rw-r--r--src/game/FleeingMovementGenerator.cpp379
-rw-r--r--src/game/FleeingMovementGenerator.h62
-rw-r--r--src/game/FollowerRefManager.h31
-rw-r--r--src/game/FollowerReference.cpp36
-rw-r--r--src/game/FollowerReference.h34
-rw-r--r--src/game/Formulas.h198
-rw-r--r--src/game/GameEvent.cpp659
-rw-r--r--src/game/GameEvent.h93
-rw-r--r--src/game/GameObject.cpp1253
-rw-r--r--src/game/GameObject.h592
-rw-r--r--src/game/GlobalEvents.cpp82
-rw-r--r--src/game/GlobalEvents.h29
-rw-r--r--src/game/GossipDef.cpp765
-rw-r--r--src/game/GossipDef.h193
-rw-r--r--src/game/GridDefines.h170
-rw-r--r--src/game/GridNotifiers.cpp218
-rw-r--r--src/game/GridNotifiers.h812
-rw-r--r--src/game/GridNotifiersImpl.h489
-rw-r--r--src/game/GridStates.cpp73
-rw-r--r--src/game/GridStates.h72
-rw-r--r--src/game/Group.cpp1406
-rw-r--r--src/game/Group.h368
-rw-r--r--src/game/GroupHandler.cpp934
-rw-r--r--src/game/GroupRefManager.h33
-rw-r--r--src/game/GroupReference.cpp39
-rw-r--r--src/game/GroupReference.h41
-rw-r--r--src/game/GuardAI.cpp150
-rw-r--r--src/game/GuardAI.h54
-rw-r--r--src/game/Guild.cpp1940
-rw-r--r--src/game/Guild.h435
-rw-r--r--src/game/GuildHandler.cpp1815
-rw-r--r--src/game/HateMatrix.h84
-rw-r--r--src/game/HomeMovementGenerator.cpp86
-rw-r--r--src/game/HomeMovementGenerator.h54
-rw-r--r--src/game/HostilRefManager.cpp147
-rw-r--r--src/game/HostilRefManager.h66
-rw-r--r--src/game/IdleMovementGenerator.cpp49
-rw-r--r--src/game/IdleMovementGenerator.h52
-rw-r--r--src/game/InstanceData.cpp29
-rw-r--r--src/game/InstanceData.h66
-rw-r--r--src/game/InstanceSaveMgr.cpp649
-rw-r--r--src/game/InstanceSaveMgr.h172
-rw-r--r--src/game/Item.cpp915
-rw-r--r--src/game/Item.h298
-rw-r--r--src/game/ItemEnchantmentMgr.cpp202
-rw-r--r--src/game/ItemEnchantmentMgr.h27
-rw-r--r--src/game/ItemHandler.cpp1221
-rw-r--r--src/game/ItemPrototype.h565
-rw-r--r--src/game/LFGHandler.cpp337
-rw-r--r--src/game/Language.h665
-rw-r--r--src/game/Level0.cpp233
-rw-r--r--src/game/Level1.cpp2351
-rw-r--r--src/game/Level2.cpp3948
-rw-r--r--src/game/Level3.cpp5424
-rw-r--r--src/game/LootHandler.cpp494
-rw-r--r--src/game/LootMgr.cpp1239
-rw-r--r--src/game/LootMgr.h331
-rw-r--r--src/game/Mail.cpp838
-rw-r--r--src/game/Mail.h212
-rw-r--r--src/game/Makefile.am278
-rw-r--r--src/game/Map.cpp1795
-rw-r--r--src/game/Map.h396
-rw-r--r--src/game/MapInstanced.cpp252
-rw-r--r--src/game/MapInstanced.h70
-rw-r--r--src/game/MapManager.cpp340
-rw-r--r--src/game/MapManager.h141
-rw-r--r--src/game/MiscHandler.cpp1761
-rw-r--r--src/game/MotionMaster.cpp342
-rw-r--r--src/game/MotionMaster.h92
-rw-r--r--src/game/MovementGenerator.cpp23
-rw-r--r--src/game/MovementGenerator.h98
-rw-r--r--src/game/MovementGeneratorImpl.h31
-rw-r--r--src/game/MovementHandler.cpp585
-rw-r--r--src/game/NPCHandler.cpp832
-rw-r--r--src/game/NPCHandler.h77
-rw-r--r--src/game/NullCreatureAI.cpp23
-rw-r--r--src/game/NullCreatureAI.h42
-rw-r--r--src/game/Object.cpp1631
-rw-r--r--src/game/Object.h460
-rw-r--r--src/game/ObjectAccessor.cpp648
-rw-r--r--src/game/ObjectAccessor.h226
-rw-r--r--src/game/ObjectDefines.h114
-rw-r--r--src/game/ObjectGridLoader.cpp304
-rw-r--r--src/game/ObjectGridLoader.h108
-rw-r--r--src/game/ObjectMgr.cpp6512
-rw-r--r--src/game/ObjectMgr.h780
-rw-r--r--src/game/ObjectPosSelector.cpp157
-rw-r--r--src/game/ObjectPosSelector.h155
-rw-r--r--src/game/Opcodes.cpp1090
-rw-r--r--src/game/Opcodes.h1125
-rw-r--r--src/game/Path.h85
-rw-r--r--src/game/Pet.cpp1750
-rw-r--r--src/game/Pet.h245
-rw-r--r--src/game/PetAI.cpp352
-rw-r--r--src/game/PetAI.h56
-rw-r--r--src/game/PetHandler.cpp648
-rw-r--r--src/game/PetitionsHandler.cpp936
-rw-r--r--src/game/Player.cpp18130
-rw-r--r--src/game/Player.h2310
-rw-r--r--src/game/PlayerDump.cpp605
-rw-r--r--src/game/PlayerDump.h108
-rw-r--r--src/game/PointMovementGenerator.cpp73
-rw-r--r--src/game/PointMovementGenerator.h51
-rw-r--r--src/game/QueryHandler.cpp430
-rw-r--r--src/game/QuestDef.cpp199
-rw-r--r--src/game/QuestDef.h330
-rw-r--r--src/game/QuestHandler.cpp651
-rw-r--r--src/game/RandomMovementGenerator.cpp159
-rw-r--r--src/game/RandomMovementGenerator.h50
-rw-r--r--src/game/ReactorAI.cpp127
-rw-r--r--src/game/ReactorAI.h44
-rw-r--r--src/game/ScriptCalls.cpp95
-rw-r--r--src/game/ScriptCalls.h90
-rw-r--r--src/game/SharedDefines.h1997
-rw-r--r--src/game/SkillDiscovery.cpp165
-rw-r--r--src/game/SkillDiscovery.h28
-rw-r--r--src/game/SkillExtraItems.cpp144
-rw-r--r--src/game/SkillExtraItems.h30
-rw-r--r--src/game/SkillHandler.cpp179
-rw-r--r--src/game/SocialMgr.cpp309
-rw-r--r--src/game/SocialMgr.h153
-rw-r--r--src/game/Spell.cpp5115
-rw-r--r--src/game/Spell.h694
-rw-r--r--src/game/SpellAuraDefines.h313
-rw-r--r--src/game/SpellAuras.cpp6360
-rw-r--r--src/game/SpellAuras.h375
-rw-r--r--src/game/SpellEffects.cpp6090
-rw-r--r--src/game/SpellHandler.cpp460
-rw-r--r--src/game/SpellMgr.cpp2268
-rw-r--r--src/game/SpellMgr.h859
-rw-r--r--src/game/StatSystem.cpp955
-rw-r--r--src/game/TargetedMovementGenerator.cpp202
-rw-r--r--src/game/TargetedMovementGenerator.h73
-rw-r--r--src/game/TaxiHandler.cpp278
-rw-r--r--src/game/TemporarySummon.cpp182
-rw-r--r--src/game/TemporarySummon.h41
-rw-r--r--src/game/ThreatManager.cpp472
-rw-r--r--src/game/ThreatManager.h214
-rw-r--r--src/game/Tools.h26
-rw-r--r--src/game/Totem.cpp161
-rw-r--r--src/game/Totem.h60
-rw-r--r--src/game/TotemAI.cpp123
-rw-r--r--src/game/TotemAI.h46
-rw-r--r--src/game/TradeHandler.cpp633
-rw-r--r--src/game/Transports.cpp525
-rw-r--r--src/game/Transports.h115
-rw-r--r--src/game/Traveller.h102
-rw-r--r--src/game/Unit.cpp10808
-rw-r--r--src/game/Unit.h1331
-rw-r--r--src/game/UnitEvents.h136
-rw-r--r--src/game/UpdateData.cpp160
-rw-r--r--src/game/UpdateData.h67
-rw-r--r--src/game/UpdateFields.h451
-rw-r--r--src/game/UpdateMask.h124
-rw-r--r--src/game/VoiceChatHandler.cpp45
-rw-r--r--src/game/WaypointManager.cpp272
-rw-r--r--src/game/WaypointManager.h89
-rw-r--r--src/game/WaypointMovementGenerator.cpp648
-rw-r--r--src/game/WaypointMovementGenerator.h136
-rw-r--r--src/game/Weather.cpp318
-rw-r--r--src/game/Weather.h72
-rw-r--r--src/game/World.cpp2460
-rw-r--r--src/game/World.h534
-rw-r--r--src/game/WorldLog.cpp51
-rw-r--r--src/game/WorldLog.h76
-rw-r--r--src/game/WorldSession.cpp463
-rw-r--r--src/game/WorldSession.h638
-rw-r--r--src/game/WorldSocket.cpp664
-rw-r--r--src/game/WorldSocket.h179
-rw-r--r--src/game/WorldSocketMgr.cpp55
-rw-r--r--src/game/WorldSocketMgr.h47
-rw-r--r--src/game/debugcmds.cpp514
-rw-r--r--src/game/tools.cpp114
238 files changed, 152407 insertions, 0 deletions
diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp
new file mode 100644
index 00000000000..3b14db46dba
--- /dev/null
+++ b/src/game/AccountMgr.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "AccountMgr.h"
+#include "Database/DatabaseEnv.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Policies/SingletonImp.h"
+#include "Util.h"
+
+#ifdef DO_POSTGRESQL
+extern DatabasePostgre loginDatabase;
+#else
+extern DatabaseMysql loginDatabase;
+#endif
+
+INSTANTIATE_SINGLETON_1(AccountMgr);
+
+AccountMgr::AccountMgr()
+{}
+
+AccountMgr::~AccountMgr()
+{}
+
+AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password)
+{
+ if(utf8length(username) > MAX_ACCOUNT_STR)
+ return AOR_NAME_TOO_LONG; // username's too long
+
+ normilizeString(username);
+ normilizeString(password);
+
+ loginDatabase.escape_string(username);
+ loginDatabase.escape_string(password);
+
+ 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()))
+ return AOR_DB_INTERNAL_ERROR; // unexpected error
+ loginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM account, realmlist WHERE account.id NOT IN (SELECT acctid FROM realmcharacters)");
+
+ return AOR_OK; // everything's fine
+}
+
+AccountOpResult AccountMgr::DeleteAccount(uint32 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;
+
+ result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE account='%d'",accid);
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guidlo = fields[0].GetUInt32();
+ uint64 guid = MAKE_NEW_GUID(guidlo, 0, HIGHGUID_PLAYER);
+
+ // kick if player currently
+ if(Player* p = objmgr.GetPlayer(guid))
+ {
+ WorldSession* s = p->GetSession();
+ s->KickPlayer(); // mark session to remove at next session list update
+ s->LogoutPlayer(false); // logout player without waiting next session list update
+ }
+
+ Player::DeleteFromDB(guid, accid, false); // no need to update realm characters
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ // table realm specific but common for all characters of account for realm
+ CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid);
+
+ loginDatabase.BeginTransaction();
+
+ bool res =
+ loginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) &&
+ loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid);
+
+ loginDatabase.CommitTransaction();
+
+ if(!res)
+ return AOR_DB_INTERNAL_ERROR; // unexpected error;
+
+ return AOR_OK;
+}
+
+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);
+ if(!result)
+ return AOR_NAME_NOT_EXIST; // account doesn't exist
+ delete result;
+
+ if(utf8length(new_uname) > MAX_ACCOUNT_STR)
+ return AOR_NAME_TOO_LONG;
+
+ if(utf8length(new_passwd) > MAX_ACCOUNT_STR)
+ return AOR_PASS_TOO_LONG;
+
+ normilizeString(new_uname);
+ normilizeString(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))
+ return AOR_DB_INTERNAL_ERROR; // unexpected error
+
+ return AOR_OK;
+}
+
+AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd)
+{
+ 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;
+
+ if (utf8length(new_passwd) > MAX_ACCOUNT_STR)
+ return AOR_PASS_TOO_LONG;
+
+ normilizeString(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))
+ return AOR_DB_INTERNAL_ERROR; // unexpected error
+
+ return AOR_OK;
+}
+
+uint32 AccountMgr::GetId(std::string username)
+{
+ loginDatabase.escape_string(username);
+ QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str());
+ if(!result)
+ return 0;
+ else
+ {
+ uint32 id = (*result)[0].GetUInt32();
+ delete result;
+ return id;
+ }
+}
+
+bool AccountMgr::CheckPassword(uint32 accid, std::string passwd)
+{
+ normilizeString(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());
+ if (result)
+ {
+ delete result;
+ return true;
+ }
+
+ return false;
+}
+
+bool AccountMgr::normilizeString(std::string& utf8str)
+{
+ wchar_t wstr_buf[MAX_ACCOUNT_STR+1];
+
+ size_t wstr_len = MAX_ACCOUNT_STR;
+ if(!Utf8toWStr(utf8str,wstr_buf,wstr_len))
+ return false;
+
+ std::transform( &wstr_buf[0], wstr_buf+wstr_len, &wstr_buf[0], wcharToUpperOnlyLatin );
+
+ return WStrToUtf8(wstr_buf,wstr_len,utf8str);
+}
diff --git a/src/game/AccountMgr.h b/src/game/AccountMgr.h
new file mode 100644
index 00000000000..ed04773a8d3
--- /dev/null
+++ b/src/game/AccountMgr.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _ACCMGR_H
+#define _ACCMGR_H
+
+#include "Common.h"
+#include "Policies/Singleton.h"
+#include <string>
+
+enum AccountOpResult
+{
+ AOR_OK,
+ AOR_NAME_TOO_LONG,
+ AOR_PASS_TOO_LONG,
+ AOR_NAME_ALREDY_EXIST,
+ AOR_NAME_NOT_EXIST,
+ AOR_DB_INTERNAL_ERROR
+};
+
+#define MAX_ACCOUNT_STR 16
+
+class AccountMgr
+{
+ public:
+ AccountMgr();
+ ~AccountMgr();
+
+ AccountOpResult CreateAccount(std::string username, std::string password);
+ AccountOpResult DeleteAccount(uint32 accid);
+ AccountOpResult ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd);
+ AccountOpResult ChangePassword(uint32 accid, std::string new_passwd);
+ bool CheckPassword(uint32 accid, std::string passwd);
+
+ uint32 GetId(std::string username);
+
+ static bool normilizeString(std::string& utf8str);
+};
+
+#define accmgr MaNGOS::Singleton<AccountMgr>::Instance()
+#endif
diff --git a/src/game/AddonHandler.cpp b/src/game/AddonHandler.cpp
new file mode 100644
index 00000000000..a03ea0359ad
--- /dev/null
+++ b/src/game/AddonHandler.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "AddonHandler.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Policies/SingletonImp.h"
+#include "zlib/zlib.h"
+
+INSTANTIATE_SINGLETON_1( AddonHandler );
+
+AddonHandler::AddonHandler()
+{
+}
+
+AddonHandler::~AddonHandler()
+{
+}
+
+bool AddonHandler::BuildAddonPacket(WorldPacket *Source, WorldPacket *Target)
+{
+ ByteBuffer AddOnPacked;
+ uLongf AddonRealSize;
+ uint32 CurrentPosition;
+ uint32 TempValue;
+
+ unsigned char tdata[256] =
+ {
+ 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
+ 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
+ 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
+ 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
+ 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
+ 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
+ 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
+ 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
+ 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
+ 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
+ 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
+ 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
+ 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
+ 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
+ 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
+ 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
+ };
+
+ // broken addon packet, can't be received from real client
+ if (Source->rpos() + 4 > Source->size())
+ return false;
+
+ *Source >> TempValue; //get real size of the packed structure
+
+ // empty addon packet, nothing process, can't be received from real client
+ if(!TempValue)
+ return false;
+
+ AddonRealSize = TempValue; //temp value because ZLIB only excepts uLongf
+
+ CurrentPosition = Source->rpos(); //get the position of the pointer in the structure
+
+ AddOnPacked.resize(AddonRealSize); //resize target for zlib action
+
+ if (!uncompress(const_cast<uint8*>(AddOnPacked.contents()), &AddonRealSize, const_cast<uint8*>((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK)
+ {
+ Target->Initialize(SMSG_ADDON_INFO);
+
+ while(AddOnPacked.rpos() < AddOnPacked.size())
+ {
+ std::string AddonNames;
+ uint8 unk6;
+ uint32 crc, unk7;
+
+ // check next addon data format correctness
+ if(AddOnPacked.rpos()+1+4+4+1 > AddOnPacked.size())
+ return false;
+
+ AddOnPacked >> AddonNames;
+
+ // recheck next addon data format correctness
+ if(AddOnPacked.rpos()+4+4+1 > AddOnPacked.size())
+ return false;
+
+ AddOnPacked >> crc >> unk7 >> unk6;
+
+ //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown1 :%x Unknown2 :%x", AddonNames.c_str(), crc, unk7, unk6);
+
+ *Target << (uint8)2;
+
+ uint8 unk1 = 1;
+ *Target << (uint8)unk1;
+ if (unk1)
+ {
+ uint8 unk2 = crc != 0x1c776d01LL; //If addon is Standard addon CRC
+ *Target << (uint8)unk2;
+ if (unk2)
+ Target->append(tdata, sizeof(tdata));
+
+ *Target << (uint32)0;
+ }
+
+ uint8 unk3 = 0;
+ *Target << (uint8)unk3;
+ if (unk3)
+ {
+ // String, 256
+ }
+ }
+ }
+ else
+ {
+ sLog.outError("Addon packet uncompress error :(");
+ return false;
+ }
+ return true;
+}
+
+/* Code use in 1.10.2 when client not ignore ban state sended for addons. Saved for reference if client switch to use server ban state information
+void AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target, uint32 Packetoffset)
+{
+ ByteBuffer AddOnPacked;
+ uLongf AddonRealSize;
+ uint32 CurrentPosition;
+ uint32 TempValue;
+
+ *Source >> TempValue; //get real size of the packed structure
+
+ AddonRealSize = TempValue; //temp value becouse ZLIB only excepts uLongf
+
+ CurrentPosition = Source->rpos(); //get the position of the pointer in the structure
+
+ AddOnPacked.resize(AddonRealSize); //resize target for zlib action
+
+ if (!uncompress((uint8*)AddOnPacked.contents(), &AddonRealSize, (uint8*)(*Source).contents() + CurrentPosition, (*Source).size() - CurrentPosition)!= Z_OK)
+ {
+ bool* AddonAllowed = new bool; //handle addon check and enable-ing
+
+ uint32 Unknown1;
+ uint8 Unknown0;
+
+ AddOnPacked >> Unknown0;
+ AddOnPacked >> Unknown1;
+
+ Target->Initialize(SMSG_ADDON_INFO);
+
+ uint32 i = 5; //offset for addon extraction
+ while(i != AddOnPacked.size())
+ {
+ std::string AddonNames;
+ AddOns* Addonstr = new AddOns;
+ uint8 unk6;
+ uint64 CRCCHECK;
+ AddOnPacked >> AddonNames >> CRCCHECK >> unk6;
+
+ //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK,unk6);
+
+ Addonstr->Name = AddonNames;
+ Addonstr->CRC = CRCCHECK;
+
+ //if not allowed but unknown added to list
+ if (GetAddonStatus(Addonstr, AddonAllowed)) // If addon is new
+ {
+ Addonstr->Enabled = m_Addon_Default; // by default new addons are set from Config file
+ *AddonAllowed = m_Addon_Default; // Set addon allowed on default value
+ _AddAddon(Addonstr);
+ sLog.outDetail("Found new Addon, Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK, unk6);
+ }
+
+ if (CRCCHECK == 0x4C1C776D01LL) //If addon is Standard addon CRC
+ {
+ //value's standard Addons
+ *Target << uint8(0) << uint8(2) << uint8(1) << uint8(0) << uint32(0);
+ }
+ else if (*AddonAllowed) //if addon is Custom addons
+ //value's enable addon
+ *Target << uint8(0x00) << uint8(0x01) << uint8(0x00) << uint8(0x01);
+ else
+ //value's disable addom
+ *Target << uint8(0x00) << uint8(0x0) << uint8(0x00) << uint8(0x0);
+
+ i += AddonNames.size() + 10;
+ }
+ *Target << uint8(0x0);
+
+ //delete mem allocation
+ delete AddonAllowed;
+ }
+ else
+ {
+ //handle uncompress error
+ }
+}
+*/
diff --git a/src/game/AddonHandler.h b/src/game/AddonHandler.h
new file mode 100644
index 00000000000..2e2db5011b2
--- /dev/null
+++ b/src/game/AddonHandler.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __ADDONHANDLER_H
+#define __ADDONHANDLER_H
+
+#include "Common.h"
+#include "Policies/Singleton.h"
+#include "WorldPacket.h"
+#include "Config/ConfigEnv.h"
+
+class AddonHandler
+{
+ public:
+ /* Construction */
+ AddonHandler();
+ ~AddonHandler();
+ //built addon packet
+ bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target);
+};
+#define sAddOnHandler MaNGOS::Singleton<AddonHandler>::Instance()
+#endif
diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp
new file mode 100644
index 00000000000..494acbb9a51
--- /dev/null
+++ b/src/game/AggressorAI.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "AggressorAI.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "VMapFactory.h"
+#include "World.h"
+
+#include <list>
+
+int
+AggressorAI::Permissible(const Creature *creature)
+{
+ // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
+ if( !creature->isCivilian() && !creature->IsNeutralToAll() )
+ return PERMIT_BASE_PROACTIVE;
+
+ return PERMIT_BASE_NO;
+}
+
+AggressorAI::AggressorAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
+{
+}
+
+void
+AggressorAI::MoveInLineOfSight(Unit *u)
+{
+ if( i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
+ return;
+ if( !i_creature.getVictim() && !i_creature.hasUnitState(UNIT_STAT_STUNDED) && u->isTargetableForAttack() &&
+ ( i_creature.IsHostileTo( u ) /*|| u->getVictim() && i_creature.IsFriendlyTo( u->getVictim() )*/ ) &&
+ u->isInAccessablePlaceFor(&i_creature) )
+ {
+ float attackRadius = i_creature.GetAttackDistance(u);
+ if(i_creature.IsWithinDistInMap(u, attackRadius) && i_creature.IsWithinLOSInMap(u) )
+ {
+ AttackStart(u);
+ u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ }
+ }
+}
+
+void AggressorAI::EnterEvadeMode()
+{
+ if( !i_creature.isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow());
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.DeleteThreatList();
+ return;
+ }
+
+ Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
+
+ if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( !victim->isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_creature.GetGUIDLow());
+ //i_state = STATE_LOOK_AT_VICTIM;
+ //i_tracker.Reset(TIME_INTERVAL_LOOK);
+ }
+
+ if(!i_creature.isCharmed())
+ {
+ i_creature.RemoveAllAuras();
+
+ // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+ if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ i_creature.GetMotionMaster()->MoveTargetedHome();
+ }
+
+ i_creature.DeleteThreatList();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.SetLootRecipient(NULL);
+}
+
+void
+AggressorAI::UpdateAI(const uint32 /*diff*/)
+{
+ // update i_victimGuid if i_creature.getVictim() !=0 and changed
+ if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
+ return;
+
+ i_victimGuid = i_creature.getVictim()->GetGUID();
+
+ if( i_creature.isAttackReady() )
+ {
+ if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
+ {
+ i_creature.AttackerStateUpdate(i_creature.getVictim());
+ i_creature.resetAttackTimer();
+ }
+ }
+}
+
+bool
+AggressorAI::IsVisible(Unit *pl) const
+{
+ return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER)
+ && pl->isVisibleForOrDetect(&i_creature,true);
+}
+
+void
+AggressorAI::AttackStart(Unit *u)
+{
+ if( !u )
+ return;
+
+ if(i_creature.Attack(u,true))
+ {
+ i_creature.SetInCombatWith(u);
+ u->SetInCombatWith(&i_creature);
+
+ i_creature.AddThreat(u, 0.0f);
+ // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
+ i_victimGuid = u->GetGUID();
+
+ i_creature.GetMotionMaster()->MoveChase(u);
+ }
+}
diff --git a/src/game/AggressorAI.h b/src/game/AggressorAI.h
new file mode 100644
index 00000000000..b90b12182b2
--- /dev/null
+++ b/src/game/AggressorAI.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_AGGRESSORAI_H
+#define MANGOS_AGGRESSORAI_H
+
+#include "CreatureAI.h"
+#include "Timer.h"
+
+class Creature;
+
+class MANGOS_DLL_DECL AggressorAI : public CreatureAI
+{
+ enum AggressorState
+ {
+ STATE_NORMAL = 1,
+ STATE_LOOK_AT_VICTIM = 2
+ };
+
+ public:
+
+ AggressorAI(Creature &c);
+
+ void MoveInLineOfSight(Unit *);
+ void AttackStart(Unit *);
+ void EnterEvadeMode();
+ bool IsVisible(Unit *) const;
+
+ void UpdateAI(const uint32);
+ static int Permissible(const Creature *);
+
+ private:
+ Creature &i_creature;
+ uint64 i_victimGuid;
+ AggressorState i_state;
+ TimeTracker i_tracker;
+};
+#endif
diff --git a/src/game/AnimalRandomMovementGenerator.h b/src/game/AnimalRandomMovementGenerator.h
new file mode 100644
index 00000000000..a40eb65eb07
--- /dev/null
+++ b/src/game/AnimalRandomMovementGenerator.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_ANIMAL_RANDOMMOVEMENTGENERATOR_H
+#define MANGOS_ANIMAL_RANDOMMOVEMENTGENERATOR_H
+
+/** AnimalRandomMovementGenerator follows the research on
+ * quantifying scale-dependant effects of animal movement
+ * with simple per-location models (R.H. Gardner, R.V. O'Neil,
+ * M.G: Turner and V.H. Dale). It is specifically used on
+ * animal creatures.
+ */
+#endif
diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp
new file mode 100644
index 00000000000..578340a4a8d
--- /dev/null
+++ b/src/game/ArenaTeam.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "WorldPacket.h"
+#include "ObjectMgr.h"
+#include "ArenaTeam.h"
+
+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 = 0;
+ stats.played = 0;
+ stats.rank = 0;
+ stats.rating = 1500;
+ stats.wins = 0;
+ stats.wins2 = 0;
+}
+
+ArenaTeam::~ArenaTeam()
+{
+
+}
+
+bool ArenaTeam::create(uint64 captainGuid, uint32 type, std::string ArenaTeamName)
+{
+ if(!objmgr.GetPlayer(captainGuid)) // player not exist
+ return false;
+ if(objmgr.GetArenaTeamByName(ArenaTeamName)) // arena team with this name already exist
+ return false;
+
+ sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid));
+
+ CaptainGuid = captainGuid;
+ Name = ArenaTeamName;
+ Type = type;
+
+ QueryResult *result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
+ if( result )
+ {
+ Id = (*result)[0].GetUInt32()+1;
+ delete result;
+ }
+ else Id = 1;
+
+ // 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("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);
+ 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,stats.wins,stats.played,stats.wins2,stats.rank);
+
+ CharacterDatabase.CommitTransaction();
+
+ AddMember(CaptainGuid);
+ return true;
+}
+
+bool ArenaTeam::AddMember(uint64 PlayerGuid)
+{
+ std::string plName;
+ uint8 plClass;
+
+ if(GetMembersSize() >= GetType() * 2)
+ {
+ // arena team is full (can't have more than type * 2 players!)
+ // return false
+ return false;
+ }
+
+ if(!objmgr.GetPlayerNameByGUID(PlayerGuid, plName)) // player doesnt exist
+ return false;
+ // player already in arenateam of that size
+ if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0)
+ {
+ sLog.outError("Arena::AddMember() : player already in this sized team");
+ return false;
+ }
+
+ // remove all player signs from another petitions
+ // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity
+ Player::RemovePetitionsAndSigns(PlayerGuid, GetType());
+
+ Player *pl = objmgr.GetPlayer(PlayerGuid);
+ if(pl)
+ {
+ plClass = (uint8)pl->getClass();
+ }
+ else
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid='%u'", GUID_LOPART(PlayerGuid));
+ if(!result)
+ return false;
+ plClass = (*result)[0].GetUInt8();
+ delete result;
+ }
+
+ ArenaTeamMember newmember;
+ newmember.name = plName;
+ newmember.guid = PlayerGuid;
+ newmember.Class = plClass;
+ newmember.played_season = 0;
+ newmember.played_week = 0;
+ newmember.wons_season = 0;
+ newmember.wons_week = 0;
+ members.push_back(newmember);
+
+ CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid,guid) VALUES ('%u', '%u')", Id, GUID_LOPART(newmember.guid));
+
+ if(pl)
+ {
+ pl->SetInArenaTeam(Id, GetSlot());
+ pl->SetArenaTeamIdInvited(0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), Id, PlayerGuid);
+ }
+
+ // hide promote/remove buttons
+ if(CaptainGuid != PlayerGuid)
+ {
+ if(pl)
+ pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1);
+ else
+ Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, PlayerGuid);
+ }
+ return true;
+}
+
+bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId)
+{
+ LoadStatsFromDB(ArenaTeamId);
+ LoadMembersFromDB(ArenaTeamId);
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor FROM arena_team WHERE arenateamid = '%u'", ArenaTeamId);
+
+ if(!result)
+ return false;
+
+ 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();
+
+ delete result;
+
+ return true;
+}
+
+void ArenaTeam::LoadStatsFromDB(uint32 ArenaTeamId)
+{
+ // 0 1 2 3 4 5
+ QueryResult *result = CharacterDatabase.PQuery("SELECT rating,games,wins,played,wins2,rank FROM arena_team_stats WHERE arenateamid = '%u'", ArenaTeamId);
+
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+
+ stats.rating = fields[0].GetUInt32();
+ stats.games = fields[1].GetUInt32();
+ stats.wins = fields[2].GetUInt32();
+ stats.played = fields[3].GetUInt32();
+ stats.wins2 = fields[4].GetUInt32();
+ stats.rank = fields[5].GetUInt32();
+
+ delete result;
+}
+
+void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId)
+{
+ Field *fields;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid,played_week,wons_week,played_season,wons_season FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId);
+ if(!result)
+ return;
+
+ do
+ {
+ fields = result->Fetch();
+ ArenaTeamMember newmember;
+ newmember.guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ LoadPlayerStats(&newmember);
+ newmember.played_week = fields[1].GetUInt32();
+ newmember.wons_week = fields[2].GetUInt32();
+ newmember.played_season = fields[3].GetUInt32();
+ newmember.wons_season = fields[4].GetUInt32();
+ members.push_back(newmember);
+ }while( result->NextRow() );
+ delete result;
+}
+
+void ArenaTeam::LoadPlayerStats(ArenaTeamMember *member)
+{
+ Field *fields;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT name,class FROM characters WHERE guid = '%u'", GUID_LOPART(member->guid));
+ if(!result)
+ return;
+ fields = result->Fetch();
+ member->name = fields[0].GetCppString();
+ member->Class = fields[1].GetUInt8();
+
+ delete result;
+}
+
+void ArenaTeam::SetCaptain(uint64 guid)
+{
+ // disable remove/promote buttons
+ Player *oldcaptain = objmgr.GetPlayer(GetCaptain());
+ if(oldcaptain)
+ oldcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1);
+ else
+ Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, GetCaptain());
+
+ // set new captain
+ CaptainGuid = guid;
+
+ // update database
+ CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), Id);
+
+ // enable remove/promote buttons
+ Player *newcaptain = objmgr.GetPlayer(guid);
+ if(newcaptain)
+ newcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0);
+ else
+ Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0, guid);
+}
+
+void ArenaTeam::DelMember(uint64 guid)
+{
+ MemberList::iterator itr;
+ for (itr = members.begin(); itr != members.end(); itr++)
+ {
+ if (itr->guid == guid)
+ {
+ members.erase(itr);
+ break;
+ }
+ }
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ {
+ player->SetInArenaTeam(0, GetSlot());
+ player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), 0, guid);
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE guid = '%u'", GUID_LOPART(guid));
+}
+
+void ArenaTeam::Disband(WorldSession *session)
+{
+ // event
+ WorldPacket data;
+ session->BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_DISBANDED_S, 2, session->GetPlayerName(), GetName(), "");
+ BroadcastPacket(&data);
+
+ uint32 count = members.size();
+ uint64 *memberGuids = new uint64[count];
+
+ MemberList::iterator itr;
+ uint32 i=0;
+ for(itr = members.begin(); itr != members.end(); itr++)
+ {
+ memberGuids[i] = itr->guid;
+ ++i;
+ }
+
+ for(uint32 j = 0; j < count; j++)
+ DelMember(memberGuids[j]);
+ delete[] memberGuids;
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", Id);
+ CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", Id);
+ CharacterDatabase.CommitTransaction();
+ objmgr.RemoveArenaTeam(this);
+}
+
+void ArenaTeam::Roster(WorldSession *session)
+{
+ Player *pl = NULL;
+
+ WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100);
+ data << uint32(GetId()); // arena team id
+ data << uint32(GetMembersSize()); // members count
+ data << uint32(GetType()); // arena team type?
+
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ pl = objmgr.GetPlayer(itr->guid);
+ if(pl)
+ {
+ data << uint64(pl->GetGUID()); // guid
+ data << uint8(1); // online flag
+ data << pl->GetName(); // member name
+ data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown
+ data << uint8(pl->getLevel()); // unknown, probably level
+ data << uint8(pl->getClass()); // class
+ data << uint32(itr->played_week); // played this week
+ data << uint32(itr->wons_week); // wins this week
+ data << uint32(itr->played_season); // played this season
+ data << uint32(itr->wons_season); // wins this season
+ data << uint32(0); // personal rating?
+ }
+ else
+ {
+ data << uint64(itr->guid); // guid
+ data << uint8(0); // online flag
+ data << itr->name; // member name
+ data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown
+ data << uint8(0); // unknown, level?
+ data << uint8(itr->Class); // class
+ data << uint32(itr->played_week); // played this week
+ data << uint32(itr->wons_week); // wins this week
+ data << uint32(itr->played_season); // played this season
+ data << uint32(itr->wons_season); // wins this season
+ data << uint32(0); // personal rating?
+ }
+ }
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER");
+}
+
+void ArenaTeam::Query(WorldSession *session)
+{
+ WorldPacket data(SMSG_ARENA_TEAM_QUERY_RESPONSE, 4*7+GetName().size()+1);
+ 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
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE");
+}
+
+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); // games
+ data << uint32(stats.wins); // wins
+ data << uint32(stats.played); // played
+ data << uint32(stats.wins2); // wins(again o_O)
+ data << uint32(stats.rank); // rank
+ session->SendPacket(&data);
+}
+
+void ArenaTeam::InspectStats(WorldSession *session, uint64 guid)
+{
+ WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8+1+4*6);
+ 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); // games
+ data << uint32(stats.wins); // wins
+ data << uint32(stats.played); // played (count of all games, that played...)
+ data << uint32(0); // 2.3.3 personal rating?
+ session->SendPacket(&data);
+}
+
+void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor)
+{
+ BackgroundColor = backgroundColor;
+ EmblemStyle = emblemStyle;
+ EmblemColor = emblemColor;
+ BorderStyle = borderStyle;
+ 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);
+}
+
+void ArenaTeam::SetStats(uint32 stat_type, uint32 value)
+{
+ switch(stat_type)
+ {
+ case STAT_TYPE_RATING:
+ stats.rating = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ case STAT_TYPE_GAMES:
+ stats.games = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET games = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ case STAT_TYPE_WINS:
+ stats.wins = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ case STAT_TYPE_PLAYED:
+ stats.played = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET played = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ case STAT_TYPE_WINS2:
+ stats.wins2 = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins2 = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ case STAT_TYPE_RANK:
+ stats.rank = value;
+ CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId());
+ break;
+ default:
+ sLog.outDebug("unknown stat type in ArenaTeam::SetStats() %u", stat_type);
+ break;
+ }
+}
+
+uint8 ArenaTeam::GetSlot() const
+{
+ uint8 slot = GetSlotByType(GetType());
+ if(slot >= MAX_ARENA_SLOT)
+ {
+ sLog.outError("Unknown arena team type %u for arena team %u", uint32(GetType()), GetId());
+ return 0; // better return existed slot to prevent untelated data curruption
+ }
+
+ return slot;
+}
+
+void ArenaTeam::BroadcastPacket(WorldPacket *packet)
+{
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); itr++)
+ {
+ Player *player = objmgr.GetPlayer(itr->guid);
+ if(player)
+ player->GetSession()->SendPacket(packet);
+ }
+}
+
+uint8 ArenaTeam::GetSlotByType( uint32 type )
+{
+ switch(type)
+ {
+ case ARENA_TEAM_2v2: return 0;
+ case ARENA_TEAM_3v3: return 1;
+ case ARENA_TEAM_5v5: return 2;
+ default:
+ break;
+ }
+ return 0xFF;
+}
+
+bool ArenaTeam::HaveMember( uint64 guid ) const
+{
+ for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
+ if(itr->guid==guid)
+ return true;
+
+ return false;
+}
+
+/*
+arenateam fields (id from 2.3.3 client):
+1414 - arena team id 2v2
+1415 - 0=captain, 1=member
+1416 - played this season
+1417 - played this week
+1418 - unk
+1419 - unk
+1420 - arena team id 3v3
+1421 - 0=captain, 1=member
+1422 - played this season
+1423 - played this week
+1424 - unk
+1425 - unk
+1426 - arena team id 5v5
+1427 - 0=captain, 1=member
+1428 - played this season
+1429 - played this week
+1430 - unk
+1431 - unk
+*/
diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h
new file mode 100644
index 00000000000..9b4ef67ead1
--- /dev/null
+++ b/src/game/ArenaTeam.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_ARENATEAM_H
+#define MANGOSSERVER_ARENATEAM_H
+
+enum ArenaTeamCommandTypes
+{
+ ERR_ARENA_TEAM_CREATE_S = 0x00,
+ ERR_ARENA_TEAM_INVITE_SS = 0x01,
+ //ERR_ARENA_TEAM_QUIT_S = 0x02,
+ ERR_ARENA_TEAM_QUIT_S = 0x03,
+ ERR_ARENA_TEAM_FOUNDER_S = 0x0C // need check, probably wrong...
+};
+
+enum ArenaTeamCommandErrors
+{
+ //ARENA_TEAM_PLAYER_NO_MORE_IN_ARENA_TEAM = 0x00,
+ ERR_ARENA_TEAM_INTERNAL = 0x01,
+ ERR_ALREADY_IN_ARENA_TEAM = 0x02,
+ ERR_ALREADY_IN_ARENA_TEAM_S = 0x03,
+ ERR_INVITED_TO_ARENA_TEAM = 0x04,
+ ERR_ALREADY_INVITED_TO_ARENA_TEAM_S = 0x05,
+ ERR_ARENA_TEAM_NAME_INVALID = 0x06,
+ ERR_ARENA_TEAM_NAME_EXISTS_S = 0x07,
+ ERR_ARENA_TEAM_LEADER_LEAVE_S = 0x08,
+ ERR_ARENA_TEAM_PERMISSIONS = 0x08,
+ ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09,
+ ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A,
+ ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B,
+ ERR_ARENA_TEAM_NOT_ALLIED = 0x0C
+};
+
+enum ArenaTeamEvents
+{
+ ERR_ARENA_TEAM_JOIN_SS = 3, // player name + arena team name
+ ERR_ARENA_TEAM_LEAVE_SS = 4, // player name + arena team name
+ ERR_ARENA_TEAM_REMOVE_SSS = 5, // player name + arena team name + captain name
+ ERR_ARENA_TEAM_LEADER_IS_SS = 6, // player name + arena team name
+ ERR_ARENA_TEAM_LEADER_CHANGED_SSS = 7, // old captain + new captain + arena team name
+ ERR_ARENA_TEAM_DISBANDED_S = 8 // captain name + arena team name
+};
+
+/*
+need info how to send these ones:
+ERR_ARENA_TEAM_YOU_JOIN_S - client show it automatically when accept invite
+ERR_ARENA_TEAM_TARGET_TOO_LOW_S
+ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S
+ERR_ARENA_TEAM_LEVEL_TOO_LOW_I
+*/
+
+enum ArenaTeamStatTypes
+{
+ STAT_TYPE_RATING = 0,
+ STAT_TYPE_GAMES = 1,
+ STAT_TYPE_WINS = 2,
+ STAT_TYPE_PLAYED = 3,
+ STAT_TYPE_WINS2 = 4,
+ STAT_TYPE_RANK = 5
+};
+
+enum ArenaTeamTypes
+{
+ ARENA_TEAM_2v2 = 2,
+ ARENA_TEAM_3v3 = 3,
+ ARENA_TEAM_5v5 = 5
+};
+
+struct ArenaTeamMember
+{
+ uint64 guid;
+ std::string name;
+ //uint32 unk2;
+ //uint8 unk1;
+ uint8 Class;
+ uint32 played_week;
+ uint32 wons_week;
+ uint32 played_season;
+ uint32 wons_season;
+};
+
+struct ArenaTeamStats
+{
+ uint32 rating;
+ uint32 games;
+ uint32 wins;
+ uint32 played;
+ uint32 wins2;
+ uint32 rank;
+};
+
+#define MAX_ARENA_SLOT 3 // 0..2 slots
+
+class ArenaTeam
+{
+ public:
+ ArenaTeam();
+ ~ArenaTeam();
+
+ 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; }
+ uint8 GetSlot() const;
+ static uint8 GetSlotByType(uint32 type);
+ const uint64& GetCaptain() const { return CaptainGuid; }
+ std::string GetName() const { return Name; }
+ ArenaTeamStats GetStats() const { return stats; }
+ void SetStats(uint32 stat_type, uint32 value);
+ uint32 GetRating() const { return 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; }
+
+ void SetCaptain(uint64 guid);
+ bool AddMember(uint64 PlayerGuid);
+ void DelMember(uint64 guid);
+
+ void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor);
+
+ uint32 GetMembersSize() const { return members.size(); }
+ MemberList::iterator membersbegin(){ return members.begin(); }
+ MemberList::iterator membersEnd(){ return members.end(); }
+ bool HaveMember(uint64 guid) const;
+
+ bool LoadArenaTeamFromDB(uint32 ArenaTeamId);
+ void LoadMembersFromDB(uint32 ArenaTeamId);
+ void LoadStatsFromDB(uint32 ArenaTeamId);
+ void LoadPlayerStats(ArenaTeamMember* member);
+
+ void BroadcastPacket(WorldPacket *packet);
+
+ void Roster(WorldSession *session);
+ void Query(WorldSession *session);
+ void Stats(WorldSession *session);
+ void InspectStats(WorldSession *session, uint64 guid);
+
+ protected:
+
+ uint32 Id;
+ uint32 Type;
+ std::string Name;
+ uint64 CaptainGuid;
+
+ uint32 BackgroundColor; // ARGB format
+ uint32 EmblemStyle; // icon id
+ uint32 EmblemColor; // ARGB format
+ uint32 BorderStyle; // border image id
+ uint32 BorderColor; // ARGB format
+
+ MemberList members;
+ ArenaTeamStats stats;
+};
+#endif
diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp
new file mode 100644
index 00000000000..33786e93532
--- /dev/null
+++ b/src/game/ArenaTeamHandler.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "WorldSession.h"
+#include "WorldPacket.h"
+#include "Log.h"
+#include "Database/DatabaseEnv.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "ArenaTeam.h"
+#include "World.h"
+#include "SocialMgr.h"
+
+void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("MSG_INSPECT_ARENA_TEAMS");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ sLog.outDebug("Inspect Arena stats " I64FMTD, guid);
+
+ if(Player *plr = objmgr.GetPlayer(guid))
+ {
+ for (uint8 i = 0; i < MAX_ARENA_SLOT; i++)
+ {
+ if(uint32 a_id = plr->GetArenaTeamId(i))
+ {
+ if(ArenaTeam *at = objmgr.GetArenaTeamById(a_id))
+ at->InspectStats(this, plr->GetGUID());
+ }
+ }
+ }
+}
+
+void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" );
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId;
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam) // arena team not found
+ return;
+
+ arenateam->Query(this);
+ arenateam->Stats(this);
+}
+
+void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_ROSTER" );
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam)
+ return;
+
+ arenateam->Roster(this);
+}
+
+void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_ADD_MEMBER");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId; // arena team id
+ std::string Invitedname;
+
+ Player * player = NULL;
+
+ recv_data >> ArenaTeamId >> Invitedname;
+
+ if(!Invitedname.empty())
+ {
+ if(!normalizePlayerName(Invitedname))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str());
+ }
+
+ if(!player)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
+ return;
+ }
+
+ if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S);
+ // can't find related opcode
+ SendNotification("%s is not high enough level to join your team", player->GetName());
+ return;
+ }
+
+ ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!arenateam)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM);
+ return;
+ }
+
+ // OK result but not send invite
+ if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ return;
+
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
+ return;
+ }
+
+ if(player->GetArenaTeamId(arenateam->GetSlot()))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
+ return;
+ }
+
+ if(player->GetArenaTeamIdInvited())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
+ return;
+ }
+
+ if(arenateam->GetMembersSize() >= arenateam->GetType() * 2)
+ {
+ // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL
+// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL);
+ SendNotification("Your arena team is full, %s cannot join it.", player->GetName());
+ return;
+ }
+
+ sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str());
+
+ player->SetArenaTeamIdInvited(arenateam->GetId());
+
+ WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10));
+ data << GetPlayer()->GetName();
+ data << arenateam->GetName();
+ player->GetSession()->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE");
+}
+
+void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_INVITE_ACCEPT"); // empty opcode
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited());
+ if(!at)
+ {
+ // arena team not exist
+ return;
+ }
+
+ if(_player->GetArenaTeamId(at->GetSlot()))
+ {
+ // already in arena team that size
+ return;
+ }
+
+ // not let enemies sign petition
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain()))
+ return;
+
+ if(!at->AddMember(_player->GetGUID()))
+ return;
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_JOIN_SS, 2, _player->GetName(), at->GetName(), "");
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleArenaTeamInviteDeclineOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_INVITE_DECLINE"); // empty opcode
+
+ _player->SetArenaTeamIdInvited(0); // no more invited
+}
+
+void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_LEAVE");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // send command result
+ return;
+ }
+ if(_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1)
+ {
+ // check for correctness
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
+ return;
+ }
+ // arena team has only one member (=captain)
+ if(_player->GetGUID() == at->GetCaptain())
+ {
+ at->Disband(this);
+ delete at;
+ return;
+ }
+
+ at->DelMember(_player->GetGUID());
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEAVE_SS, 2, _player->GetName(), at->GetName(), "");
+ at->BroadcastPacket(&data);
+
+ //SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0);
+}
+
+void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_DISBAND");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 ArenaTeamId; // arena team id
+ recv_data >> ArenaTeamId;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ at->Disband(this);
+ delete at;
+}
+
+void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_REMOVE_FROM_TEAM");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId;
+ std::string name;
+
+ recv_data >> ArenaTeamId;
+ recv_data >> name;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+ if(!guid)
+ {
+ // player guid not found
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ // unsure
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
+ return;
+ }
+
+ at->DelMember(guid);
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_REMOVE_SSS, 3, name, at->GetName(), _player->GetName());
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_ARENA_TEAM_PROMOTE_TO_CAPTAIN");
+ //recv_data.hexlike();
+
+ CHECK_PACKET_SIZE(recv_data, 4+1);
+
+ uint32 ArenaTeamId;
+ std::string name;
+
+ recv_data >> ArenaTeamId;
+ recv_data >> name;
+
+ ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
+ if(!at)
+ {
+ // arena team not found
+ return;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+ if(!guid)
+ {
+ // player guid not found
+ return;
+ }
+
+ if(at->GetCaptain() == guid)
+ {
+ // target player already captain
+ return;
+ }
+
+ if(at->GetCaptain() != _player->GetGUID())
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
+ return;
+ }
+
+ at->SetCaptain(guid);
+
+ // event
+ WorldPacket data;
+ BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 3, _player->GetName(), name, at->GetName());
+ at->BroadcastPacket(&data);
+}
+
+void WorldSession::SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3)
+{
+ WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+str1.length()+1+str2.length()+1+4);
+ data << unk1;
+ data << str1;
+ data << str2;
+ data << unk3;
+ SendPacket(&data);
+}
+
+void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3)
+{
+ data->Initialize(SMSG_ARENA_TEAM_EVENT, 1+1+1);
+ *data << eventid;
+ *data << str_count;
+ switch(str_count)
+ {
+ case 1:
+ *data << str1;
+ break;
+ case 2:
+ *data << str1;
+ *data << str2;
+ break;
+ case 3:
+ *data << str1;
+ *data << str2;
+ *data << str3;
+ break;
+ default:
+ sLog.outError("Unhandled str_count %u in SendArenaTeamEvent()", str_count);
+ return;
+ }
+}
+
+void WorldSession::SendNotInArenaTeamPacket(uint8 type)
+{
+ WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team
+ uint32 unk = 0;
+ data << uint32(unk); // unk(0)
+ if(!unk)
+ data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types...
+ SendPacket(&data);
+}
+
+/*
++ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team"
+
++ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]."
++ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s"
++ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s"
+ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]."
+
++ERR_ARENA_TEAM_INTERNAL "Internal arena team error"
++ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size"
++ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size"
++ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team"
++ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team"
++ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name"
++ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\""
++ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team"
++ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that"
++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size"
++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s"
++ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found"
++ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance"
+
++ERR_ARENA_TEAM_JOIN_SS "%s has joined %s"
++ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]."
+
++ERR_ARENA_TEAM_LEAVE_SS "%s has left %s"
+
++ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s"
++ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s"
+
++ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s"
+
++ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s"
+
+ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team"
+
+ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full"
+
+ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team"
+*/
diff --git a/src/game/AuctionHouse.cpp b/src/game/AuctionHouse.cpp
new file mode 100644
index 00000000000..9b527400b04
--- /dev/null
+++ b/src/game/AuctionHouse.cpp
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "AuctionHouseObject.h"
+#include "Util.h"
+
+//please DO NOT use iterator++, because it is slower than ++iterator!!!
+//post-incrementation is always slower than pre-incrementation !
+
+//void called when player click on auctioneer npc
+void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid; //NPC guid
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendAuctionHello(guid, unit);
+}
+
+static uint8 AuctioneerFactionToLocation(uint32 faction)
+{
+ if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
+ return 7; // neutral
+
+ FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(faction);
+ if(!u_entry)
+ return 7; // neutral
+
+ if(u_entry->ourMask & FACTION_MASK_ALLIANCE)
+ return 2;
+ else if(u_entry->ourMask & FACTION_MASK_HORDE)
+ return 6;
+ else
+ return 7;
+}
+
+//this void causes that auction window is opened
+void WorldSession::SendAuctionHello( uint64 guid, Creature* unit )
+{
+ WorldPacket data( MSG_AUCTION_HELLO, 12 );
+ data << (uint64) guid;
+ data << (uint32) AuctioneerFactionToLocation(unit->getFaction());
+ SendPacket( &data );
+}
+
+//this function inserts to WorldPacket auction's data
+bool WorldSession::SendAuctionInfo(WorldPacket & data, AuctionEntry* auction)
+{
+ Item *pItem = objmgr.GetAItem(auction->item_guidlow);
+ if (!pItem)
+ {
+ sLog.outError("auction to item, that doesn't exist !!!!");
+ return false;
+ }
+ data << auction->Id;
+ data << pItem->GetUInt32Value(OBJECT_FIELD_ENTRY);
+
+ for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; i++)
+ {
+ data << (uint32) pItem->GetEnchantmentId(EnchantmentSlot(i));
+ data << (uint32) pItem->GetEnchantmentDuration(EnchantmentSlot(i));
+ data << (uint32) pItem->GetEnchantmentCharges(EnchantmentSlot(i));
+ }
+
+ data << (uint32) pItem->GetItemRandomPropertyId(); //random item property id
+ data << (uint32) pItem->GetItemSuffixFactor(); //SuffixFactor
+ data << (uint32) pItem->GetCount(); //item->count
+ data << (uint32) pItem->GetSpellCharges(); //item->charge FFFFFFF
+ data << (uint32) 0; //Unknown
+ data << (uint64) auction->owner; //Auction->owner
+ data << (uint32) auction->startbid; //Auction->startbid (not sure if useful)
+ data << (uint32) ((auction->bid)? objmgr.GetAuctionOutBid(auction->bid) : 0);
+ //minimal outbid
+ data << (uint32) auction->buyout; //auction->buyout
+ data << (uint32) (auction->time - time(NULL)) * 1000; //time left
+ data << (uint64) auction->bidder; //auction->bidder current
+ data << (uint32) auction->bid; //current bid
+ return true;
+}
+
+//call this method when player bids, creates, or deletes auction
+void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError )
+{
+ WorldPacket data( SMSG_AUCTION_COMMAND_RESULT, 16 );
+ data << auctionId;
+ data << Action;
+ data << ErrorCode;
+ if ( !ErrorCode && Action )
+ data << bidError; //when bid, then send 0, once...
+ SendPacket(&data);
+}
+
+//this function sends notification, if bidder is online
+void WorldSession::SendAuctionBidderNotification( uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template)
+{
+ WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4));
+ data << location;
+ data << auctionId;
+ data << (uint64) bidder;
+ data << bidSum;
+ data << (uint32) diff;
+ data << item_template;
+ data << (uint32) 0;
+ SendPacket(&data);
+}
+
+//this void causes on client to display: "Your auction sold"
+void WorldSession::SendAuctionOwnerNotification( AuctionEntry* auction)
+{
+ WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4));
+ data << auction->Id;
+ data << auction->bid;
+ data << (uint32) 0; //unk
+ data << (uint32) 0; //unk
+ data << (uint32) 0; //unk
+ data << auction->item_template;
+ data << (uint32) 0; //unk
+ SendPacket(&data);
+}
+
+//this function sends mail to old bidder
+void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice)
+{
+ uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER);
+ Player *oldBidder = objmgr.GetPlayer(oldBidder_guid);
+
+ uint32 oldBidder_accId = 0;
+ if(!oldBidder)
+ oldBidder_accId = objmgr.GetPlayerAccountIdByGUID(oldBidder_guid);
+
+ // old bidder exist
+ if(oldBidder || oldBidder_accId)
+ {
+ std::ostringstream msgAuctionOutbiddedSubject;
+ msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED;
+
+ if (oldBidder)
+ oldBidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, _player->GetGUID(), newPrice, objmgr.GetAuctionOutBid(auction->bid), auction->item_template);
+
+ WorldSession::SendMailTo(oldBidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionOutbiddedSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE);
+ }
+}
+
+//this function sends mail, when auction is cancelled to old bidder
+void WorldSession::SendAuctionCancelledToBidderMail( AuctionEntry* auction )
+{
+ uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
+ Player *bidder = objmgr.GetPlayer(bidder_guid);
+
+ uint32 bidder_accId = 0;
+ if(!bidder)
+ bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid);
+
+ // bidder exist
+ if(bidder || bidder_accId)
+ {
+ std::ostringstream msgAuctionCancelledSubject;
+ msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER;
+
+ WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionCancelledSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE);
+ }
+}
+
+//this void creates new auction and adds auction to some auctionhouse
+void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+4+4+4);
+
+ uint64 auctioneer, item;
+ uint32 etime, bid, buyout;
+ recv_data >> auctioneer >> item;
+ recv_data >> bid >> buyout >> etime;
+ Player *pl = GetPlayer();
+
+ if (!item || !bid || !etime)
+ return; //check for cheaters
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) );
+ return;
+ }
+
+ // client send time in minutes, convert to common used sec time
+ etime *= MINUTE;
+
+ // client understand only 3 auction time
+ switch(etime)
+ {
+ case 1*MIN_AUCTION_TIME:
+ case 2*MIN_AUCTION_TIME:
+ case 4*MIN_AUCTION_TIME:
+ break;
+ default:
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *it = pl->GetItemByGuid( item );
+ //do not allow to sell already auctioned items
+ if(objmgr.GetAItem(GUID_LOPART(item)))
+ {
+ sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->GetName(), GUID_LOPART(item));
+ SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
+ return;
+ }
+ // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction)
+ if(!it)
+ {
+ SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND);
+ return;
+ }
+
+ if(!it->CanBeTraded())
+ {
+ SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
+ return;
+ }
+
+ if (it->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION))
+ {
+ SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
+ return;
+ }
+
+ uint32 location = AuctioneerFactionToLocation(pCreature->getFaction());
+ AuctionHouseObject * mAuctions;
+ mAuctions = objmgr.GetAuctionsMap( location );
+
+ //we have to take deposit :
+ uint32 deposit = objmgr.GetAuctionDeposit( location, etime, it );
+ if ( pl->GetMoney() < deposit )
+ {
+ SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
+ return;
+ }
+
+ if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ {
+ sLog.outCommand("GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)",
+ GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount());
+ }
+
+ pl->ModifyMoney( -int32(deposit) );
+
+ uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME));
+
+ AuctionEntry *AH = new AuctionEntry;
+ AH->Id = objmgr.GenerateAuctionID();
+ AH->auctioneer = GUID_LOPART(auctioneer);
+ AH->item_guidlow = GUID_LOPART(item);
+ AH->item_template = it->GetEntry();
+ AH->owner = pl->GetGUIDLow();
+ AH->startbid = bid;
+ AH->bidder = 0;
+ AH->bid = 0;
+ AH->buyout = buyout;
+ AH->time = time(NULL) + auction_time;
+ AH->deposit = deposit;
+ AH->location = location;
+
+ sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in location: %u", GUID_LOPART(item), GUID_LOPART(auctioneer), bid, buyout, auction_time, location);
+ mAuctions->AddAuction(AH);
+
+ objmgr.AddAItem(it);
+ pl->MoveItemFromInventory( it->GetBagSlot(), it->GetSlot(), true);
+
+ CharacterDatabase.BeginTransaction();
+ it->DeleteFromInventoryDB();
+ it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone
+ CharacterDatabase.PExecute("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u')",
+ AH->Id, AH->auctioneer, AH->item_guidlow, AH->item_template, AH->owner, AH->buyout, (uint64)AH->time, AH->bidder, AH->bid, AH->startbid, AH->deposit, AH->location);
+ pl->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+
+ SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK);
+}
+
+//this function is called when client bids or buys out auction
+void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ uint64 auctioneer;
+ uint32 auctionId;
+ uint32 price;
+ recv_data >> auctioneer;
+ recv_data >> auctionId >> price;
+
+ if (!auctionId || !price)
+ return; //check for cheaters
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 location = AuctioneerFactionToLocation(pCreature->getFaction());
+
+ AuctionHouseObject * mAuctions;
+ mAuctions = objmgr.GetAuctionsMap( location );
+
+ AuctionEntry *auction = mAuctions->GetAuction(auctionId);
+ Player *pl = GetPlayer();
+
+ if( !auction || auction->owner == pl->GetGUIDLow() )
+ {
+ //you cannot bid your own auction:
+ SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR );
+ return;
+ }
+
+ // impossible have online own another character (use this for speedup check in case online owner)
+ Player* auction_owner = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER));
+ if( !auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId())
+ {
+ //you cannot bid your another character auction:
+ SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR );
+ return;
+ }
+
+ if (price < (auction->bid + objmgr.GetAuctionOutBid(auction->bid)))
+ {
+ //auction has already higher bid, client tests it!
+ //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
+ return;
+ }
+
+ if (price > pl->GetMoney())
+ {
+ //you don't have enought money!, client tests!
+ //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
+ return;
+ }
+
+ if ((price < auction->buyout) || (auction->buyout == 0))
+ {
+ if (auction->bidder > 0)
+ {
+ if ( auction->bidder == pl->GetGUIDLow() )
+ {
+ pl->ModifyMoney( -int32(price - auction->bid));
+ }
+ else
+ {
+ // mail to last bidder and return money
+ SendAuctionOutbiddedMail( auction , price );
+ pl->ModifyMoney( -int32(price) );
+ }
+ }
+ else
+ {
+ pl->ModifyMoney( -int32(price) );
+ }
+ auction->bidder = pl->GetGUIDLow();
+ auction->bid = price;
+
+ // after this update we should save player's money ...
+ CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id);
+
+ SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0 );
+ }
+ else
+ {
+ //buyout:
+ if (pl->GetGUIDLow() == auction->bidder )
+ {
+ pl->ModifyMoney(-int32(auction->buyout - auction->bid));
+ }
+ else
+ {
+ pl->ModifyMoney(-int32(auction->buyout));
+ if ( auction->bidder ) //buyout for bidded auction ..
+ {
+ SendAuctionOutbiddedMail( auction, auction->buyout );
+ }
+ }
+ auction->bidder = pl->GetGUIDLow();
+ auction->bid = auction->buyout;
+
+ objmgr.SendAuctionSalePendingMail( auction );
+ objmgr.SendAuctionSuccessfulMail( auction );
+ objmgr.SendAuctionWonMail( auction );
+
+ SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
+
+ objmgr.RemoveAItem(auction->item_guidlow);
+ mAuctions->RemoveAuction(auction->Id);
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",auction->Id);
+
+ delete auction;
+ }
+ CharacterDatabase.BeginTransaction();
+ pl->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+}
+
+//this void is called when auction_owner cancels his auction
+void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 auctioneer;
+ uint32 auctionId;
+ recv_data >> auctioneer;
+ recv_data >> auctionId;
+ //sLog.outDebug( "Cancel AUCTION AuctionID: %u", auctionId);
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 location = AuctioneerFactionToLocation(pCreature->getFaction());
+
+ AuctionHouseObject * mAuctions;
+ mAuctions = objmgr.GetAuctionsMap( location );
+
+ AuctionEntry *auction = mAuctions->GetAuction(auctionId);
+ Player *pl = GetPlayer();
+
+ if (auction && auction->owner == pl->GetGUIDLow())
+ {
+ Item *pItem = objmgr.GetAItem(auction->item_guidlow);
+ if (pItem)
+ {
+ if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid
+ {
+ uint32 auctionCut = objmgr.GetAuctionCut( auction->location, auction->bid);
+ if ( pl->GetMoney() < auctionCut ) //player doesn't have enough money, maybe message needed
+ return;
+ //some auctionBidderNotification would be needed, but don't know that parts..
+ SendAuctionCancelledToBidderMail( auction );
+ pl->ModifyMoney( -int32(auctionCut) );
+ }
+ // Return the item by mail
+ std::ostringstream msgAuctionCanceledOwner;
+ msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED;
+
+ MailItemsInfo mi;
+ mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
+ // item will deleted or added to received mail list
+ WorldSession::SendMailTo(pl, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, pl->GetGUIDLow(), msgAuctionCanceledOwner.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ }
+ else
+ {
+ sLog.outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow);
+ SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR );
+ return;
+ }
+ }
+ else
+ {
+ SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR );
+ //this code isn't possible ... maybe there should be assert
+ sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId );
+ return;
+ }
+
+ //inform player, that auction is removed
+ SendAuctionCommandResult( auction->Id, AUCTION_CANCEL, AUCTION_OK );
+ // Now remove the auction
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",auction->Id);
+ pl->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+ objmgr.RemoveAItem( auction->item_guidlow );
+ mAuctions->RemoveAuction( auction->Id );
+ delete auction;
+}
+
+//called when player lists his bids
+void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ uint64 guid; //NPC guid
+ uint32 listfrom; //page of auctions
+ uint32 outbiddedCount; //count of outbidded auctions
+
+ recv_data >> guid;
+ recv_data >> listfrom; // not used in fact (this list not have page control in client)
+ recv_data >> outbiddedCount;
+ if (recv_data.size() != (16 + outbiddedCount * 4 ))
+ {
+ sLog.outError("Client sent bad opcode!!! with count: %u and size : %d (mustbe: %d", outbiddedCount, recv_data.size(),(16 + outbiddedCount * 4 ));
+ outbiddedCount = 0;
+ }
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionListBidderItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 location = AuctioneerFactionToLocation(pCreature->getFaction());
+ AuctionHouseObject* mAuctions = objmgr.GetAuctionsMap( location );
+
+ WorldPacket data( SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4) );
+ Player *pl = GetPlayer();
+ data << (uint32) 0; //add 0 as count
+ uint32 count = 0;
+ uint32 totalcount = 0;
+ while ( outbiddedCount > 0) //add all data, which client requires
+ {
+ --outbiddedCount;
+ uint32 outbiddedAuctionId;
+ recv_data >> outbiddedAuctionId;
+ AuctionEntry * auction = mAuctions->GetAuction( outbiddedAuctionId );
+ if ( auction && SendAuctionInfo(data, auction))
+ {
+ ++totalcount;
+ ++count;
+ }
+ }
+ for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr)
+ {
+ AuctionEntry *Aentry = itr->second;
+ if( Aentry && Aentry->bidder == pl->GetGUIDLow() )
+ {
+ if (SendAuctionInfo(data, itr->second))
+ ++count;
+ ++totalcount;
+ }
+ }
+ data.put<uint32>( 0, count ); // add count to placeholder
+ data << totalcount;
+ data << (uint32)300; //unk 2.3.0
+ SendPacket(&data);
+}
+
+//this void sends player info about his auctions
+void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint32 listfrom;
+ uint64 guid;
+
+ recv_data >> guid;
+ recv_data >> listfrom; // not used in fact (this list not have page control in client)
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionListOwnerItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 location = AuctioneerFactionToLocation(pCreature->getFaction());
+
+ AuctionHouseObject* mAuctions = objmgr.GetAuctionsMap( location );
+
+ WorldPacket data( SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4) );
+ data << (uint32) 0; // amount place holder
+
+ uint32 count = 0;
+ uint32 totalcount = 0;
+ for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr)
+ {
+ AuctionEntry *Aentry = itr->second;
+ if( Aentry && Aentry->owner == _player->GetGUIDLow() )
+ {
+ if(SendAuctionInfo(data, itr->second))
+ ++count;
+ ++totalcount;
+ }
+ }
+ data.put<uint32>(0, count);
+ data << (uint32) totalcount;
+ data << (uint32) 0;
+ SendPacket(&data);
+}
+
+//this void is called when player clicks on search button
+void WorldSession::HandleAuctionListItems( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+1+1+1+4+4+4+4+1);
+
+ std::string searchedname, name;
+ uint8 levelmin, levelmax, usable, location;
+ uint32 count, totalcount, listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality;
+ uint64 guid;
+
+ recv_data >> guid;
+ recv_data >> listfrom; // start, used for page control listing by 50 elements
+ recv_data >> searchedname;
+
+ // recheck with known string size
+ CHECK_PACKET_SIZE(recv_data,8+4+(searchedname.size()+1)+1+1+4+4+4+4+1);
+
+ recv_data >> levelmin >> levelmax;
+ recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
+ recv_data >> quality >> usable;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleAuctionListItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ location = AuctioneerFactionToLocation(pCreature->getFaction());
+ AuctionHouseObject * mAuctions;
+ mAuctions = objmgr.GetAuctionsMap( location );
+
+ //sLog.outDebug("Auctionhouse search guid: " I64FMTD ", list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", guid, listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable);
+
+ WorldPacket data( SMSG_AUCTION_LIST_RESULT, (4+4+4) );
+ count = 0;
+ totalcount = 0;
+ data << (uint32) 0;
+
+ // converting string that we try to find to lower case
+ std::wstring wsearchedname;
+ if(!Utf8toWStr(searchedname,wsearchedname))
+ return;
+
+ wstrToLower(wsearchedname);
+
+ for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr)
+ {
+ AuctionEntry *Aentry = itr->second;
+ Item *item = objmgr.GetAItem(Aentry->item_guidlow);
+ if( item )
+ {
+ ItemPrototype const *proto = item->GetProto();
+ if( proto )
+ {
+ if( auctionMainCategory == (0xffffffff) || proto->Class == auctionMainCategory )
+ {
+ if( auctionSubCategory == (0xffffffff) || proto->SubClass == auctionSubCategory )
+ {
+ if( auctionSlotID == (0xffffffff) || proto->InventoryType == auctionSlotID )
+ {
+ if( quality == (0xffffffff) || proto->Quality == quality )
+ {
+ if( usable == (0x00) || _player->CanUseItem( item ) == EQUIP_ERR_OK )
+ {
+ if( ( levelmin == (0x00) || proto->RequiredLevel >= levelmin ) && ( levelmax == (0x00) || proto->RequiredLevel <= levelmax ) )
+ {
+ name = proto->Name1;
+
+ // local name
+ int loc_idx = GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(proto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ name = il->Name[loc_idx];
+ }
+ }
+
+ if(name.empty())
+ continue;
+
+ if( wsearchedname.empty() || Utf8FitTo(name, wsearchedname) )
+ {
+ if ((count < 50) && (totalcount >= listfrom))
+ {
+ ++count;
+ SendAuctionInfo( data, Aentry);
+ }
+ ++totalcount;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ data.put<uint32>(0, count);
+ data << (uint32) totalcount;
+ data << (uint32) 300; // unk 2.3.0 const?
+ SendPacket(&data);
+}
diff --git a/src/game/AuctionHouseObject.h b/src/game/AuctionHouseObject.h
new file mode 100644
index 00000000000..504c41c62e5
--- /dev/null
+++ b/src/game/AuctionHouseObject.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _AUCTION_HOUSE_H
+#define _AUCTION_HOUSE_H
+
+#include "SharedDefines.h"
+
+#define MIN_AUCTION_TIME (12*HOUR)
+
+enum AuctionError
+{
+ AUCTION_OK = 0,
+ AUCTION_INTERNAL_ERROR = 2,
+ AUCTION_NOT_ENOUGHT_MONEY = 3,
+ AUCTION_ITEM_NOT_FOUND = 4,
+ CANNOT_BID_YOUR_AUCTION_ERROR = 10
+};
+
+enum AuctionAction
+{
+ AUCTION_SELL_ITEM = 0,
+ AUCTION_CANCEL = 1,
+ AUCTION_PLACE_BID = 2
+};
+
+struct AuctionEntry
+{
+ uint32 Id;
+ uint32 auctioneer;
+ uint32 item_guidlow;
+ uint32 item_template;
+ uint32 owner;
+ uint32 startbid; //maybe useless
+ uint32 bid;
+ uint32 buyout;
+ time_t time;
+ uint32 bidder;
+ uint32 deposit; //deposit can be calculated only when creating auction
+ uint32 location;
+};
+
+//this class is used as auctionhouse instance
+class AuctionHouseObject
+{
+ public:
+ AuctionHouseObject() {}
+ ~AuctionHouseObject()
+ {
+ for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
+ delete itr->second;
+ }
+
+ typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
+
+ uint32 Getcount() { return AuctionsMap.size(); }
+
+ AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();}
+ AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();}
+
+ void AddAuction(AuctionEntry *ah)
+ {
+ ASSERT( ah );
+ AuctionsMap[ah->Id] = ah;
+ }
+
+ AuctionEntry* GetAuction(uint32 id) const
+ {
+ AuctionEntryMap::const_iterator itr = AuctionsMap.find( id );
+ if( itr != AuctionsMap.end() )
+ return itr->second;
+ return NULL;
+ }
+
+ bool RemoveAuction(uint32 id)
+ {
+ AuctionEntryMap::iterator i = AuctionsMap.find(id);
+ if (i == AuctionsMap.end())
+ {
+ return false;
+ }
+ AuctionsMap.erase(i);
+ return true;
+ }
+
+ private:
+ AuctionEntryMap AuctionsMap;
+};
+#endif
diff --git a/src/game/Bag.cpp b/src/game/Bag.cpp
new file mode 100644
index 00000000000..a529d075dcf
--- /dev/null
+++ b/src/game/Bag.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Bag.h"
+#include "ObjectMgr.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "UpdateData.h"
+#include "WorldSession.h"
+
+Bag::Bag( ): Item()
+{
+ m_objectType |= TYPEMASK_CONTAINER;
+ m_objectTypeId = TYPEID_CONTAINER;
+
+ m_valuesCount = CONTAINER_END;
+
+ memset(m_bagslot, 0, sizeof(Item *) * MAX_BAG_SIZE); // Maximum 20 Slots
+}
+
+Bag::~Bag()
+{
+ for(int i = 0; i<MAX_BAG_SIZE; i++)
+ {
+ if(m_bagslot[i]) delete m_bagslot[i];
+ }
+}
+
+void Bag::AddToWorld()
+{
+ Item::AddToWorld();
+
+ for(int i = 0; i<MAX_BAG_SIZE; i++)
+ {
+ if(m_bagslot[i])
+ m_bagslot[i]->AddToWorld();
+ }
+}
+
+void Bag::RemoveFromWorld()
+{
+ for(int i = 0; i<MAX_BAG_SIZE; i++)
+ {
+ if(m_bagslot[i])
+ m_bagslot[i]->RemoveFromWorld();
+ }
+
+ Item::RemoveFromWorld();
+}
+
+bool Bag::Create(uint32 guidlow, uint32 itemid, Player const* owner)
+{
+ ItemPrototype const * itemProto = objmgr.GetItemPrototype(itemid);
+
+ if(!itemProto || itemProto->ContainerSlots > MAX_BAG_SIZE)
+ return false;
+
+ Object::_Create( guidlow, 0, HIGHGUID_CONTAINER );
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, itemid);
+ SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);
+
+ SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0);
+ SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0);
+
+ SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
+ SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
+ SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags);
+ SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
+
+ // Setting the number of Slots the Container has
+ SetUInt32Value(CONTAINER_FIELD_NUM_SLOTS, itemProto->ContainerSlots);
+
+ // Cleanning 20 slots
+ for (uint8 i = 0; i < MAX_BAG_SIZE; i++)
+ {
+ SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0);
+ m_bagslot[i] = NULL;
+ }
+
+ return true;
+}
+
+void Bag::SaveToDB()
+{
+ Item::SaveToDB();
+}
+
+bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
+{
+ if(!Item::LoadFromDB(guid, owner_guid, result))
+ return false;
+
+ // cleanup bag content related item value fields (its will be filled correctly from `character_inventory`)
+ for (uint32 i = 0; i < GetProto()->ContainerSlots; i++)
+ {
+ SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0);
+ if (m_bagslot[i])
+ {
+ delete m_bagslot[i];
+ m_bagslot[i] = NULL;
+ }
+ }
+
+ return true;
+}
+
+void Bag::DeleteFromDB()
+{
+ for (int i = 0; i < MAX_BAG_SIZE; i++)
+ {
+ if (m_bagslot[i])
+ {
+ m_bagslot[i]->DeleteFromDB();
+ }
+ }
+
+ Item::DeleteFromDB();
+}
+
+uint32 Bag::GetFreeSlots() const
+{
+ uint32 ContainerSlots=GetProto()->ContainerSlots;
+ uint32 slots = 0;
+ for (uint8 i=0; i <ContainerSlots; i++)
+ if (!m_bagslot[i])
+ ++slots;
+
+ return slots;
+}
+
+void Bag::RemoveItem( uint8 slot, bool /*update*/ )
+{
+ assert(slot < MAX_BAG_SIZE);
+
+ if (m_bagslot[slot])
+ m_bagslot[slot]->SetContainer(NULL);
+
+ m_bagslot[slot] = NULL;
+ SetUInt64Value( CONTAINER_FIELD_SLOT_1 + (slot * 2), 0 );
+}
+
+void Bag::StoreItem( uint8 slot, Item *pItem, bool /*update*/ )
+{
+ assert(slot < MAX_BAG_SIZE);
+
+ if( pItem )
+ {
+ m_bagslot[slot] = pItem;
+ SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (slot * 2), pItem->GetGUID());
+ pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, GetGUID());
+ pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetOwnerGUID() );
+ pItem->SetContainer(this);
+ pItem->SetSlot(slot);
+ }
+}
+
+void Bag::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
+{
+ Item::BuildCreateUpdateBlockForPlayer( data, target );
+
+ for (int i = 0; i < MAX_BAG_SIZE; i++)
+ {
+ if(m_bagslot[i])
+ m_bagslot[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+}
+
+// If the bag is empty returns true
+bool Bag::IsEmpty() const
+{
+ uint32 ContainerSlots=GetProto()->ContainerSlots;
+ for(uint32 i=0; i < ContainerSlots; i++)
+ if (m_bagslot[i]) return false;
+
+ return true;
+}
+
+uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const
+{
+ uint32 ContainerSlots=GetProto()->ContainerSlots;
+
+ Item *pItem;
+ uint32 count = 0;
+ for(uint32 i=0; i < ContainerSlots; i++)
+ {
+ pItem = m_bagslot[i];
+ if( pItem && pItem != eItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+
+ if(eItem && eItem->GetProto()->GemProperties)
+ {
+ for(uint32 i=0; i < ContainerSlots; i++)
+ {
+ pItem = m_bagslot[i];
+ if( pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color )
+ count += pItem->GetGemCountWithID(item);
+ }
+ }
+
+ return count;
+}
+
+uint8 Bag::GetSlotByItemGUID(uint64 guid) const
+{
+ uint32 ContainerSlots=GetProto()->ContainerSlots;
+
+ for(uint32 i=0;i<ContainerSlots;i++)
+ {
+ if(m_bagslot[i] != 0)
+ if(m_bagslot[i]->GetGUID() == guid)
+ return i;
+ }
+ return NULL_SLOT;
+}
+
+// Adds an item to a bag slot
+// - slot can be NULL_SLOT, in that case function searchs for a free slot
+// - Return values: 0 - item not added
+// 1 - item added to a free slot (and perhaps to a stack)
+// 2 - item added to a stack (item should be deleted)
+Item* Bag::GetItemByPos( uint8 slot ) const
+{
+ ItemPrototype const *pBagProto = GetProto();
+ if( pBagProto )
+ {
+ if( slot < pBagProto->ContainerSlots )
+ return m_bagslot[slot];
+ }
+ return NULL;
+}
diff --git a/src/game/Bag.h b/src/game/Bag.h
new file mode 100644
index 00000000000..cc7ca52b338
--- /dev/null
+++ b/src/game/Bag.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_BAG_H
+#define MANGOS_BAG_H
+
+// Maximum 36 Slots ( (CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2
+#define MAX_BAG_SIZE 36 // 2.0.12
+
+#include "Object.h"
+#include "ItemPrototype.h"
+#include "Unit.h"
+#include "Creature.h"
+#include "Item.h"
+
+class Bag : public Item
+{
+ public:
+
+ Bag();
+ ~Bag();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create(uint32 guidlow, uint32 itemid, Player const* owner);
+
+ void Clear();
+ void StoreItem( uint8 slot, Item *pItem, bool update );
+ void RemoveItem( uint8 slot, bool update );
+
+ Item* GetItemByPos( uint8 slot ) const;
+ uint32 GetItemCount( uint32 item, Item* eItem = NULL ) const;
+
+ uint8 GetSlotByItemGUID(uint64 guid) const;
+ bool IsEmpty() const;
+ uint32 GetFreeSlots() const;
+
+ // DB operations
+ // overwrite virtual Item::SaveToDB
+ void SaveToDB();
+ // overwrite virtual Item::LoadFromDB
+ bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL);
+ // overwrite virtual Item::DeleteFromDB
+ void DeleteFromDB();
+
+ void BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const;
+
+ protected:
+
+ // Bag Storage space
+ Item* m_bagslot[MAX_BAG_SIZE];
+};
+
+inline Item* NewItemOrBag(ItemPrototype const * proto)
+{
+ return (proto->InventoryType == INVTYPE_BAG) ? new Bag : new Item;
+}
+#endif
diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp
new file mode 100644
index 00000000000..589b4175049
--- /dev/null
+++ b/src/game/BattleGround.cpp
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "Language.h"
+#include "Chat.h"
+#include "SpellAuras.h"
+#include "World.h"
+#include "Util.h"
+
+BattleGround::BattleGround()
+{
+ m_TypeID = 0;
+ m_InstanceID = 0;
+ m_Status = 0;
+ m_EndTime = 0;
+ m_LastResurrectTime = 0;
+ m_Queue_type = MAX_BATTLEGROUND_QUEUES;
+ m_InvitedAlliance = 0;
+ m_InvitedHorde = 0;
+ m_ArenaType = 0;
+ m_IsArena = false;
+ m_Winner = 2;
+ m_StartTime = 0;
+ m_Events = 0;
+ m_IsRated = false;
+ m_BuffChange = false;
+ m_Name = "";
+ m_LevelMin = 0;
+ m_LevelMax = 0;
+
+ m_MaxPlayersPerTeam = 0;
+ m_MaxPlayers = 0;
+ m_MinPlayersPerTeam = 0;
+ m_MinPlayers = 0;
+
+ m_MapId = 0;
+
+ m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
+ m_TeamStartLocX[BG_TEAM_HORDE] = 0;
+
+ m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
+ m_TeamStartLocY[BG_TEAM_HORDE] = 0;
+
+ m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
+ m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
+
+ m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
+ m_TeamStartLocO[BG_TEAM_HORDE] = 0;
+
+ m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
+ m_BgRaids[BG_TEAM_HORDE] = NULL;
+
+ m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
+ m_PlayersCount[BG_TEAM_HORDE] = 0;
+}
+
+BattleGround::~BattleGround()
+{
+
+}
+
+void BattleGround::Update(time_t diff)
+{
+
+ if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize())
+ //BG is empty
+ return;
+
+ WorldPacket data;
+
+ if(GetRemovedPlayersSize())
+ {
+ for(std::map<uint64, uint8>::iterator itr = m_RemovedPlayers.begin(); itr != m_RemovedPlayers.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+ switch(itr->second)
+ {
+ //following code is handled by event:
+ /*case 0:
+ sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].RemovePlayer(itr->first);
+ //RemovePlayerFromQueue(itr->first);
+ if(plr)
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+ break;*/
+ case 1: // currently in bg and was removed from bg
+ if(plr)
+ RemovePlayerAtLeave(itr->first, true, true);
+ else
+ RemovePlayerAtLeave(itr->first, false, false);
+ break;
+ case 2: // revive queue
+ RemovePlayerFromResurrectQueue(itr->first);
+ break;
+ default:
+ sLog.outError("BattleGround: Unknown remove player case!");
+ }
+ }
+ m_RemovedPlayers.clear();
+ }
+
+ // this code isn't efficient and its idea isn't implemented yet
+ /* offline players are removed from battleground in worldsession::LogoutPlayer()
+ // remove offline players from bg after ~5 minutes
+ if(GetPlayersSize())
+ {
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+ itr->second.LastOnlineTime += diff;
+
+ if(plr)
+ itr->second.LastOnlineTime = 0; // update last online time
+ else
+ if(itr->second.LastOnlineTime >= MAX_OFFLINE_TIME) // 5 minutes
+ m_RemovedPlayers[itr->first] = 1; // add to remove list (BG)
+ }
+ }*/
+
+ m_LastResurrectTime += diff;
+ if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
+ {
+ if(GetReviveQueueSize())
+ {
+ for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
+ {
+ Creature *sh = NULL;
+ for(std::vector<uint64>::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
+ {
+ Player *plr = objmgr.GetPlayer(*itr2);
+ if(!plr)
+ continue;
+
+ if (!sh)
+ {
+ sh = ObjectAccessor::GetCreature(*plr, itr->first);
+ // only for visual effect
+ if (sh)
+ sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); // Spirit Heal, effect 117
+ }
+
+ plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); // Resurrection visual
+ m_ResurrectQueue.push_back(*itr2);
+ }
+ (itr->second).clear();
+ }
+
+ m_ReviveQueue.clear();
+ m_LastResurrectTime = 0;
+ }
+ else
+ // queue is clear and time passed, just update last resurrection time
+ m_LastResurrectTime = 0;
+ }
+ else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
+ {
+ for(std::vector<uint64>::iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(*itr);
+ if(!plr)
+ continue;
+ plr->ResurrectPlayer(1.0f);
+ plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
+ ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
+ }
+ m_ResurrectQueue.clear();
+ }
+
+ if(GetStatus() == STATUS_WAIT_LEAVE)
+ {
+ // remove all players from battleground after 2 minutes
+ m_EndTime += diff;
+ if(m_EndTime >= TIME_TO_AUTOREMOVE) // 2 minutes
+ {
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ m_RemovedPlayers[itr->first] = 1; // add to remove list (BG)
+ }
+ // do not change any battleground's private variables
+ }
+ }
+}
+
+void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
+{
+ uint8 idx = GetTeamIndexByTeamId(TeamID);
+ m_TeamStartLocX[idx] = X;
+ m_TeamStartLocY[idx] = Y;
+ m_TeamStartLocZ[idx] = Z;
+ m_TeamStartLocO[idx] = O;
+}
+
+void BattleGround::SendPacketToAll(WorldPacket *packet)
+{
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+ if(plr)
+ plr->GetSession()->SendPacket(packet);
+ else
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ }
+}
+
+void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
+{
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(!self && sender == plr)
+ continue;
+
+ if(plr->GetTeam() == TeamID)
+ plr->GetSession()->SendPacket(packet);
+ }
+}
+
+void BattleGround::PlaySoundToAll(uint32 SoundID)
+{
+ WorldPacket data;
+ sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
+ SendPacketToAll(&data);
+}
+
+void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
+{
+ WorldPacket data;
+
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(plr->GetTeam() == TeamID)
+ {
+ sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
+ plr->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
+{
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(plr->GetTeam() == TeamID)
+ plr->CastSpell(plr, SpellID, true);
+ }
+}
+
+void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
+{
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(plr->GetTeam() == TeamID)
+ UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
+ }
+}
+
+void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
+{
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
+
+ if(!factionEntry)
+ return;
+
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(plr->GetTeam() == TeamID)
+ plr->ModifyFactionReputation(factionEntry, Reputation);
+ }
+}
+
+void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
+{
+ WorldPacket data;
+ sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
+ SendPacketToAll(&data);
+}
+
+void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
+{
+ WorldPacket data;
+ sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
+ Source->GetSession()->SendPacket(&data);
+}
+
+void BattleGround::EndBattleGround(uint32 winner)
+{
+ WorldPacket data;
+ Player *Source = NULL;
+ const char *winmsg = "";
+
+ if(winner == ALLIANCE)
+ {
+ winmsg = GetMangosString(LANG_BG_A_WINS);
+
+ PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
+
+ SetWinner(WINNER_ALLIANCE);
+ }
+ else
+ {
+ winmsg = GetMangosString(LANG_BG_H_WINS);
+
+ PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
+
+ SetWinner(WINNER_HORDE);
+ }
+
+ SetStatus(STATUS_WAIT_LEAVE);
+ m_EndTime = 0;
+
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+ if(!plr)
+ {
+ sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
+ continue;
+ }
+
+ if(!plr->isAlive())
+ {
+ plr->ResurrectPlayer(1.0f);
+ plr->SpawnCorpseBones();
+ }
+
+ if(plr->GetTeam() == winner)
+ {
+ if(!Source)
+ Source = plr;
+ RewardMark(plr,ITEM_WINNER_COUNT);
+ UpdatePlayerScore(plr, SCORE_BONUS_HONOR, 20);
+ RewardQuest(plr);
+ }
+ else
+ {
+ RewardMark(plr,ITEM_LOSER_COUNT);
+ }
+
+ plr->CombatStopWithPets(true);
+
+ BlockMovement(plr);
+
+ sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
+ plr->GetSession()->SendPacket(&data);
+
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime());
+ plr->GetSession()->SendPacket(&data);
+ }
+
+ if(Source)
+ {
+ ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, Source->GetGUID(), winmsg);
+ SendPacketToAll(&data);
+ }
+}
+
+uint32 BattleGround::GetBattlemasterEntry() const
+{
+ switch(GetTypeID())
+ {
+ case BATTLEGROUND_AV: return 15972;
+ case BATTLEGROUND_WS: return 14623;
+ case BATTLEGROUND_AB: return 14879;
+ case BATTLEGROUND_EY: return 22516;
+ case BATTLEGROUND_NA: return 20200;
+ default: return 0;
+ }
+}
+
+void BattleGround::RewardMark(Player *plr,uint32 count)
+{
+ // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
+ if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
+ return;
+
+ BattleGroundMarks mark;
+ bool IsSpell;
+ switch(GetTypeID())
+ {
+ case BATTLEGROUND_AV:
+ IsSpell = true;
+ if(count == ITEM_WINNER_COUNT)
+ mark = SPELL_AV_MARK_WINNER;
+ else
+ mark = SPELL_AV_MARK_LOSER;
+ break;
+ case BATTLEGROUND_WS:
+ IsSpell = true;
+ if(count == ITEM_WINNER_COUNT)
+ mark = SPELL_WS_MARK_WINNER;
+ else
+ mark = SPELL_WS_MARK_LOSER;
+ break;
+ case BATTLEGROUND_AB:
+ IsSpell = true;
+ if(count == ITEM_WINNER_COUNT)
+ mark = SPELL_AB_MARK_WINNER;
+ else
+ mark = SPELL_AB_MARK_LOSER;
+ break;
+ case BATTLEGROUND_EY:
+ IsSpell = false;
+ mark = ITEM_EY_MARK_OF_HONOR;
+ break;
+ default:
+ return;
+ }
+
+ if(IsSpell)
+ plr->CastSpell(plr, mark, true);
+ else if ( objmgr.GetItemPrototype( mark ) )
+ {
+ ItemPosCountVec dest;
+ uint32 no_space_count = 0;
+ uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, mark, count, &no_space_count );
+ if( msg != EQUIP_ERR_OK ) // convert to possible store amount
+ count -= no_space_count;
+
+ if( count != 0 && !dest.empty()) // can add some
+ if(Item* item = plr->StoreNewItem( dest, mark, true, 0))
+ plr->SendNewItem(item,count,false,true);
+
+ if(no_space_count > 0)
+ SendRewardMarkByMail(plr,mark,no_space_count);
+ }
+}
+
+void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
+{
+ uint32 bmEntry = GetBattlemasterEntry();
+ if(!bmEntry)
+ return;
+
+ ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
+ if(!markProto)
+ return;
+
+ if(Item* markItem = Item::CreateItem(mark,count,plr))
+ {
+ // save new item before send
+ markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+
+ // item
+ MailItemsInfo mi;
+ mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
+
+ // subject: item name
+ std::string subject = markProto->Name1;
+ int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ if(ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ subject = il->Name[loc_idx];
+
+ // text
+ std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
+ char textBuf[300];
+ snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
+ uint32 itemTextId = objmgr.CreateItemText( textBuf );
+
+ WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ }
+}
+
+void BattleGround::RewardQuest(Player *plr)
+{
+ // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
+ if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
+ return;
+
+ uint32 quest;
+ switch(GetTypeID())
+ {
+ case BATTLEGROUND_AV:
+ quest = SPELL_AV_QUEST_REWARD;
+ break;
+ case BATTLEGROUND_WS:
+ quest = SPELL_WS_QUEST_REWARD;
+ break;
+ case BATTLEGROUND_AB:
+ quest = SPELL_AB_QUEST_REWARD;
+ break;
+ case BATTLEGROUND_EY:
+ quest = SPELL_EY_QUEST_REWARD;
+ break;
+ default:
+ return;
+ }
+
+ plr->CastSpell(plr, quest, true);
+}
+
+void BattleGround::BlockMovement(Player *plr)
+{
+ plr->SetClientControl(plr, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave()
+}
+
+void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
+{
+ // Remove from lists/maps
+ std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.find(guid);
+ if(itr != m_Players.end())
+ {
+ UpdatePlayersCountByTeam(itr->second.Team, true); // -1 player
+ m_Players.erase(itr);
+ }
+
+ std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid);
+ if(itr2 != m_PlayerScores.end())
+ {
+ delete itr2->second; // delete player's score
+ m_PlayerScores.erase(itr2);
+ }
+
+ RemovePlayerFromResurrectQueue(guid);
+
+ Player *plr = objmgr.GetPlayer(guid);
+
+ if(plr && !plr->isAlive()) // resurrect on exit
+ {
+ plr->ResurrectPlayer(1.0f);
+ plr->SpawnCorpseBones();
+ }
+
+ RemovePlayer(plr, guid); // BG subclass specific code
+
+ if(plr)
+ {
+ plr->ClearAfkReports();
+
+ if(isArena())
+ {
+ if(!sWorld.IsFFAPvPRealm())
+ plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ }
+
+ WorldPacket data;
+ if(SendPacket)
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+
+ // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
+ plr->RemoveBattleGroundQueueId(m_TypeID);
+
+ DecreaseInvitedCount(plr->GetTeam());
+ //we should update battleground queue, but only if bg isn't ending
+ if (GetQueueType() < MAX_BATTLEGROUND_QUEUES)
+ sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].Update(GetTypeID(), GetQueueType());
+
+ if(!plr->GetBattleGroundId())
+ return;
+
+ Group * group = plr->GetGroup();
+
+ // remove from raid group if exist
+ if(group && group == GetBgRaid(plr->GetTeam()))
+ {
+ if(!group->RemoveMember(guid, 0)) // group was disbanded
+ {
+ SetBgRaid(plr->GetTeam(), NULL);
+ delete group;
+ }
+ }
+
+ // Do next only if found in battleground
+ plr->SetBattleGroundId(0); // We're not in BG.
+
+ // Let others know
+ sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr);
+ SendPacketToTeam(plr->GetTeam(), &data, plr, false);
+
+ if(Transport)
+ {
+ plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO());
+ //sLog.outDetail("BATTLEGROUND: Sending %s to %f,%f,%f,%f", pl->GetName(), x,y,z,O);
+ }
+
+ // Log
+ sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
+ }
+
+ /// there will be code which will add battleground to BGFreeSlotQueue , when battleground instance will exist
+ // we always should check if BG is in that queue before adding..
+
+ if(!GetPlayersSize())
+ {
+ Reset();
+ }
+}
+
+// this method is called when no players remains in battleground
+void BattleGround::Reset()
+{
+ SetQueueType(MAX_BATTLEGROUND_QUEUES);
+ SetWinner(WINNER_NONE);
+ SetStatus(STATUS_WAIT_QUEUE);
+ SetStartTime(0);
+ SetEndTime(0);
+ SetLastResurrectTime(0);
+
+ m_Events = 0;
+
+ if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
+ sLog.outError("BattleGround system ERROR: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
+
+ m_InvitedAlliance = 0;
+ m_InvitedHorde = 0;
+
+ m_Players.clear();
+ m_PlayerScores.clear();
+
+ // reset BGSubclass
+ this->ResetBGSubclass();
+}
+
+void BattleGround::StartBattleGround()
+{
+ ///this method should spawn spirit guides and so on
+ SetStartTime(0);
+
+ SetLastResurrectTime(0);
+}
+
+void BattleGround::AddPlayer(Player *plr)
+{
+ // score struct must be created in inherited class
+
+ uint64 guid = plr->GetGUID();
+ uint32 team = plr->GetBGTeam();
+
+ BattleGroundPlayer bp;
+ bp.LastOnlineTime = 0;
+ bp.Team = team;
+
+ // Add to list/maps
+ m_Players[guid] = bp;
+
+ UpdatePlayersCountByTeam(team, false); // +1 player
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
+ SendPacketToTeam(team, &data, plr, false);
+
+ if(isArena())
+ {
+ plr->RemoveArenaSpellCooldowns();
+ //plr->RemoveArenaAuras();
+ plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
+ if(team == ALLIANCE && plr->GetTeam() == ALLIANCE)
+ plr->CastSpell(plr,SPELL_ALLIANCE_GOLD_FLAG,true);
+ else if(team == HORDE && plr->GetTeam() == ALLIANCE)
+ plr->CastSpell(plr,SPELL_ALLIANCE_GREEN_FLAG,true);
+ else if(team == ALLIANCE && plr->GetTeam() == HORDE)
+ plr->CastSpell(plr,SPELL_HORDE_GOLD_FLAG,true);
+ else
+ plr->CastSpell(plr,SPELL_HORDE_GREEN_FLAG,true);
+ plr->DestroyConjuredItems(true);
+
+ if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
+ {
+ plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
+
+ plr->SetHealth(plr->GetMaxHealth());
+ plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
+ }
+ }
+ else
+ {
+ if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
+ plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
+ }
+
+ if(isArena())
+ plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+
+ // Log
+ sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
+}
+
+/* This method should be called only once ... it adds pointer to queue */
+void BattleGround::AddToBGFreeSlotQueue()
+{
+ sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
+}
+
+/* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
+void BattleGround::RemoveFromBGFreeSlotQueue()
+{
+ /* uncomment this code when battlegrounds will work like instances
+ for (std::deque<BattleGround*>::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
+ {
+ if ((*itr)->GetInstanceID() == m_InstanceID)
+ {
+ sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
+ return;
+ }
+ }*/
+}
+
+/*
+this method should decide, if we can invite new player of certain team to BG, it is based on BATTLEGROUND_STATUS
+*/
+bool BattleGround::HasFreeSlotsForTeam(uint32 Team) const
+{
+ //if BG is starting ... invite anyone:
+ if (GetStatus() == STATUS_WAIT_JOIN)
+ return GetInvitedCount(Team) < GetMaxPlayersPerTeam();
+ //if BG is already started .. do not allow to join too much players of one faction
+ uint32 otherTeam;
+ if (Team == ALLIANCE)
+ otherTeam = GetInvitedCount(HORDE);
+ else
+ otherTeam = GetInvitedCount(ALLIANCE);
+ if (GetStatus() == STATUS_IN_PROGRESS)
+ return (GetInvitedCount(Team) <= otherTeam && GetInvitedCount(Team) < GetMaxPlayersPerTeam());
+
+ return false;
+}
+
+/* this method isn't called already, it will be useful when more battlegrounds of one type will be available */
+bool BattleGround::HasFreeSlots() const
+{
+ return GetPlayersSize() < GetMaxPlayers();
+}
+
+void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
+{
+ //this procedure is called from virtual function implemented in bg subclass
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if(itr == m_PlayerScores.end()) // player not found...
+ return;
+
+ switch(type)
+ {
+ case SCORE_KILLING_BLOWS: // Killing blows
+ itr->second->KillingBlows += value;
+ break;
+ case SCORE_DEATHS: // Deaths
+ itr->second->Deaths += value;
+ break;
+ case SCORE_HONORABLE_KILLS: // Honorable kills
+ itr->second->HonorableKills += value;
+ break;
+ case SCORE_BONUS_HONOR: // Honor bonus
+ // reward honor instantly
+ if(Source->RewardHonor(NULL, 1, value))
+ itr->second->BonusHonor += value;
+ break;
+ //used only in EY, but in MSG_PVP_LOG_DATA opcode
+ case SCORE_DAMAGE_DONE: // Damage Done
+ itr->second->DamageDone += value;
+ break;
+ case SCORE_HEALING_DONE: // Healing Done
+ itr->second->HealingDone += value;
+ break;
+ default:
+ sLog.outError("BattleGround: Unknown player score type %u", type);
+ break;
+ }
+}
+
+void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
+{
+ m_ReviveQueue[npc_guid].push_back(player_guid);
+
+ Player *plr = objmgr.GetPlayer(player_guid);
+ if(!plr)
+ return;
+
+ plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT );
+ if(spellInfo)
+ {
+ Aura *Aur = CreateAura(spellInfo, 0, NULL, plr);
+ plr->AddAura(Aur);
+ }
+}
+
+void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
+{
+ for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
+ {
+ for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
+ {
+ if(*itr2 == player_guid)
+ {
+ (itr->second).erase(itr2);
+
+ Player *plr = objmgr.GetPlayer(player_guid);
+ if(!plr)
+ return;
+
+ plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
+
+ return;
+ }
+ }
+ }
+}
+
+bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime)
+{
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
+ if(!goinfo)
+ {
+ sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
+ return false;
+ }
+
+ uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ GameObjectData& data = objmgr.NewGOData(guid);
+
+ data.id = entry;
+ data.mapid = GetMapId();
+ data.posX = x;
+ data.posY = y;
+ data.posZ = z;
+ data.orientation = o;
+ data.rotation0 = rotation0;
+ data.rotation1 = rotation1;
+ data.rotation2 = rotation2;
+ data.rotation3 = rotation3;
+ data.spawntimesecs = respawnTime;
+ data.animprogress = 100;
+ data.go_state = 1;
+ data.spawnMask = 1;
+ objmgr.AddGameobjectToGrid(guid, &data);
+
+ m_BgObjects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
+
+ return true;
+}
+
+//some doors aren't despawned so we cannot handle their closing in gameobject::update()
+//it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
+void BattleGround::DoorClose(uint32 type)
+{
+ GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
+ if(obj)
+ {
+ //if doors are open, close it
+ if( obj->getLootState() == GO_ACTIVATED && !obj->GetGoState() )
+ {
+ //change state to allow door to be closed
+ obj->SetLootState(GO_READY);
+ obj->UseDoorOrButton(RESPAWN_ONE_DAY);
+ }
+ }
+ else
+ {
+ sLog.outError("BattleGround: Door object not found (cannot close doors)");
+ }
+}
+
+void BattleGround::DoorOpen(uint32 type)
+{
+ GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
+ if(obj)
+ {
+ //change state to be sure they will be opened
+ obj->SetLootState(GO_READY);
+ obj->UseDoorOrButton(RESPAWN_ONE_DAY);
+ }
+ else
+ {
+ sLog.outError("BattleGround: Door object not found! - doors will be closed.");
+ }
+}
+
+void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
+{
+ if( respawntime == 0 )
+ {
+ GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
+ if(obj)
+ {
+ //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
+ if( obj->getLootState() == GO_JUST_DEACTIVATED )
+ obj->SetLootState(GO_READY);
+ obj->Respawn();
+ }
+ else
+ objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0);
+ }
+ else
+ {
+ GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
+ if(obj)
+ {
+ obj->SetRespawnTime(respawntime);
+ obj->SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else
+ objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime);
+ }
+}
+
+Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o)
+{
+ // note: this should normally be FindMap
+ // but it's a hack to allow the battlegrounds to initialize at server startup
+ Map * map = MapManager::Instance().GetMap(GetMapId(), 0);
+ if(!map) return NULL;
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, entry, teamval))
+ {
+ sLog.outError("Can't create creature entry: %u",entry);
+ delete pCreature;
+ return NULL;
+ }
+
+ pCreature->Relocate(x, y, z, o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ return NULL;
+ }
+
+ pCreature->AIM_Initialize();
+
+ //pCreature->SetDungeonDifficulty(0);
+
+ map->Add(pCreature);
+ m_BgCreatures[type] = pCreature->GetGUID();
+ return pCreature;
+}
+
+bool BattleGround::DelCreature(uint32 type)
+{
+ Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
+ if(!cr)
+ {
+ sLog.outError("Can't find creature guid: %u",m_BgCreatures[type]);
+ return false;
+ }
+ cr->CleanupsBeforeDelete();
+ cr->AddObjectToRemoveList();
+ m_BgCreatures[type] = 0;
+ return true;
+}
+
+bool BattleGround::DelObject(uint32 type)
+{
+ GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
+ if(!obj)
+ {
+ sLog.outError("Can't find gobject guid: %u",m_BgObjects[type]);
+ return false;
+ }
+ obj->SetRespawnTime(0); // not save respawn time
+ obj->Delete();
+ m_BgObjects[type] = 0;
+ return true;
+}
+
+bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
+{
+ uint32 entry = 0;
+
+ if(team == ALLIANCE)
+ entry = 13116;
+ else
+ entry = 13117;
+
+ Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
+ if(!pCreature)
+ {
+ sLog.outError("Can't create Spirit guide. BattleGround not created!");
+ this->EndNow();
+ return false;
+ }
+
+ pCreature->setDeathState(DEAD);
+
+ pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
+ // aura
+ pCreature->SetUInt32Value(UNIT_FIELD_AURA, SPELL_SPIRIT_HEAL_CHANNEL);
+ pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
+ pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
+ pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
+ // casting visual effect
+ pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
+ // correct cast speed
+ pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
+
+ //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
+
+ return true;
+}
+
+void BattleGround::SendMessageToAll(char const* text)
+{
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL);
+ SendPacketToAll(&data);
+}
+
+void BattleGround::SendMessageToAll(int32 entry)
+{
+ char const* text = GetMangosString(entry);
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL);
+ SendPacketToAll(&data);
+}
+
+void BattleGround::EndNow()
+{
+ SetStatus(STATUS_WAIT_LEAVE);
+ SetEndTime(TIME_TO_AUTOREMOVE);
+}
+
+// Battleground messages are localized using the dbc lang, they are not client language dependent
+const char *BattleGround::GetMangosString(int32 entry)
+{
+ // FIXME: now we have different DBC locales and need localized message for each target client
+ return objmgr.GetMangosStringForDBCLocale(entry);
+}
+
+/*
+important notice:
+buffs aren't spawned/despawned when players captures anything
+buffs are in their positions when battleground starts
+*/
+void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
+{
+ GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
+ if(!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
+ return;
+
+ //change buff type, when buff is used:
+ int32 index = m_BgObjects.size() - 1;
+ while (index >= 0 && m_BgObjects[index] != go_guid)
+ index--;
+ if (index < 0)
+ {
+ sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType());
+ return;
+ }
+
+ //randomly select new buff
+ uint8 buff = urand(0, 2);
+ uint32 entry = obj->GetEntry();
+ if( m_BuffChange && entry != Buff_Entries[buff] )
+ {
+ //despawn current buff
+ SpawnBGObject(index, RESPAWN_ONE_DAY);
+ //set index for new one
+ for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
+ if( entry == Buff_Entries[currBuffTypeIndex] )
+ {
+ index -= currBuffTypeIndex;
+ index += buff;
+ }
+ }
+
+ SpawnBGObject(index, BUFF_RESPAWN_TIME);
+}
+
+void BattleGround::HandleKillPlayer( Player *player, Player *killer )
+{
+ //keep in mind that for arena this will have to be changed a bit
+
+ // add +1 deaths
+ UpdatePlayerScore(player, SCORE_DEATHS, 1);
+
+ // add +1 kills to group and +1 killing_blows to killer
+ if( killer )
+ {
+ UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
+ UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
+
+ for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Player *plr = objmgr.GetPlayer(itr->first);
+
+ if(!plr || plr == killer)
+ continue;
+
+ if( plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player) )
+ UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
+ }
+ }
+
+ // to be able to remove insignia
+ player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
+}
diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h
new file mode 100644
index 00000000000..154e5b02416
--- /dev/null
+++ b/src/game/BattleGround.h
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __BATTLEGROUND_H
+#define __BATTLEGROUND_H
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "BattleGroundMgr.h"
+#include "SharedDefines.h"
+
+enum BattleGroundSounds
+{
+ SOUND_HORDE_WINS = 8454,
+ SOUND_ALLIANCE_WINS = 8455,
+ SOUND_BG_START = 3439
+};
+
+enum BattleGroundQuests
+{
+ SPELL_WS_QUEST_REWARD = 43483,
+ SPELL_AB_QUEST_REWARD = 43484,
+ SPELL_AV_QUEST_REWARD = 43475,
+ SPELL_AV_QUEST_KILLED_BOSS = 23658,
+ SPELL_EY_QUEST_REWARD = 43477,
+ SPELL_AB_QUEST_REWARD_4_BASES = 24061,
+ SPELL_AB_QUEST_REWARD_5_BASES = 24064
+};
+
+enum BattleGroundMarks
+{
+ SPELL_WS_MARK_LOSER = 24950,
+ SPELL_WS_MARK_WINNER = 24951,
+ SPELL_AB_MARK_LOSER = 24952,
+ SPELL_AB_MARK_WINNER = 24953,
+ SPELL_AV_MARK_LOSER = 24954,
+ SPELL_AV_MARK_WINNER = 24955,
+ ITEM_EY_MARK_OF_HONOR = 29024
+};
+
+enum BattleGroundMarksCount
+{
+ ITEM_WINNER_COUNT = 3,
+ ITEM_LOSER_COUNT = 1
+};
+
+enum BattleGroundSpells
+{
+ SPELL_WAITING_FOR_RESURRECT = 2584, // Waiting to Resurrect
+ SPELL_SPIRIT_HEAL_CHANNEL = 22011, // Spirit Heal Channel
+ SPELL_SPIRIT_HEAL = 22012, // Spirit Heal
+ SPELL_RESURRECTION_VISUAL = 24171, // Resurrection Impact Visual
+ SPELL_ARENA_PREPARATION = 32727, // use this one, 32728 not correct
+ SPELL_ALLIANCE_GOLD_FLAG = 32724,
+ SPELL_ALLIANCE_GREEN_FLAG = 32725,
+ SPELL_HORDE_GOLD_FLAG = 35774,
+ SPELL_HORDE_GREEN_FLAG = 35775,
+ SPELL_PREPARATION = 44521, // Preparation
+ SPELL_SPIRIT_HEAL_MANA = 44535, // Spirit Heal
+ SPELL_RECENTLY_DROPPED_FLAG = 42792, // Recently Dropped Flag
+ SPELL_AURA_PLAYER_INACTIVE = 43681 // Inactive
+};
+
+enum BattleGroundTimeIntervals
+{
+ RESURRECTION_INTERVAL = 30000, // ms
+ REMIND_INTERVAL = 30000, // ms
+ INVITE_ACCEPT_WAIT_TIME = 120000, // ms
+ TIME_TO_AUTOREMOVE = 120000, // ms
+ MAX_OFFLINE_TIME = 300000, // ms
+ START_DELAY0 = 120000, // ms
+ START_DELAY1 = 60000, // ms
+ START_DELAY2 = 30000, // ms
+ START_DELAY3 = 15000, // ms used only in arena
+ RESPAWN_ONE_DAY = 86400, // secs
+ RESPAWN_IMMEDIATELY = 0, // secs
+ BUFF_RESPAWN_TIME = 180, // secs
+ BG_HONOR_SCORE_TICKS = 330 // points
+};
+
+enum BattleGroundBuffObjects
+{
+ BG_OBJECTID_SPEEDBUFF_ENTRY = 179871,
+ BG_OBJECTID_REGENBUFF_ENTRY = 179904,
+ BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905
+};
+
+const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY };
+
+enum BattleGroundStatus
+{
+ STATUS_NONE = 0,
+ STATUS_WAIT_QUEUE = 1,
+ STATUS_WAIT_JOIN = 2,
+ STATUS_IN_PROGRESS = 3,
+ STATUS_WAIT_LEAVE = 4 // custom
+};
+
+struct BattleGroundPlayer
+{
+ uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes
+ uint32 Team; // Player's team
+};
+
+struct BattleGroundObjectInfo
+{
+ BattleGroundObjectInfo() : object(NULL), timer(0), spellid(0) {}
+
+ GameObject *object;
+ int32 timer;
+ uint32 spellid;
+};
+
+#define MAX_QUEUED_PLAYERS_MAP 7
+
+enum BattleGroundTypeId
+{
+ BATTLEGROUND_AV = 1,
+ BATTLEGROUND_WS = 2,
+ BATTLEGROUND_AB = 3,
+ BATTLEGROUND_NA = 4,
+ BATTLEGROUND_BE = 5,
+ BATTLEGROUND_AA = 6,
+ BATTLEGROUND_EY = 7,
+ BATTLEGROUND_RL = 8
+};
+
+enum ScoreType
+{
+ SCORE_KILLING_BLOWS = 1,
+ SCORE_DEATHS = 2,
+ SCORE_HONORABLE_KILLS = 3,
+ SCORE_BONUS_HONOR = 4,
+ //EY, but in MSG_PVP_LOG_DATA opcode!
+ SCORE_DAMAGE_DONE = 5,
+ SCORE_HEALING_DONE = 6,
+ //WS
+ SCORE_FLAG_CAPTURES = 7,
+ SCORE_FLAG_RETURNS = 8,
+ //AB
+ SCORE_BASES_ASSAULTED = 9,
+ SCORE_BASES_DEFENDED = 10,
+ //AV
+ SCORE_GRAVEYARDS_ASSAULTED = 11,
+ SCORE_GRAVEYARDS_DEFENDED = 12,
+ SCORE_TOWERS_ASSAULTED = 13,
+ SCORE_TOWERS_DEFENDED = 14,
+ SCORE_MINES_CAPTURED = 15,
+ SCORE_LEADERS_KILLED = 16,
+ SCORE_SECONDARY_OBJECTIVES = 17
+ // TODO : implement them
+};
+
+enum ArenaType
+{
+ ARENA_TYPE_2v2 = 2,
+ ARENA_TYPE_3v3 = 3,
+ ARENA_TYPE_5v5 = 5
+};
+
+enum BattleGroundType
+{
+ TYPE_BATTLEGROUND = 3,
+ TYPE_ARENA = 4
+};
+
+enum BattleGroundWinner
+{
+ WINNER_HORDE = 0,
+ WINNER_ALLIANCE = 1,
+ WINNER_NONE = 2
+};
+
+enum BattleGroundTeamId
+{
+ BG_TEAM_ALLIANCE = 0,
+ BG_TEAM_HORDE = 1
+};
+
+class BattleGroundScore
+{
+ public:
+ BattleGroundScore() : KillingBlows(0), HonorableKills(0), Deaths(0), DamageDone(0), HealingDone(0), BonusHonor(0) {};
+ virtual ~BattleGroundScore() //virtual destructor is used when deleting score from scores map
+ {
+ };
+ uint32 KillingBlows;
+ uint32 Deaths;
+ uint32 HonorableKills;
+ uint32 BonusHonor;
+ uint32 DamageDone;
+ uint32 HealingDone;
+};
+
+/*
+This class is used to:
+1. Add player to battleground
+2. Remove player from battleground
+3. some certain cases, same for all battlegrounds
+4. It has properties same for all battlegrounds
+*/
+class BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ /* Construction */
+ BattleGround();
+ virtual ~BattleGround();
+ virtual void Update(time_t diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version
+ virtual bool SetupBattleGround() // must be implemented in BG subclass
+ {
+ return true;
+ }
+ void Reset(); // resets all common properties for battlegrounds
+ virtual void ResetBGSubclass() // must be implemented in BG subclass
+ {
+ }
+
+ /* Battleground */
+ // Get methods:
+ char const* GetName() const { return m_Name; }
+ uint32 GetTypeID() const { return m_TypeID; }
+ uint32 GetQueueType() const { return m_Queue_type; }
+ uint32 GetInstanceID() const { return m_InstanceID; }
+ uint32 GetStatus() const { return m_Status; }
+ uint32 GetStartTime() const { return m_StartTime; }
+ uint32 GetEndTime() const { return m_EndTime; }
+ uint32 GetLastResurrectTime() const { return m_LastResurrectTime; }
+ uint32 GetMaxPlayers() const { return m_MaxPlayers; }
+ uint32 GetMinPlayers() const { return m_MinPlayers; }
+
+ uint32 GetMinLevel() const { return m_LevelMin; }
+ uint32 GetMaxLevel() const { return m_LevelMax; }
+
+ uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; }
+ uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; }
+
+ int GetStartDelayTime() const { return m_StartDelayTime; }
+ uint8 GetArenaType() const { return m_ArenaType; }
+ uint8 GetWinner() const { return m_Winner; }
+ uint32 GetBattlemasterEntry() const;
+
+ // Set methods:
+ void SetName(char const* Name) { m_Name = Name; }
+ void SetTypeID(uint32 TypeID) { m_TypeID = TypeID; }
+ void SetQueueType(uint32 ID) { m_Queue_type = ID; }
+ void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; }
+ void SetStatus(uint32 Status) { m_Status = Status; }
+ void SetStartTime(uint32 Time) { m_StartTime = Time; }
+ void SetEndTime(uint32 Time) { m_EndTime = Time; }
+ void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; }
+ void SetMaxPlayers(uint32 MaxPlayers) { m_MaxPlayers = MaxPlayers; }
+ void SetMinPlayers(uint32 MinPlayers) { m_MinPlayers = MinPlayers; }
+ void SetLevelRange(uint32 min, uint32 max) { m_LevelMin = min; m_LevelMax = max; }
+ void SetRated(bool state) { m_IsRated = state; }
+ void SetArenaType(uint8 type) { m_ArenaType = type; }
+ void SetArenaorBGType(bool _isArena) { m_IsArena = _isArena; }
+ void SetWinner(uint8 winner) { m_Winner = winner; }
+
+ void ModifyStartDelayTime(int diff) { m_StartDelayTime -= diff; }
+ void SetStartDelayTime(int Time) { m_StartDelayTime = Time; }
+
+ void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; }
+ void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; }
+
+ void AddToBGFreeSlotQueue(); //this queue will be useful when more battlegrounds instances will be available
+ void RemoveFromBGFreeSlotQueue(); //this method could delete whole BG instance, if another free is available
+
+ void DecreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; }
+ void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; }
+ uint32 GetInvitedCount(uint32 team) const
+ {
+ if( team == ALLIANCE )
+ return m_InvitedAlliance;
+ else
+ return m_InvitedHorde;
+ }
+ bool HasFreeSlotsForTeam(uint32 Team) const;
+ bool HasFreeSlots() const;
+
+ bool isArena() const { return m_IsArena; }
+ bool isBattleGround() const { return !m_IsArena; }
+ bool isRated() const { return m_IsRated; }
+
+ typedef std::map<uint64, BattleGroundPlayer> BattleGroundPlayerMap;
+ BattleGroundPlayerMap const& GetPlayers() const { return m_Players; }
+ uint32 GetPlayersSize() const { return m_Players.size(); }
+ uint32 GetRemovedPlayersSize() const { return m_RemovedPlayers.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(); }
+ uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); }
+
+ uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); }
+
+ void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid);
+ void RemovePlayerFromResurrectQueue(uint64 player_guid);
+
+ void StartBattleGround();
+
+ /* Location */
+ void SetMapId(uint32 MapID) { m_MapId = MapID; }
+ uint32 GetMapId() const { return m_MapId; }
+
+ 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);
+ X = m_TeamStartLocX[idx];
+ Y = m_TeamStartLocY[idx];
+ Z = m_TeamStartLocZ[idx];
+ O = m_TeamStartLocO[idx];
+ }
+
+ /* Packet Transfer */
+ // method that should fill worldpacket with actual world states (not yet implemented for all battlegrounds!)
+ virtual void FillInitialWorldStates(WorldPacket& /*data*/) {}
+ void SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender = NULL, bool self = true);
+ void SendPacketToAll(WorldPacket *packet);
+ void PlaySoundToTeam(uint32 SoundID, uint32 TeamID);
+ void PlaySoundToAll(uint32 SoundID);
+ void CastSpellOnTeam(uint32 SpellID, uint32 TeamID);
+ void RewardHonorToTeam(uint32 Honor, uint32 TeamID);
+ void RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID);
+ void RewardMark(Player *plr,uint32 count);
+ void SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count);
+ void RewardQuest(Player *plr);
+ void UpdateWorldState(uint32 Field, uint32 Value);
+ void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source);
+ void EndBattleGround(uint32 winner);
+ void BlockMovement(Player *plr);
+
+ void SendMessageToAll(char const* text);
+ void SendMessageToAll(int32 entry);
+
+ /* Raid Group */
+ Group *GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; }
+ void SetBgRaid(uint32 TeamID, Group *bg_raid)
+ {
+ Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
+ if(old_raid) old_raid->SetBattlegroundGroup(NULL);
+ if(bg_raid) bg_raid->SetBattlegroundGroup(this);
+ old_raid = bg_raid;
+ }
+
+ virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
+ uint8 GetTeamIndexByTeamId(uint32 Team) const { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; }
+ uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; }
+ void UpdatePlayersCountByTeam(uint32 Team, bool remove)
+ {
+ if(remove)
+ --m_PlayersCount[GetTeamIndexByTeamId(Team)];
+ else
+ ++m_PlayersCount[GetTeamIndexByTeamId(Team)];
+ }
+
+ /* Triggers handle */
+ // must be implemented in BG subclass
+ virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {}
+ // must be implemented in BG subclass if need AND call base class generic code
+ virtual void HandleKillPlayer(Player *player, Player *killer);
+
+ /* Battleground events */
+ /* these functions will return true event is possible, but false if player is bugger */
+ virtual void EventPlayerDroppedFlag(Player* /*player*/) {}
+ virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {}
+ virtual void EventPlayerCapturedFlag(Player* /*player*/) {}
+
+ /* Death related */
+ virtual WorldSafeLocsEntry const* GetClosestGraveYard(float /*x*/, float /*y*/, float /*z*/, uint32 /*team*/) { return NULL; }
+
+ virtual void AddPlayer(Player *plr); // must be implemented in BG subclass
+ virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket);
+ // can be extended in in BG subclass
+
+ void HandleTriggerBuff(uint64 const& go_guid);
+
+ // TODO: make this protected:
+ typedef std::vector<uint64> BGObjects;
+ typedef std::vector<uint64> BGCreatures;
+ BGObjects m_BgObjects;
+ BGCreatures m_BgCreatures;
+ void SpawnBGObject(uint32 type, uint32 respawntime);
+ bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0);
+ Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o);
+ bool DelCreature(uint32 type);
+ bool DelObject(uint32 type);
+ bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team);
+
+ void DoorOpen(uint32 type);
+ void DoorClose(uint32 type);
+ const char *GetMangosString(int32 entry);
+
+ protected:
+ //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround
+ void EndNow();
+
+ /* Scorekeeping */
+ // Player scores
+ std::map<uint64, BattleGroundScore*> m_PlayerScores;
+ // must be implemented in BG subclass
+ virtual void RemovePlayer(Player * /*player*/, uint64 /*guid*/) {}
+
+ /* Player lists, those need to be accessible by inherited classes */
+ BattleGroundPlayerMap m_Players;
+ // Spirit Guide guid + Player list GUIDS
+ std::map<uint64, std::vector<uint64> > m_ReviveQueue;
+
+ /*
+ this is important variable used for invitation messages
+ */
+ uint8 m_Events;
+
+ bool m_BuffChange;
+
+ private:
+ /* Battleground */
+ uint32 m_TypeID; //Battleground type, defined in enum BattleGroundTypeId
+ uint32 m_InstanceID; //BattleGround Instance's GUID!
+ uint32 m_Status;
+ uint32 m_StartTime;
+ uint32 m_EndTime;
+ uint32 m_LastResurrectTime;
+ uint32 m_Queue_type;
+ uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5
+ // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless
+ //uint8 m_BattleGroundType; // 3=BG, 4=arena
+ //instead of uint8 (in previous line) is bool used
+ bool m_IsArena;
+ uint8 m_Winner; // 0=alliance, 1=horde, 2=none
+ int32 m_StartDelayTime;
+ bool m_IsRated; // is this battle rated?
+ char const *m_Name;
+
+ /* Player lists */
+ std::vector<uint64> m_ResurrectQueue; // Player GUID
+ std::map<uint64, uint8> m_RemovedPlayers; // uint8 is remove type (0 - bgqueue, 1 - bg, 2 - resurrect queue)
+
+ /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */
+ /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */
+ /* Invited players counters*/
+ uint32 m_InvitedAlliance;
+ uint32 m_InvitedHorde;
+
+ /* Raid Group */
+ Group *m_BgRaids[2]; // 0 - alliance, 1 - horde
+
+ /* Players count by team */
+ uint32 m_PlayersCount[2];
+
+ /* Limits */
+ uint32 m_LevelMin;
+ uint32 m_LevelMax;
+ uint32 m_MaxPlayersPerTeam;
+ uint32 m_MaxPlayers;
+ uint32 m_MinPlayersPerTeam;
+ uint32 m_MinPlayers;
+
+ /* Location */
+ uint32 m_MapId;
+ float m_TeamStartLocX[2];
+ float m_TeamStartLocY[2];
+ float m_TeamStartLocZ[2];
+ float m_TeamStartLocO[2];
+};
+#endif
diff --git a/src/game/BattleGroundAA.cpp b/src/game/BattleGroundAA.cpp
new file mode 100644
index 00000000000..6929ef29681
--- /dev/null
+++ b/src/game/BattleGroundAA.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundAA.h"
+
+BattleGroundAA::BattleGroundAA()
+{
+
+}
+
+BattleGroundAA::~BattleGroundAA()
+{
+
+}
+
+void BattleGroundAA::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+}
+
+void BattleGroundAA::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundAAScore* sc = new BattleGroundAAScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundAA::RemovePlayer(Player * /*plr*/, uint64 /*guid*/)
+{
+}
+
+void BattleGroundAA::HandleKillPlayer(Player* player, Player* killer)
+{
+ BattleGround::HandleKillPlayer(player, killer);
+}
+
+void BattleGroundAA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/)
+{
+}
+
+bool BattleGroundAA::SetupBattleGround()
+{
+ return true;
+}
diff --git a/src/game/BattleGroundAA.h b/src/game/BattleGroundAA.h
new file mode 100644
index 00000000000..3db494430ee
--- /dev/null
+++ b/src/game/BattleGroundAA.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef __BATTLEGROUNDAA_H
+#define __BATTLEGROUNDAA_H
+
+class BattleGround;
+
+class BattleGroundAAScore : public BattleGroundScore
+{
+ public:
+ BattleGroundAAScore() {};
+ virtual ~BattleGroundAAScore() {};
+ //TODO fix me
+};
+
+class BattleGroundAA : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundAA();
+ ~BattleGroundAA();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+ void RemovePlayer(Player *plr, uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ bool SetupBattleGround();
+ void HandleKillPlayer(Player* player, Player *killer);
+};
+#endif
diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp
new file mode 100644
index 00000000000..feb305e546a
--- /dev/null
+++ b/src/game/BattleGroundAB.cpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundAB.h"
+#include "Creature.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Language.h"
+#include "Util.h"
+
+BattleGroundAB::BattleGroundAB()
+{
+ m_BuffChange = true;
+ m_BgObjects.resize(BG_AB_OBJECT_MAX);
+ m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT);
+}
+
+BattleGroundAB::~BattleGroundAB()
+{
+}
+
+void BattleGroundAB::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+
+ if( GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize() )
+ {
+ ModifyStartDelayTime(diff);
+
+ if( !(m_Events & 0x01) )
+ {
+ m_Events |= 0x01;
+
+ sLog.outDebug("Arathi Basin: entering state STATUS_WAIT_JOIN ...");
+
+ // despawn banners, auras and buffs
+ for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj)
+ SpawnBGObject(obj, RESPAWN_ONE_DAY);
+ for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i)
+ SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY);
+
+ // Starting doors
+ SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY);
+ DoorClose(BG_AB_OBJECT_GATE_A);
+ DoorClose(BG_AB_OBJECT_GATE_H);
+
+ // Starting base spirit guides
+ _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE);
+ _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE);
+
+ SetStartDelayTime(START_DELAY0);
+ }
+ // After 1 minute, warning is signalled
+ else if( GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04) )
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(GetMangosString(LANG_BG_AB_ONEMINTOSTART));
+ }
+ // After 1,5 minute, warning is signalled
+ else if( GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08) )
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(GetMangosString(LANG_BG_AB_HALFMINTOSTART));
+ }
+ // After 2 minutes, gates OPEN ! x)
+ else if( GetStartDelayTime() < 0 && !(m_Events & 0x10) )
+ {
+ m_Events |= 0x10;
+ SendMessageToAll(GetMangosString(LANG_BG_AB_STARTED));
+
+ // spawn neutral banners
+ for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i)
+ SpawnBGObject(banner, RESPAWN_IMMEDIATELY);
+ for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ {
+ //randomly select buff to spawn
+ uint8 buff = urand(0, 2);
+ SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY);
+ }
+ DoorOpen(BG_AB_OBJECT_GATE_A);
+ DoorOpen(BG_AB_OBJECT_GATE_H);
+
+ PlaySoundToAll(SOUND_BG_START);
+ SetStatus(STATUS_IN_PROGRESS);
+
+ 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 )
+ {
+ int team_points[2] = { 0, 0 };
+
+ for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node)
+ {
+ // 3 sec delay to spawn new banner instead previous despawned one
+ if( m_BannerTimers[node].timer )
+ {
+ if( m_BannerTimers[node].timer > diff )
+ m_BannerTimers[node].timer -= diff;
+ else
+ {
+ m_BannerTimers[node].timer = 0;
+ _CreateBanner(node, m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false);
+ }
+ }
+
+ // 1-minute to occupy a node from contested state
+ if( m_NodeTimers[node] )
+ {
+ if( m_NodeTimers[node] > diff )
+ m_NodeTimers[node] -= diff;
+ else
+ {
+ m_NodeTimers[node] = 0;
+ // Change from contested to occupied !
+ uint8 teamIndex = m_Nodes[node]-1;
+ m_prevNodes[node] = m_Nodes[node];
+ m_Nodes[node] += 2;
+ // burn current contested banner
+ _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex);
+ // create new occupied banner
+ _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true);
+ _SendNodeUpdate(node);
+ _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE);
+ // Message to chatlog
+ char buf[256];
+ uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE;
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE), _GetNodeName(node));
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL);
+ SendPacketToAll(&data);
+ PlaySoundToAll((teamIndex == 0) ? SOUND_NODE_CAPTURED_ALLIANCE : SOUND_NODE_CAPTURED_HORDE);
+ }
+ }
+
+ for (int team = 0; team < 2; ++team)
+ if( m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED )
+ ++team_points[team];
+ }
+
+ // Accumulate points
+ for (int team = 0; team < 2; ++team)
+ {
+ int points = team_points[team];
+ if( !points )
+ continue;
+ m_lastTick[team] += diff;
+ if( m_lastTick[team] > BG_AB_TickIntervals[points] )
+ {
+ m_lastTick[team] -= BG_AB_TickIntervals[points];
+ m_TeamScores[team] += BG_AB_TickPoints[points];
+ m_HonorScoreTics[team] += BG_AB_TickPoints[points];
+ m_ReputationScoreTics[team] += BG_AB_TickPoints[points];
+ if( m_ReputationScoreTics[team] >= 200 )
+ {
+ (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE);
+ m_ReputationScoreTics[team] -= 200;
+ }
+ if( m_HonorScoreTics[team] >= BG_HONOR_SCORE_TICKS )
+ {
+ (team == BG_TEAM_ALLIANCE) ? RewardHonorToTeam(20, ALLIANCE) : RewardHonorToTeam(20, HORDE);
+ m_HonorScoreTics[team] -= BG_HONOR_SCORE_TICKS;
+ }
+ if( !m_IsInformedNearVictory && m_TeamScores[team] > 1800 )
+ {
+ if( team == BG_TEAM_ALLIANCE )
+ SendMessageToAll(GetMangosString(LANG_BG_AB_A_NEAR_VICTORY));
+ else
+ SendMessageToAll(GetMangosString(LANG_BG_AB_H_NEAR_VICTORY));
+ PlaySoundToAll(SOUND_NEAR_VICTORY);
+ m_IsInformedNearVictory = true;
+ }
+
+ if( m_TeamScores[team] > 2000 )
+ m_TeamScores[team] = 2000;
+ if( team == BG_TEAM_ALLIANCE )
+ UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]);
+ if( team == BG_TEAM_HORDE )
+ UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]);
+ }
+ }
+
+ // Test win condition
+ if( m_TeamScores[BG_TEAM_ALLIANCE] >= 2000 )
+ EndBattleGround(ALLIANCE);
+ if( m_TeamScores[BG_TEAM_HORDE] >= 2000 )
+ EndBattleGround(HORDE);
+ }
+}
+
+void BattleGroundAB::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in the constructor
+ BattleGroundABScore* sc = new BattleGroundABScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/)
+{
+
+}
+
+void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ if( GetStatus() != STATUS_IN_PROGRESS )
+ return;
+
+ switch(Trigger)
+ {
+ case 3948: // Arathi Basin Alliance Exit.
+ if( Source->GetTeam() != ALLIANCE )
+ Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal");
+ else
+ Source->LeaveBattleground();
+ break;
+ case 3949: // Arathi Basin Horde Exit.
+ if( Source->GetTeam() != HORDE )
+ Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal");
+ else
+ Source->LeaveBattleground();
+ break;
+ case 3866: // Stables
+ case 3869: // Gold Mine
+ case 3867: // Farm
+ case 3868: // Lumber Mill
+ case 3870: // Black Smith
+ case 4020: // Unk1
+ case 4021: // Unk2
+ //break;
+ default:
+ //sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ //Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+}
+
+/* type: 0-neutral, 1-contested, 3-occupied
+ teamIndex: 0-ally, 1-horde */
+void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay)
+{
+ // Just put it into the queue
+ if( delay )
+ {
+ m_BannerTimers[node].timer = 2000;
+ m_BannerTimers[node].type = type;
+ m_BannerTimers[node].teamIndex = teamIndex;
+ return;
+ }
+
+ uint8 obj = node*8 + type + teamIndex;
+
+ SpawnBGObject(obj, RESPAWN_IMMEDIATELY);
+
+ // handle aura with banner
+ if( !type )
+ return;
+ obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7);
+ SpawnBGObject(obj, RESPAWN_IMMEDIATELY);
+}
+
+void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex)
+{
+ uint8 obj = node*8 + type + teamIndex;
+ SpawnBGObject(obj, RESPAWN_ONE_DAY);
+
+ // handle aura with banner
+ if( !type )
+ return;
+ obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7);
+ SpawnBGObject(obj, RESPAWN_ONE_DAY);
+}
+
+const char* BattleGroundAB::_GetNodeName(uint8 node)
+{
+ switch (node)
+ {
+ case BG_AB_NODE_STABLES:
+ return GetMangosString(LANG_BG_AB_NODE_STABLES);
+ case BG_AB_NODE_BLACKSMITH:
+ return GetMangosString(LANG_BG_AB_NODE_BLACKSMITH);
+ case BG_AB_NODE_FARM:
+ return GetMangosString(LANG_BG_AB_NODE_FARM);
+ case BG_AB_NODE_LUMBER_MILL:
+ return GetMangosString(LANG_BG_AB_NODE_LUMBER_MILL);
+ case BG_AB_NODE_GOLD_MINE:
+ return GetMangosString(LANG_BG_AB_NODE_GOLD_MINE);
+ default:
+ ASSERT(0);
+ }
+ return "";
+}
+
+void BattleGroundAB::FillInitialWorldStates(WorldPacket& data)
+{
+ const uint8 plusArray[] = {0, 2, 3, 0, 1};
+
+ // Node icons
+ for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node)
+ data << uint32(BG_AB_OP_NODEICONS[node]) << uint32((m_Nodes[node]==0)?1:0);
+
+ // Node occupied states
+ for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node)
+ for (uint8 i = 1; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ data << uint32(BG_AB_OP_NODESTATES[node] + plusArray[i]) << uint32((m_Nodes[node]==i)?1:0);
+
+ // How many bases each team owns
+ uint8 ally = 0, horde = 0;
+ for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node)
+ if( m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED )
+ ++ally;
+ else if( m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED )
+ ++horde;
+
+ data << uint32(BG_AB_OP_OCCUPIED_BASES_ALLY) << uint32(ally);
+ data << uint32(BG_AB_OP_OCCUPIED_BASES_HORDE) << uint32(horde);
+
+ // Team scores
+ data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE);
+ data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_SCORE);
+ data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]);
+ data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]);
+
+ // other unknown
+ data << uint32(0x745) << uint32(0x2); // 37 1861 unk
+}
+
+void BattleGroundAB::_SendNodeUpdate(uint8 node)
+{
+ // Send node owner state update to refresh map icons on client
+ const uint8 plusArray[] = {0, 2, 3, 0, 1};
+
+ if( m_prevNodes[node] )
+ UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_prevNodes[node]], 0);
+ else
+ UpdateWorldState(BG_AB_OP_NODEICONS[node], 0);
+
+ UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_Nodes[node]], 1);
+
+ // How many bases each team owns
+ uint8 ally = 0, horde = 0;
+ for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ if( m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED )
+ ++ally;
+ else if( m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED )
+ ++horde;
+
+ UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_ALLY, ally);
+ UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_HORDE, horde);
+}
+
+void BattleGroundAB::_NodeOccupied(uint8 node,Team team)
+{
+ if( !AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team) )
+ sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team);
+
+ uint8 capturedNodes = 0;
+ for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ {
+ if( m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i])
+ ++capturedNodes;
+ }
+ if(capturedNodes >= 5)
+ CastSpellOnTeam(SPELL_AB_QUEST_REWARD_5_BASES, team);
+ if(capturedNodes >= 4)
+ CastSpellOnTeam(SPELL_AB_QUEST_REWARD_4_BASES, team);
+}
+
+void BattleGroundAB::_NodeDeOccupied(uint8 node)
+{
+ if( node >= BG_AB_DYNAMIC_NODES_COUNT)
+ return;
+
+ // Those who are waiting to resurrect at this node are taken to the closest own node's graveyard
+ std::vector<uint64> ghost_list = m_ReviveQueue[m_BgCreatures[node]];
+ if( !ghost_list.empty() )
+ {
+ WorldSafeLocsEntry const *ClosestGrave = NULL;
+ Player *plr;
+ for (std::vector<uint64>::iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr)
+ {
+ plr = objmgr.GetPlayer(*ghost_list.begin());
+ if( !plr )
+ continue;
+ if( !ClosestGrave )
+ ClosestGrave = GetClosestGraveYard(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetTeam());
+
+ plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation());
+ }
+ }
+
+ if( m_BgCreatures[node] )
+ DelCreature(node);
+
+ // buff object isn't despawned
+}
+
+/* Invoked if a player used a banner as a gameobject */
+void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/)
+{
+ if( GetStatus() != STATUS_IN_PROGRESS )
+ return;
+
+ uint8 node = BG_AB_NODE_STABLES;
+ GameObject* obj=HashMapHolder<GameObject>::Find(m_BgObjects[node*8+7]);
+ while ( (node < BG_AB_DYNAMIC_NODES_COUNT) && ((!obj) || (!source->IsWithinDistInMap(obj,10))))
+ {
+ ++node;
+ obj=HashMapHolder<GameObject>::Find(m_BgObjects[node*8+BG_AB_OBJECT_AURA_CONTESTED]);
+ }
+
+ if( node == BG_AB_DYNAMIC_NODES_COUNT)
+ {
+ // this means our player isn't close to any of banners - maybe cheater ??
+ return;
+ }
+
+ uint8 teamIndex = GetTeamIndexByTeamId(source->GetTeam());
+
+ // Message to chatlog
+ char buf[256];
+ uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE;
+
+ // Check if player really could use this banner, not cheated
+ if( !(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2) )
+ return;
+
+ source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ uint32 sound = 0;
+ // If node is neutral, change to contested
+ if( m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL )
+ {
+ UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1);
+ m_prevNodes[node] = m_Nodes[node];
+ m_Nodes[node] = teamIndex + 1;
+ // burn current neutral banner
+ _DelBanner(node, BG_AB_NODE_TYPE_NEUTRAL, 0);
+ // create new contested banner
+ _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true);
+ _SendNodeUpdate(node);
+ m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_CLAIMED), _GetNodeName(node), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE));
+ sound = SOUND_NODE_CLAIMED;
+ }
+ // If node is contested
+ else if( (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED) )
+ {
+ // If last state is NOT occupied, change node to enemy-contested
+ if( m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED )
+ {
+ UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1);
+ m_prevNodes[node] = m_Nodes[node];
+ m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED;
+ // burn current contested banner
+ _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex);
+ // create new contested banner
+ _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true);
+ _SendNodeUpdate(node);
+ m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node));
+ }
+ // If contested, change back to occupied
+ else
+ {
+ UpdatePlayerScore(source, SCORE_BASES_DEFENDED, 1);
+ m_prevNodes[node] = m_Nodes[node];
+ m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_OCCUPIED;
+ // burn current contested banner
+ _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex);
+ // create new occupied banner
+ _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true);
+ _SendNodeUpdate(node);
+ m_NodeTimers[node] = 0;
+ _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE);
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_DEFENDED), _GetNodeName(node));
+ }
+ sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE;
+ }
+ // If node is occupied, change to enemy-contested
+ else
+ {
+ UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1);
+ m_prevNodes[node] = m_Nodes[node];
+ m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED;
+ // burn current occupied banner
+ _DelBanner(node, BG_AB_NODE_TYPE_OCCUPIED, !teamIndex);
+ // create new contested banner
+ _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true);
+ _SendNodeUpdate(node);
+ _NodeDeOccupied(node);
+ m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node));
+ sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE;
+ }
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, source->GetSession(), type, LANG_UNIVERSAL, NULL, source->GetGUID(), buf, NULL);
+ SendPacketToAll(&data);
+ // If node is occupied again, send "X has taken the Y" msg.
+ if( m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED )
+ {
+ sprintf(buf, GetMangosString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE), _GetNodeName(node));
+ ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL);
+ SendPacketToAll(&data);
+ }
+ PlaySoundToAll(sound);
+}
+
+bool BattleGroundAB::SetupBattleGround()
+{
+ for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ {
+ if( !AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_BANNER_CONT_A + 8*i,BG_AB_OBJECTID_BANNER_CONT_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_BANNER_CONT_H + 8*i,BG_AB_OBJECTID_BANNER_CONT_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_BANNER_ALLY + 8*i,BG_AB_OBJECTID_BANNER_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_BANNER_HORDE + 8*i,BG_AB_OBJECTID_BANNER_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_AURA_ALLY + 8*i,BG_AB_OBJECTID_AURA_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_AURA_HORDE + 8*i,BG_AB_OBJECTID_AURA_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_AURA_CONTESTED + 8*i,BG_AB_OBJECTID_AURA_C,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY)
+ )
+ {
+ sLog.outErrorDb("BatteGroundAB: Failed to spawn some object BattleGround not created!");
+ return false;
+ }
+ }
+ if( !AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_AB_OBJECT_GATE_H,BG_AB_OBJECTID_GATE_H,BG_AB_DoorPositions[1][0],BG_AB_DoorPositions[1][1],BG_AB_DoorPositions[1][2],BG_AB_DoorPositions[1][3],BG_AB_DoorPositions[1][4],BG_AB_DoorPositions[1][5],BG_AB_DoorPositions[1][6],BG_AB_DoorPositions[1][7],RESPAWN_IMMEDIATELY)
+ )
+ {
+ sLog.outErrorDb("BatteGroundAB: Failed to spawn door object BattleGround not created!");
+ return false;
+ }
+ //buffs
+ for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ {
+ if( !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 1, Buff_Entries[1], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY)
+ || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 2, Buff_Entries[2], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY)
+ )
+ sLog.outErrorDb("BatteGroundAB: Failed to spawn buff object!");
+ }
+
+ return true;
+}
+
+void BattleGroundAB::ResetBGSubclass()
+{
+ m_TeamScores[BG_TEAM_ALLIANCE] = 0;
+ m_TeamScores[BG_TEAM_HORDE] = 0;
+ m_lastTick[BG_TEAM_ALLIANCE] = 0;
+ m_lastTick[BG_TEAM_HORDE] = 0;
+ m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0;
+ m_HonorScoreTics[BG_TEAM_HORDE] = 0;
+ m_ReputationScoreTics[BG_TEAM_ALLIANCE] = 0;
+ m_ReputationScoreTics[BG_TEAM_HORDE] = 0;
+ m_IsInformedNearVictory = false;
+ for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ {
+ m_Nodes[i] = 0;
+ m_prevNodes[i] = 0;
+ m_NodeTimers[i] = 0;
+ m_BannerTimers[i].timer = 0;
+ }
+
+ for (uint8 i = 0; i < BG_AB_ALL_NODES_COUNT; ++i)
+ if(m_BgCreatures[i])
+ DelCreature(i);
+}
+
+WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, float /*z*/, uint32 team)
+{
+ uint8 teamIndex = GetTeamIndexByTeamId(team);
+
+ // Is there any occupied node for this team?
+ std::vector<uint8> nodes;
+ for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
+ if( m_Nodes[i] == teamIndex + 3 )
+ nodes.push_back(i);
+
+ WorldSafeLocsEntry const* good_entry = NULL;
+ // If so, select the closest node to place ghost on
+ if( !nodes.empty() )
+ {
+ float mindist = 999999.0f;
+ for (uint8 i = 0; i < nodes.size(); ++i)
+ {
+ WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[nodes[i]] );
+ if( !entry )
+ continue;
+ float dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y);
+ if( mindist > dist )
+ {
+ mindist = dist;
+ good_entry = entry;
+ }
+ }
+ nodes.clear();
+ }
+ // If not, place ghost on starting location
+ if( !good_entry )
+ good_entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[teamIndex+5] );
+
+ return good_entry;
+}
+
+void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
+{
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if( itr == m_PlayerScores.end() ) // player not found...
+ return;
+
+ switch(type)
+ {
+ case SCORE_BASES_ASSAULTED:
+ ((BattleGroundABScore*)itr->second)->BasesAssaulted += value;
+ break;
+ case SCORE_BASES_DEFENDED:
+ ((BattleGroundABScore*)itr->second)->BasesDefended += value;
+ break;
+ default:
+ BattleGround::UpdatePlayerScore(Source,type,value);
+ break;
+ }
+}
diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h
new file mode 100644
index 00000000000..289d26f98fd
--- /dev/null
+++ b/src/game/BattleGroundAB.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef __BATTLEGROUNDAB_H
+#define __BATTLEGROUNDAB_H
+
+class BattleGround;
+
+enum BG_AB_WorldStates
+{
+ BG_AB_OP_OCCUPIED_BASES_HORDE = 1778,
+ BG_AB_OP_OCCUPIED_BASES_ALLY = 1779,
+ BG_AB_OP_RESOURCES_ALLY = 1776,
+ BG_AB_OP_RESOURCES_HORDE = 1777,
+ BG_AB_OP_RESOURCES_MAX = 1780,
+ BG_AB_OP_RESOURCES_WARNING = 1955
+/*
+ BG_AB_OP_STABLE_ICON = 1842, //Stable map icon (NONE)
+ BG_AB_OP_STABLE_STATE_ALIENCE = 1767, //Stable map state (ALIENCE)
+ BG_AB_OP_STABLE_STATE_HORDE = 1768, //Stable map state (HORDE)
+ BG_AB_OP_STABLE_STATE_CON_ALI = 1769, //Stable map state (CON ALIENCE)
+ BG_AB_OP_STABLE_STATE_CON_HOR = 1770, //Stable map state (CON HORDE)
+ BG_AB_OP_FARM_ICON = 1845, //Farm map icon (NONE)
+ BG_AB_OP_FARM_STATE_ALIENCE = 1772, //Farm state (ALIENCE)
+ BG_AB_OP_FARM_STATE_HORDE = 1773, //Farm state (HORDE)
+ BG_AB_OP_FARM_STATE_CON_ALI = 1774, //Farm state (CON ALIENCE)
+ BG_AB_OP_FARM_STATE_CON_HOR = 1775, //Farm state (CON HORDE)
+
+ BG_AB_OP_BLACKSMITH_ICON = 1846, //Blacksmith map icon (NONE)
+ BG_AB_OP_BLACKSMITH_STATE_ALIENCE = 1782, //Blacksmith map state (ALIENCE)
+ BG_AB_OP_BLACKSMITH_STATE_HORDE = 1783, //Blacksmith map state (HORDE)
+ BG_AB_OP_BLACKSMITH_STATE_CON_ALI = 1784, //Blacksmith map state (CON ALIENCE)
+ BG_AB_OP_BLACKSMITH_STATE_CON_HOR = 1785, //Blacksmith map state (CON HORDE)
+ BG_AB_OP_LUMBERMILL_ICON = 1844, //Lumber Mill map icon (NONE)
+ BG_AB_OP_LUMBERMILL_STATE_ALIENCE = 1792, //Lumber Mill map state (ALIENCE)
+ BG_AB_OP_LUMBERMILL_STATE_HORDE = 1793, //Lumber Mill map state (HORDE)
+ BG_AB_OP_LUMBERMILL_STATE_CON_ALI = 1794, //Lumber Mill map state (CON ALIENCE)
+ BG_AB_OP_LUMBERMILL_STATE_CON_HOR = 1795, //Lumber Mill map state (CON HORDE)
+ BG_AB_OP_GOLDMINE_ICON = 1843, //Gold Mine map icon (NONE)
+ BG_AB_OP_GOLDMINE_STATE_ALIENCE = 1787, //Gold Mine map state (ALIENCE)
+ BG_AB_OP_GOLDMINE_STATE_HORDE = 1788, //Gold Mine map state (HORDE)
+ BG_AB_OP_GOLDMINE_STATE_CON_ALI = 1789, //Gold Mine map state (CON ALIENCE
+ BG_AB_OP_GOLDMINE_STATE_CON_HOR = 1790, //Gold Mine map state (CON HORDE)
+*/
+};
+
+const uint32 BG_AB_OP_NODESTATES[5] = {1767, 1782, 1772, 1792, 1787};
+
+const uint32 BG_AB_OP_NODEICONS[5] = {1842, 1846, 1845, 1844, 1843};
+
+/* Note: code uses that these IDs follow each other */
+enum BG_AB_NodeObjectId
+{
+ BG_AB_OBJECTID_NODE_BANNER_0 = 180087, // Stables banner
+ BG_AB_OBJECTID_NODE_BANNER_1 = 180088, // Blacksmith banner
+ BG_AB_OBJECTID_NODE_BANNER_2 = 180089, // Farm banner
+ BG_AB_OBJECTID_NODE_BANNER_3 = 180090, // Lumber mill banner
+ BG_AB_OBJECTID_NODE_BANNER_4 = 180091 // Gold mine banner
+};
+
+enum BG_AB_ObjectType
+{
+ // for all 5 node points 8*5=40 objects
+ BG_AB_OBJECT_BANNER_NEUTRAL = 0,
+ BG_AB_OBJECT_BANNER_CONT_A = 1,
+ BG_AB_OBJECT_BANNER_CONT_H = 2,
+ BG_AB_OBJECT_BANNER_ALLY = 3,
+ BG_AB_OBJECT_BANNER_HORDE = 4,
+ BG_AB_OBJECT_AURA_ALLY = 5,
+ BG_AB_OBJECT_AURA_HORDE = 6,
+ BG_AB_OBJECT_AURA_CONTESTED = 7,
+ //gates
+ BG_AB_OBJECT_GATE_A = 40,
+ BG_AB_OBJECT_GATE_H = 41,
+ //buffs
+ BG_AB_OBJECT_SPEEDBUFF_STABLES = 42,
+ BG_AB_OBJECT_REGENBUFF_STABLES = 43,
+ BG_AB_OBJECT_BERSERKBUFF_STABLES = 44,
+ BG_AB_OBJECT_SPEEDBUFF_BLACKSMITH = 45,
+ BG_AB_OBJECT_REGENBUFF_BLACKSMITH = 46,
+ BG_AB_OBJECT_BERSERKBUFF_BLACKSMITH = 47,
+ BG_AB_OBJECT_SPEEDBUFF_FARM = 48,
+ BG_AB_OBJECT_REGENBUFF_FARM = 49,
+ BG_AB_OBJECT_BERSERKBUFF_FARM = 50,
+ BG_AB_OBJECT_SPEEDBUFF_LUMBER_MILL = 51,
+ BG_AB_OBJECT_REGENBUFF_LUMBER_MILL = 52,
+ BG_AB_OBJECT_BERSERKBUFF_LUMBER_MILL = 53,
+ BG_AB_OBJECT_SPEEDBUFF_GOLD_MINE = 54,
+ BG_AB_OBJECT_REGENBUFF_GOLD_MINE = 55,
+ BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE = 56,
+ BG_AB_OBJECT_MAX = 57,
+};
+
+/* Object id templates from DB */
+enum BG_AB_ObjectTypes
+{
+ BG_AB_OBJECTID_BANNER_A = 180058,
+ BG_AB_OBJECTID_BANNER_CONT_A = 180059,
+ BG_AB_OBJECTID_BANNER_H = 180060,
+ BG_AB_OBJECTID_BANNER_CONT_H = 180061,
+
+ BG_AB_OBJECTID_AURA_A = 180100,
+ BG_AB_OBJECTID_AURA_H = 180101,
+ BG_AB_OBJECTID_AURA_C = 180102,
+
+ BG_AB_OBJECTID_GATE_A = 180255,
+ BG_AB_OBJECTID_GATE_H = 180256
+};
+
+enum BG_AB_Timers
+{
+ BG_AB_FLAG_CAPTURING_TIME = 60000,
+};
+
+enum BG_AB_Score
+{
+ BG_AB_MAX_TEAM_SCORE = 2000,
+ BG_AB_WARNING_SCORE = 1800
+};
+
+/* do NOT change the order, else wrong behaviour */
+enum BG_AB_BattleGroundNodes
+{
+ BG_AB_NODE_STABLES = 0,
+ BG_AB_NODE_BLACKSMITH = 1,
+ BG_AB_NODE_FARM = 2,
+ BG_AB_NODE_LUMBER_MILL = 3,
+ BG_AB_NODE_GOLD_MINE = 4,
+
+ BG_AB_DYNAMIC_NODES_COUNT = 5, // dynamic nodes that can be captured
+
+ BG_AB_SPIRIT_ALIANCE = 5,
+ BG_AB_SPIRIT_HORDE = 6,
+
+ BG_AB_ALL_NODES_COUNT = 7, // all nodes (dynamic and static)
+};
+
+enum BG_AB_NodeStatus
+{
+ BG_AB_NODE_TYPE_NEUTRAL = 0,
+ BG_AB_NODE_TYPE_CONTESTED = 1,
+ BG_AB_NODE_STATUS_ALLY_CONTESTED = 1,
+ BG_AB_NODE_STATUS_HORDE_CONTESTED = 2,
+ BG_AB_NODE_TYPE_OCCUPIED = 3,
+ BG_AB_NODE_STATUS_ALLY_OCCUPIED = 3,
+ BG_AB_NODE_STATUS_HORDE_OCCUPIED = 4
+};
+
+enum BG_AB_Sounds
+{
+ SOUND_NODE_CLAIMED = 8192,
+ SOUND_NODE_CAPTURED_ALLIANCE = 8173,
+ SOUND_NODE_CAPTURED_HORDE = 8213,
+ SOUND_NODE_ASSAULTED_ALLIANCE = 8174,
+ SOUND_NODE_ASSAULTED_HORDE = 8212,
+ SOUND_NEAR_VICTORY = 8456
+};
+
+// x, y, z, o
+const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = {
+ {1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables
+ {977.0156f, 1046.616f, -44.80923f, -2.600541f}, // blacksmith
+ {806.1821f, 874.2723f, -55.99371f, -2.303835f}, // farm
+ {856.1419f, 1148.902f, 11.18469f, -2.303835f}, // lumber mill
+ {1146.923f, 848.1782f, -110.917f, -0.7330382f} // gold mine
+};
+
+// x, y, z, o, rot0, rot1, rot2, rot3
+const float BG_AB_DoorPositions[2][8] = {
+ {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f},
+ {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f}
+};
+
+// Tick intervals and given points: case 0,1,2,3,4,5 captured nodes
+const uint32 BG_AB_TickIntervals[6] = {0, 12000, 9000, 6000, 3000, 1000};
+const uint32 BG_AB_TickPoints[6] = {0, 10, 10, 10, 10, 30};
+
+// WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location
+const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 896, 898, 899};
+
+// x, y, z, o
+const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = {
+ {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables
+ {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith
+ {817.66f, 843.34f, -56.54f, 3.01f}, // farm
+ {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill
+ {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine
+};
+
+// x, y, z, o
+const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] = {
+ {1200.03f, 1171.09f, -56.47f, 5.15f}, // stables
+ {1017.43f, 960.61f, -42.95f, 4.88f}, // blacksmith
+ {833.00f, 793.00f, -57.25f, 5.27f}, // farm
+ {775.17f, 1206.40f, 15.79f, 1.90f}, // lumber mill
+ {1207.48f, 787.00f, -83.36f, 5.51f}, // gold mine
+ {1354.05f, 1275.48f, -11.30f, 4.77f}, // alliance starting base
+ {714.61f, 646.15f, -10.87f, 4.34f} // horde starting base
+};
+
+struct BG_AB_BannerTimer
+{
+ uint32 timer;
+ uint8 type;
+ uint8 teamIndex;
+};
+
+class BattleGroundABScore : public BattleGroundScore
+{
+ public:
+ BattleGroundABScore(): BasesAssaulted(0), BasesDefended(0) {};
+ virtual ~BattleGroundABScore() {};
+ uint32 BasesAssaulted;
+ uint32 BasesDefended;
+};
+
+class BattleGroundAB : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundAB();
+ ~BattleGroundAB();
+
+ void Update(time_t diff);
+ void AddPlayer(Player *plr);
+ void RemovePlayer(Player *plr,uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ virtual bool SetupBattleGround();
+ virtual void ResetBGSubclass();
+ virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team);
+
+ /* Scorekeeping */
+ virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
+ virtual void FillInitialWorldStates(WorldPacket& data);
+
+ /* Nodes occupying */
+ virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj);
+
+ private:
+ /* Gameobject spawning/despawning */
+ void _CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay);
+ void _DelBanner(uint8 node, uint8 type, uint8 teamIndex);
+ void _SendNodeUpdate(uint8 node);
+
+ /* Creature spawning/despawning */
+ // TODO: working, scripted peons spawning
+ void _NodeOccupied(uint8 node,Team team);
+ void _NodeDeOccupied(uint8 node);
+
+ const char* _GetNodeName(uint8 node);
+
+ /* Nodes info:
+ 0: neutral
+ 1: ally contested
+ 2: horde contested
+ 3: ally occupied
+ 4: horde occupied */
+ uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT];
+ uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT];
+ BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT];
+ int32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT];
+ uint32 m_TeamScores[2];
+ uint32 m_lastTick[2];
+ uint32 m_HonorScoreTics[2];
+ uint32 m_ReputationScoreTics[2];
+ bool m_IsInformedNearVictory;
+};
+#endif
diff --git a/src/game/BattleGroundAV.cpp b/src/game/BattleGroundAV.cpp
new file mode 100644
index 00000000000..78b585be190
--- /dev/null
+++ b/src/game/BattleGroundAV.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundAV.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "Language.h"
+
+BattleGroundAV::BattleGroundAV()
+{
+
+}
+
+BattleGroundAV::~BattleGroundAV()
+{
+
+}
+
+void BattleGroundAV::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+}
+
+void BattleGroundAV::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundAVScore* sc = new BattleGroundAVScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundAV::RemovePlayer(Player* /*plr*/,uint64 /*guid*/)
+{
+
+}
+
+void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ // this is wrong way to implement these things. On official it done by gameobject spell cast.
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ uint32 SpellId = 0;
+ switch(Trigger)
+ {
+ case 95:
+ case 2606:
+ case 2608:
+ case 3326:
+ case 3327:
+ case 3328:
+ case 3329:
+ case 3330:
+ case 3331:
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+
+ if(SpellId)
+ Source->CastSpell(Source, SpellId, true);
+}
+
+void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
+{
+
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if(itr == m_PlayerScores.end()) // player not found...
+ return;
+
+ switch(type)
+ {
+ case SCORE_GRAVEYARDS_ASSAULTED:
+ ((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted += value;
+ break;
+ case SCORE_GRAVEYARDS_DEFENDED:
+ ((BattleGroundAVScore*)itr->second)->GraveyardsDefended += value;
+ break;
+ case SCORE_TOWERS_ASSAULTED:
+ ((BattleGroundAVScore*)itr->second)->TowersAssaulted += value;
+ break;
+ case SCORE_TOWERS_DEFENDED:
+ ((BattleGroundAVScore*)itr->second)->TowersDefended += value;
+ break;
+ case SCORE_MINES_CAPTURED:
+ ((BattleGroundAVScore*)itr->second)->MinesCaptured += value;
+ break;
+ case SCORE_LEADERS_KILLED:
+ ((BattleGroundAVScore*)itr->second)->LeadersKilled += value;
+ break;
+ case SCORE_SECONDARY_OBJECTIVES:
+ ((BattleGroundAVScore*)itr->second)->SecondaryObjectives += value;
+ break;
+ default:
+ BattleGround::UpdatePlayerScore(Source,type,value);
+ break;
+ }
+}
diff --git a/src/game/BattleGroundAV.h b/src/game/BattleGroundAV.h
new file mode 100644
index 00000000000..441c310fda9
--- /dev/null
+++ b/src/game/BattleGroundAV.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __BATTLEGROUNDAV_H
+#define __BATTLEGROUNDAV_H
+
+class BattleGround;
+
+class BattleGroundAVScore : public BattleGroundScore
+{
+ public:
+ BattleGroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), MinesCaptured(0), LeadersKilled(0), SecondaryObjectives(0) {};
+ virtual ~BattleGroundAVScore() {};
+ uint32 GraveyardsAssaulted;
+ uint32 GraveyardsDefended;
+ uint32 TowersAssaulted;
+ uint32 TowersDefended;
+ uint32 MinesCaptured;
+ uint32 LeadersKilled;
+ uint32 SecondaryObjectives;
+};
+
+class BattleGroundAV : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundAV();
+ ~BattleGroundAV();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ void RemovePlayer(Player *plr,uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ //bool SetupBattleGround();
+
+ /* Scorekeeping */
+ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
+ private:
+};
+#endif
diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp
new file mode 100644
index 00000000000..a85286ce6d5
--- /dev/null
+++ b/src/game/BattleGroundBE.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundBE.h"
+#include "Creature.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Language.h"
+
+BattleGroundBE::BattleGroundBE()
+{
+ m_BgObjects.resize(BG_BE_OBJECT_MAX);
+}
+
+BattleGroundBE::~BattleGroundBE()
+{
+
+}
+
+void BattleGroundBE::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+
+ // after bg start we get there
+ if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
+ {
+ ModifyStartDelayTime(diff);
+
+ if (!(m_Events & 0x01))
+ {
+ m_Events |= 0x01;
+ for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; i++)
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+
+ for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++)
+ SpawnBGObject(i, RESPAWN_ONE_DAY);
+
+ SetStartDelayTime(START_DELAY1);
+ SendMessageToAll(LANG_ARENA_ONE_MINUTE);
+ }
+ // After 30 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04))
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(LANG_ARENA_THIRTY_SECONDS);
+ }
+ // After 15 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08))
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS);
+ }
+ // delay expired (1 minute)
+ else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10))
+ {
+ m_Events |= 0x10;
+
+ for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; i++)
+ DoorOpen(i);
+
+ for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++)
+ SpawnBGObject(i, 60);
+
+ SendMessageToAll(LANG_ARENA_BEGUN);
+ SetStatus(STATUS_IN_PROGRESS);
+ SetStartDelayTime(0);
+
+ for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
+ if(Player *plr = objmgr.GetPlayer(itr->first))
+ plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
+ }
+ }
+
+ /*if(GetStatus() == STATUS_IN_PROGRESS)
+ {
+ // update something
+ }*/
+}
+
+void BattleGroundBE::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundBEScore* sc = new BattleGroundBEScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundBE::RemovePlayer(Player *plr, uint64 guid)
+{
+
+}
+
+void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ if(!killer)
+ {
+ sLog.outError("Killer player not found");
+ return;
+ }
+
+ BattleGround::HandleKillPlayer(player, killer);
+
+ uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam());
+
+ ++m_TeamKills[killer_team_index]; // add kills to killer's team
+
+ if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam()))
+ {
+ // all opponents killed
+ EndBattleGround(killer->GetTeam());
+ }
+}
+
+void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ // this is wrong way to implement these things. On official it done by gameobject spell cast.
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ //uint32 SpellId = 0;
+ //uint64 buff_guid = 0;
+ switch(Trigger)
+ {
+ case 4538: // buff trigger?
+ //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1];
+ break;
+ case 4539: // buff trigger?
+ //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2];
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+
+ //if(buff_guid)
+ // HandleTriggerBuff(buff_guid,Source);
+}
+
+void BattleGroundBE::ResetBGSubclass()
+{
+ m_TeamKills[BG_TEAM_ALLIANCE] = 0;
+ m_TeamKills[BG_TEAM_HORDE] = 0;
+}
+
+bool BattleGroundBE::SetupBattleGround()
+{
+ // gates
+ if( !AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_BE_OBJECT_DOOR_2, BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_BE_OBJECT_DOOR_3, BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY)
+ // buffs
+ || !AddObject(BG_BE_OBJECT_BUFF_1, BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120)
+ || !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120))
+ {
+ sLog.outErrorDb("BatteGroundBE: Failed to spawn some object!");
+ return false;
+ }
+
+ return true;
+}
+
+void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
+{
+
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if(itr == m_PlayerScores.end()) // player not found...
+ return;
+
+ //there is nothing special in this score
+ BattleGround::UpdatePlayerScore(Source,type,value);
+
+}
+
+/*
+21:45:46 id:231310 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86
+0000: 32 02 00 00 76 0e 00 00 00 00 00 00 09 00 f3 09 | 2...v...........
+0010: 00 00 01 00 00 00 f1 09 00 00 01 00 00 00 f0 09 | ................
+0020: 00 00 02 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................
+0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................
+0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................
+0050: 00 00 00 00 00 00 | ......
+
+spell 32724 - Gold Team
+spell 32725 - Green Team
+35774 Gold Team
+35775 Green Team
+*/
diff --git a/src/game/BattleGroundBE.h b/src/game/BattleGroundBE.h
new file mode 100644
index 00000000000..1b92faab9fc
--- /dev/null
+++ b/src/game/BattleGroundBE.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef __BATTLEGROUNDBE_H
+#define __BATTLEGROUNDBE_H
+
+class BattleGround;
+
+enum BattleGroundBEObjectTypes
+{
+ BG_BE_OBJECT_DOOR_1 = 0,
+ BG_BE_OBJECT_DOOR_2 = 1,
+ BG_BE_OBJECT_DOOR_3 = 2,
+ BG_BE_OBJECT_DOOR_4 = 3,
+ BG_BE_OBJECT_BUFF_1 = 4,
+ BG_BE_OBJECT_BUFF_2 = 5,
+ BG_BE_OBJECT_MAX = 6
+};
+
+enum BattleGroundBEObjects
+{
+ BG_BE_OBJECT_TYPE_DOOR_1 = 183971,
+ BG_BE_OBJECT_TYPE_DOOR_2 = 183973,
+ BG_BE_OBJECT_TYPE_DOOR_3 = 183970,
+ BG_BE_OBJECT_TYPE_DOOR_4 = 183972,
+ BG_BE_OBJECT_TYPE_BUFF_1 = 184663,
+ BG_BE_OBJECT_TYPE_BUFF_2 = 184664
+};
+
+class BattleGroundBEScore : public BattleGroundScore
+{
+ public:
+ BattleGroundBEScore() {};
+ virtual ~BattleGroundBEScore() {};
+};
+
+class BattleGroundBE : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundBE();
+ ~BattleGroundBE();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ void RemovePlayer(Player *plr, uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ bool SetupBattleGround();
+ void ResetBGSubclass();
+ void HandleKillPlayer(Player* player, Player *killer);
+
+ /* Scorekeeping */
+ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
+ private:
+ uint32 m_TeamKills[2]; // count of kills for each team
+};
+#endif
diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp
new file mode 100644
index 00000000000..dbde3295f4d
--- /dev/null
+++ b/src/game/BattleGroundEY.cpp
@@ -0,0 +1,909 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundEY.h"
+#include "Creature.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Language.h"
+#include "Util.h"
+
+BattleGroundEY::BattleGroundEY()
+{
+ m_BuffChange = true;
+ m_BgObjects.resize(BG_EY_OBJECT_MAX);
+ m_BgCreatures.resize(BG_EY_CREATURES_MAX);
+ m_Points_Trigger[FEL_REALVER] = TR_FEL_REALVER_BUFF;
+ m_Points_Trigger[BLOOD_ELF] = TR_BLOOD_ELF_BUFF;
+ m_Points_Trigger[DRAENEI_RUINS] = TR_DRAENEI_RUINS_BUFF;
+ m_Points_Trigger[MAGE_TOWER] = TR_MAGE_TOWER_BUFF;
+}
+
+BattleGroundEY::~BattleGroundEY()
+{
+}
+
+void BattleGroundEY::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+ // after bg start we get there (once)
+ if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
+ {
+ ModifyStartDelayTime(diff);
+
+ if(!(m_Events & 0x01))
+ {
+ m_Events |= 0x01;
+
+ SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY);
+
+ for(uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i)
+ SpawnBGObject(i, RESPAWN_ONE_DAY);
+
+ SetStartDelayTime(START_DELAY0);
+ }
+ // After 1 minute, warning is signalled
+ else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04))
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(GetMangosString(LANG_BG_EY_ONE_MINUTE));
+ }
+ // After 1,5 minute, warning is signalled
+ else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08))
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(GetMangosString(LANG_BG_EY_HALF_MINUTE));
+ }
+ // After 2 minutes, gates OPEN ! x)
+ else if(GetStartDelayTime() < 0 && !(m_Events & 0x10))
+ {
+ m_Events |= 0x10;
+ SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY);
+ SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY);
+
+ for(uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i)
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+ for(uint32 i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ //randomly spawn buff
+ uint8 buff = urand(0, 2);
+ SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY);
+ }
+
+ SendMessageToAll(GetMangosString(LANG_BG_EY_BEGIN));
+
+ PlaySoundToAll(SOUND_BG_START);
+ SetStatus(STATUS_IN_PROGRESS);
+
+ 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)
+ {
+ m_PointAddingTimer -= diff;
+ if(m_PointAddingTimer <= 0)
+ {
+ m_PointAddingTimer = BG_EY_FPOINTS_TICK_TIME;
+ if (m_TeamPointsCount[BG_TEAM_ALLIANCE] > 0)
+ AddPoints(ALLIANCE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_ALLIANCE] - 1]);
+ if (m_TeamPointsCount[BG_TEAM_HORDE] > 0)
+ AddPoints(HORDE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_HORDE] - 1]);
+ }
+
+ if(m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND)
+ {
+ m_FlagsTimer -= diff;
+
+ if(m_FlagsTimer < 0)
+ {
+ m_FlagsTimer = 0;
+ if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN)
+ RespawnFlag(true);
+ else
+ RespawnFlagAfterDrop();
+ }
+ }
+
+ m_TowerCapCheckTimer -= diff;
+ if(m_TowerCapCheckTimer <= 0)
+ {
+ //check if player joined point
+ /*I used this order of calls, because although we will check if one player is in gameobject's distance 2 times
+ but we can count of players on current point in CheckSomeoneLeftPoint
+ */
+ this->CheckSomeoneJoinedPoint();
+ //check if player left point
+ this->CheckSomeoneLeftPoint();
+ this->UpdatePointStatuses();
+ m_TowerCapCheckTimer = BG_EY_FPOINTS_TICK_TIME;
+ }
+ }
+}
+
+void BattleGroundEY::AddPoints(uint32 Team, uint32 Points)
+{
+ uint8 team_index = GetTeamIndexByTeamId(Team);
+ m_TeamScores[team_index] += Points;
+ m_HonorScoreTics[team_index] += Points;
+ if (m_HonorScoreTics[team_index] >= BG_HONOR_SCORE_TICKS)
+ {
+ RewardHonorToTeam(20, Team);
+ m_HonorScoreTics[team_index] -= BG_HONOR_SCORE_TICKS;
+ }
+ UpdateTeamScore(Team);
+}
+
+void BattleGroundEY::CheckSomeoneJoinedPoint()
+{
+ GameObject *obj = NULL;
+ for (uint8 i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ obj = HashMapHolder<GameObject>::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]);
+ if (obj)
+ {
+ uint8 j = 0;
+ while (j < m_PlayersNearPoint[EY_POINTS_MAX].size())
+ {
+ Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]);
+ if(!plr)
+ {
+ sLog.outError("BattleGroundEY: Player " I64FMTD " not found!", m_PlayersNearPoint[EY_POINTS_MAX][j]);
+ ++j;
+ continue;
+ }
+ if (plr->isAlive() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS))
+ {
+ //player joined point!
+ //show progress bar
+ UpdateWorldStateForPlayer(PROGRESS_BAR_PERCENT_GREY, BG_EY_PROGRESS_BAR_PERCENT_GREY, plr);
+ UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[i], plr);
+ UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_SHOW, plr);
+ //add player to point
+ m_PlayersNearPoint[i].push_back(m_PlayersNearPoint[EY_POINTS_MAX][j]);
+ //remove player from "free space"
+ m_PlayersNearPoint[EY_POINTS_MAX].erase(m_PlayersNearPoint[EY_POINTS_MAX].begin() + j);
+ }
+ else
+ ++j;
+ }
+ }
+ }
+}
+
+void BattleGroundEY::CheckSomeoneLeftPoint()
+{
+ //reset current point counts
+ for (uint8 i = 0; i < 2*EY_POINTS_MAX; ++i)
+ m_CurrentPointPlayersCount[i] = 0;
+ GameObject *obj = NULL;
+ for(uint8 i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ obj = HashMapHolder<GameObject>::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]);
+ if(obj)
+ {
+ uint8 j = 0;
+ while (j < m_PlayersNearPoint[i].size())
+ {
+ Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[i][j]);
+ if (!plr)
+ {
+ sLog.outError("BattleGroundEY: Player " I64FMTD " not found!", m_PlayersNearPoint[i][j]);
+ //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug
+ m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]);
+ m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j);
+ ++j;
+ continue;
+ }
+ if (!plr->isAlive() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS))
+ //move player out of point (add him to players that are out of points
+ {
+ m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]);
+ m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j);
+ this->UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_DONT_SHOW, plr);
+ }
+ else
+ {
+ //player is neat flag, so update count:
+ m_CurrentPointPlayersCount[2 * i + GetTeamIndexByTeamId(plr->GetTeam())]++;
+ ++j;
+ }
+ }
+ }
+ }
+}
+
+void BattleGroundEY::UpdatePointStatuses()
+{
+ for(uint8 point = 0; point < EY_POINTS_MAX; ++point)
+ {
+ if (m_PlayersNearPoint[point].empty())
+ continue;
+ //count new point bar status:
+ m_PointBarStatus[point] += (m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] < BG_EY_POINT_MAX_CAPTURERS_COUNT) ? m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] : BG_EY_POINT_MAX_CAPTURERS_COUNT;
+
+ if (m_PointBarStatus[point] > BG_EY_PROGRESS_BAR_ALI_CONTROLLED)
+ //point is fully alliance's
+ m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_ALI_CONTROLLED;
+ if (m_PointBarStatus[point] < BG_EY_PROGRESS_BAR_HORDE_CONTROLLED)
+ //point is fully horde's
+ m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_HORDE_CONTROLLED;
+
+ uint32 pointOwnerTeamId = 0;
+ //find which team should own this point
+ if (m_PointBarStatus[point] <= BG_EY_PROGRESS_BAR_NEUTRAL_LOW)
+ pointOwnerTeamId = HORDE;
+ else if (m_PointBarStatus[point] >= BG_EY_PROGRESS_BAR_NEUTRAL_HIGH)
+ pointOwnerTeamId = ALLIANCE;
+ else
+ pointOwnerTeamId = EY_POINT_NO_OWNER;
+
+ for (uint8 i = 0; i < m_PlayersNearPoint[point].size(); ++i)
+ {
+ Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[point][i]);
+ if (plr)
+ {
+ this->UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[point], plr);
+ //if point owner changed we must evoke event!
+ if (pointOwnerTeamId != m_PointOwnedByTeam[point])
+ {
+ //point was uncontrolled and player is from team which captured point
+ if (m_PointState[point] == EY_POINT_STATE_UNCONTROLLED && plr->GetTeam() == pointOwnerTeamId)
+ this->EventTeamCapturedPoint(plr, point);
+
+ //point was under control and player isn't from team which controlled it
+ if (m_PointState[point] == EY_POINT_UNDER_CONTROL && plr->GetTeam() != m_PointOwnedByTeam[point])
+ this->EventTeamLostPoint(plr, point);
+ }
+ }
+ }
+ }
+}
+
+void BattleGroundEY::UpdateTeamScore(uint32 Team)
+{
+ uint32 score = GetTeamScore(Team);
+ if(score >= EY_MAX_TEAM_SCORE)
+ {
+ score = EY_MAX_TEAM_SCORE;
+ EndBattleGround(Team);
+ }
+
+ if(Team == ALLIANCE)
+ UpdateWorldState(EY_ALLIANCE_RESOURCES, score);
+ else
+ UpdateWorldState(EY_HORDE_RESOURCES, score);
+}
+
+void BattleGroundEY::UpdatePointsCount(uint32 Team)
+{
+ if(Team == ALLIANCE)
+ UpdateWorldState(EY_ALLIANCE_BASE, m_TeamPointsCount[BG_TEAM_ALLIANCE]);
+ else
+ UpdateWorldState(EY_HORDE_BASE, m_TeamPointsCount[BG_TEAM_HORDE]);
+}
+
+void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point)
+{
+ //we MUST firstly send 0, after that we can send 1!!!
+ if (m_PointState[Point] == EY_POINT_UNDER_CONTROL)
+ {
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 0);
+ if(Team == ALLIANCE)
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 1);
+ else
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 1);
+ }
+ else
+ {
+ if(Team == ALLIANCE)
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 0);
+ else
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 0);
+ UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 1);
+ }
+}
+
+void BattleGroundEY::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map
+ BattleGroundEYScore* sc = new BattleGroundEYScore;
+
+ m_PlayersNearPoint[EY_POINTS_MAX].push_back(plr->GetGUID());
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid)
+{
+ // sometimes flag aura not removed :(
+ for (int j = EY_POINTS_MAX; j >= 0; --j)
+ {
+ for(int i = 0; i < m_PlayersNearPoint[j].size(); ++i)
+ if(m_PlayersNearPoint[j][i] == guid)
+ m_PlayersNearPoint[j].erase(m_PlayersNearPoint[j].begin() + i);
+ }
+ if(IsFlagPickedup())
+ {
+ if(m_FlagKeeper == guid)
+ {
+ if(plr)
+ this->EventPlayerDroppedFlag(plr);
+ else
+ {
+ SetFlagPicker(0);
+ RespawnFlag(true);
+ }
+ }
+ }
+}
+
+void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ if(!Source->isAlive()) //hack code, must be removed later
+ return;
+
+ switch(Trigger)
+ {
+ case TR_BLOOD_ELF_POINT:
+ if(m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam())
+ if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_BLOOD_ELF);
+ break;
+ case TR_FEL_REALVER_POINT:
+ if(m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam())
+ if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_FEL_REALVER);
+ break;
+ case TR_MAGE_TOWER_POINT:
+ if(m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam())
+ if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_MAGE_TOWER);
+ break;
+ case TR_DRAENEI_RUINS_POINT:
+ if(m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam())
+ if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_DRAENEI_RUINS);
+ break;
+ case 4512:
+ case 4515:
+ case 4517:
+ case 4519:
+ case 4530:
+ case 4531:
+ case 4568:
+ case 4569:
+ case 4570:
+ case 4571:
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+}
+
+bool BattleGroundEY::SetupBattleGround()
+{
+ // doors
+ if( !AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY)
+ // banners (alliance)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY)
+ // banners (horde)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY)
+ // banners (natural)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY)
+ // flags
+ || !AddObject(BG_EY_OBJECT_FLAG_NETHERSTORM, BG_OBJECT_FLAG2_EY_ENTRY, 2174.782227f, 1569.054688f, 1160.361938f, -1.448624f, 0, 0, 0.662620f, -0.748956f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_FLAG_FEL_REALVER, BG_OBJECT_FLAG1_EY_ENTRY, 2044.28f, 1729.68f, 1189.96f, -0.017453f, 0, 0, 0.008727f, -0.999962f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_FLAG_BLOOD_ELF, BG_OBJECT_FLAG1_EY_ENTRY, 2048.83f, 1393.65f, 1194.49f, 0.20944f, 0, 0, 0.104528f, 0.994522f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_FLAG_DRAENEI_RUINS, BG_OBJECT_FLAG1_EY_ENTRY, 2286.56f, 1402.36f, 1197.11f, 3.72381f, 0, 0, 0.957926f, -0.287016f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_FLAG_MAGE_TOWER, BG_OBJECT_FLAG1_EY_ENTRY, 2284.48f, 1731.23f, 1189.99f, 2.89725f, 0, 0, 0.992546f, 0.121869f, RESPAWN_ONE_DAY)
+ // tower cap
+ || !AddObject(BG_EY_OBJECT_TOWER_CAP_FEL_REALVER, BG_OBJECT_FR_TOWER_CAP_EY_ENTRY, 2024.600708f, 1742.819580f, 1195.157715f, 2.443461f, 0, 0, 0.939693f, 0.342020f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF, BG_OBJECT_BE_TOWER_CAP_EY_ENTRY, 2050.493164f, 1372.235962f, 1194.563477f, 1.710423f, 0, 0, 0.754710f, 0.656059f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS, BG_OBJECT_DR_TOWER_CAP_EY_ENTRY, 2301.010498f, 1386.931641f, 1197.183472f, 1.570796f, 0, 0, 0.707107f, 0.707107f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY)
+ )
+ {
+ sLog.outErrorDb("BatteGroundEY: Failed to spawn some object BattleGround not created!");
+ return false;
+ }
+
+ //buffs
+ for (int i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(m_Points_Trigger[i]);
+ if( !at )
+ {
+ sLog.outError("BattleGroundEY: Unknown trigger: %u", m_Points_Trigger[i]);
+ continue;
+ }
+ if ( !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY)
+ || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY)
+ )
+ sLog.outError("BattleGroundEY: Cannot spawn buff");
+ }
+
+ WorldSafeLocsEntry const *sg = NULL;
+ sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE);
+ if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE) )
+ {
+ sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!");
+ return false;
+ }
+
+ sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE);
+ if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE) )
+ {
+ sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!");
+ return false;
+ }
+
+ return true;
+}
+
+void BattleGroundEY::ResetBGSubclass()
+{
+ m_TeamScores[BG_TEAM_ALLIANCE] = 0;
+ m_TeamScores[BG_TEAM_HORDE] = 0;
+ m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0;
+ m_TeamPointsCount[BG_TEAM_HORDE] = 0;
+ m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0;
+ m_HonorScoreTics[BG_TEAM_HORDE] = 0;
+ m_FlagState = BG_EY_FLAG_STATE_ON_BASE;
+ m_FlagCapturedBgObjectType = 0;
+ m_FlagKeeper = 0;
+ m_DroppedFlagGUID = 0;
+ m_PointAddingTimer = 0;
+ m_TowerCapCheckTimer = 0;
+
+ for(uint8 i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ m_PointOwnedByTeam[i] = EY_POINT_NO_OWNER;
+ m_PointState[i] = EY_POINT_STATE_UNCONTROLLED;
+ m_PointBarStatus[i] = BG_EY_PROGRESS_BAR_STATE_MIDDLE;
+ m_PlayersNearPoint[i].clear();
+ m_PlayersNearPoint[i].reserve(15); //tip size
+ }
+ m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].clear();
+ m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].reserve(30);
+}
+
+void BattleGroundEY::RespawnFlag(bool send_message)
+{
+ if (m_FlagCapturedBgObjectType > 0)
+ SpawnBGObject(m_FlagCapturedBgObjectType, RESPAWN_ONE_DAY);
+
+ m_FlagCapturedBgObjectType = 0;
+ m_FlagState = BG_EY_FLAG_STATE_ON_BASE;
+ SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_IMMEDIATELY);
+
+ if(send_message)
+ {
+ SendMessageToAll(GetMangosString(LANG_BG_EY_RESETED_FLAG));
+ PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound...
+ }
+
+ UpdateWorldState(NETHERSTORM_FLAG, 1);
+}
+
+void BattleGroundEY::RespawnFlagAfterDrop()
+{
+ RespawnFlag(true);
+
+ GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID());
+ if(obj)
+ obj->Delete();
+ else
+ sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GetDroppedFlagGUID());
+
+ SetDroppedFlagGUID(0);
+}
+
+void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ BattleGround::HandleKillPlayer(player, killer);
+ EventPlayerDroppedFlag(player);
+}
+
+void BattleGroundEY::EventPlayerDroppedFlag(Player *Source)
+{
+ // Drop allowed in any BG state
+
+ if(!IsFlagPickedup())
+ return;
+
+ if(GetFlagPickerGUID() != Source->GetGUID())
+ return;
+
+ const char *message = "";
+ uint8 type = 0;
+
+ SetFlagPicker(0);
+ Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL);
+ m_FlagState = BG_EY_FLAG_STATE_ON_GROUND;
+ m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME;
+ Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true);
+ Source->CastSpell(Source, BG_EY_PLAYER_DROPPED_FLAG_SPELL, true);
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ message = GetMangosString(LANG_BG_EY_DROPPED_FLAG);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ }
+ else
+ {
+ message = GetMangosString(LANG_BG_EY_DROPPED_FLAG);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ }
+ //this does not work correctly :( (it should remove flag carrier name)
+ UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN);
+ UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN);
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+}
+
+void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS || this->IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10))
+ return;
+
+ const char *message;
+ uint8 type = 0;
+ message = GetMangosString(LANG_BG_EY_HAS_TAKEN_FLAG);
+
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE);
+ }
+ else
+ {
+ UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE);
+ }
+
+ if (m_FlagState == BG_EY_FLAG_STATE_ON_BASE)
+ UpdateWorldState(NETHERSTORM_FLAG, 0);
+ m_FlagState = BG_EY_FLAG_STATE_ON_PLAYER;
+
+ SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_ONE_DAY);
+ SetFlagPicker(Source->GetGUID());
+ //get flag aura on player
+ Source->CastSpell(Source, BG_EY_NETHERSTORM_FLAG_SPELL, true);
+ Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+}
+
+void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ //Natural point
+ uint8 message_type = 0;
+ const char *message = "";
+ uint32 Team = m_PointOwnedByTeam[Point];
+
+ if(!Team)
+ return;
+
+ if (Team == ALLIANCE)
+ {
+ m_TeamPointsCount[BG_TEAM_ALLIANCE]--;
+ message_type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ message = GetMangosString(m_LoosingPointTypes[Point].MessageIdAlliance);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 1, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 2, RESPAWN_ONE_DAY);
+ }
+ else
+ {
+ m_TeamPointsCount[BG_TEAM_HORDE]--;
+ message_type = CHAT_MSG_BG_SYSTEM_HORDE;
+ message = GetMangosString(m_LoosingPointTypes[Point].MessageIdHorde);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 1, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 2, RESPAWN_ONE_DAY);
+ }
+
+ SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 1, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 2, RESPAWN_IMMEDIATELY);
+
+ //buff isn't despawned
+
+ m_PointOwnedByTeam[Point] = EY_POINT_NO_OWNER;
+ m_PointState[Point] = EY_POINT_NO_OWNER;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), message_type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+
+ UpdatePointsIcons(Team, Point);
+ UpdatePointsCount(Team);
+}
+
+void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ uint8 type = 0;
+ const char *message = "";
+ uint32 Team = Source->GetTeam();
+
+ SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 1, RESPAWN_ONE_DAY);
+ SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 2, RESPAWN_ONE_DAY);
+
+ if (Team == ALLIANCE)
+ {
+ m_TeamPointsCount[BG_TEAM_ALLIANCE]++;
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ message = GetMangosString(m_CapturingPointTypes[Point].MessageIdAlliance);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 1, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 2, RESPAWN_IMMEDIATELY);
+ }
+ else
+ {
+ m_TeamPointsCount[BG_TEAM_HORDE]++;
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ message = GetMangosString(m_CapturingPointTypes[Point].MessageIdHorde);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 1, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 2, RESPAWN_IMMEDIATELY);
+ }
+
+ //buff isn't respawned
+
+ m_PointOwnedByTeam[Point] = Team;
+ m_PointState[Point] = EY_POINT_UNDER_CONTROL;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+
+ if(m_BgCreatures[Point])
+ DelCreature(Point);
+
+ WorldSafeLocsEntry const *sg = NULL;
+ sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId);
+ if(!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team))
+ sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveYardId);
+
+ UpdatePointsIcons(Team, Point);
+ UpdatePointsCount(Team);
+}
+
+void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS || this->GetFlagPickerGUID() != Source->GetGUID())
+ return;
+
+ uint8 type = 0;
+ uint8 team_id = 0;
+ const char *message = "";
+
+ SetFlagPicker(0);
+ m_FlagState = BG_EY_FLAG_STATE_WAIT_RESPAWN;
+ Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL);
+
+ Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE);
+ team_id = BG_TEAM_ALLIANCE;
+ message = GetMangosString(LANG_BG_EY_CAPTURED_FLAG_A);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ }
+ else
+ {
+ PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE);
+ team_id = BG_TEAM_HORDE;
+ message = GetMangosString(LANG_BG_EY_CAPTURED_FLAG_H);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ }
+
+ SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY);
+
+ m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME;
+ m_FlagCapturedBgObjectType = BgObjectType;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+
+ if(m_TeamPointsCount[team_id] > 0)
+ AddPoints(Source->GetTeam(), BG_EY_FlagPoints[m_TeamPointsCount[team_id] - 1]);
+
+ UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1);
+}
+
+void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
+{
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if(itr == m_PlayerScores.end()) // player not found
+ return;
+
+ switch(type)
+ {
+ case SCORE_FLAG_CAPTURES: // flags captured
+ ((BattleGroundEYScore*)itr->second)->FlagCaptures += value;
+ break;
+ default:
+ BattleGround::UpdatePlayerScore(Source, type, value);
+ break;
+ }
+}
+
+void BattleGroundEY::FillInitialWorldStates(WorldPacket& data)
+{
+ data << uint32(EY_HORDE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_HORDE]);
+ data << uint32(EY_ALLIANCE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_ALLIANCE]);
+ data << uint32(0xab6) << uint32(0x0);
+ data << uint32(0xab5) << uint32(0x0);
+ data << uint32(0xab4) << uint32(0x0);
+ data << uint32(0xab3) << uint32(0x0);
+ data << uint32(0xab2) << uint32(0x0);
+ data << uint32(0xab1) << uint32(0x0);
+ data << uint32(0xab0) << uint32(0x0);
+ data << uint32(0xaaf) << uint32(0x0);
+
+ data << uint32(DRAENEI_RUINS_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == HORDE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(DRAENEI_RUINS_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == ALLIANCE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(DRAENEI_RUINS_UNCONTROL) << uint32(m_PointState[DRAENEI_RUINS] != EY_POINT_UNDER_CONTROL);
+
+ data << uint32(MAGE_TOWER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == ALLIANCE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(MAGE_TOWER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == HORDE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(MAGE_TOWER_UNCONTROL) << uint32(m_PointState[MAGE_TOWER] != EY_POINT_UNDER_CONTROL);
+
+ data << uint32(FEL_REAVER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == HORDE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(FEL_REAVER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == ALLIANCE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(FEL_REAVER_UNCONTROL) << uint32(m_PointState[FEL_REALVER] != EY_POINT_UNDER_CONTROL);
+
+ data << uint32(BLOOD_ELF_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == HORDE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(BLOOD_ELF_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == ALLIANCE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL);
+
+ data << uint32(BLOOD_ELF_UNCONTROL) << uint32(m_PointState[BLOOD_ELF] != EY_POINT_UNDER_CONTROL);
+
+ data << uint32(NETHERSTORM_FLAG) << uint32(m_FlagState == BG_EY_FLAG_STATE_ON_BASE);
+
+ data << uint32(0xad2) << uint32(0x1);
+ data << uint32(0xad1) << uint32(0x1);
+ data << uint32(0xabe) << uint32(GetTeamScore(HORDE));
+ data << uint32(0xabd) << uint32(GetTeamScore(ALLIANCE));
+ data << uint32(0xa05) << uint32(0x8e);
+ data << uint32(0xaa0) << uint32(0x0);
+ data << uint32(0xa9f) << uint32(0x0);
+ data << uint32(0xa9e) << uint32(0x0);
+ data << uint32(0xc0d) << uint32(0x17b);
+}
+
+WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, float z, uint32 team)
+{
+ uint32 g_id = 0;
+
+ if(team == ALLIANCE)
+ g_id = EY_GRAVEYARD_MAIN_ALLIANCE;
+ else if(team == HORDE)
+ g_id = EY_GRAVEYARD_MAIN_HORDE;
+ else
+ return NULL;
+
+ float distance, nearestDistance;
+
+ WorldSafeLocsEntry const* entry = NULL;
+ WorldSafeLocsEntry const* nearestEntry = NULL;
+ entry = sWorldSafeLocsStore.LookupEntry(g_id);
+ nearestEntry = entry;
+
+ if(!entry)
+ {
+ sLog.outError("BattleGroundEY: Not found the main team graveyard. Graveyard system isn't working!");
+ return NULL;
+ }
+
+ distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z);
+ nearestDistance = distance;
+
+ for(uint8 i = 0; i < EY_POINTS_MAX; ++i)
+ {
+ if(m_PointOwnedByTeam[i]==team && m_PointState[i]==EY_POINT_UNDER_CONTROL)
+ {
+ entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId);
+ if(!entry)
+ sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId);
+ else
+ {
+ distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z);
+ if(distance < nearestDistance)
+ {
+ nearestDistance = distance;
+ nearestEntry = entry;
+ }
+ }
+ }
+ }
+
+ return nearestEntry;
+}
diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h
new file mode 100644
index 00000000000..661f107682d
--- /dev/null
+++ b/src/game/BattleGroundEY.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __BATTLEGROUNDEY_H
+#define __BATTLEGROUNDEY_H
+
+#include "Language.h"
+
+class BattleGround;
+
+#define EY_MAX_TEAM_SCORE 2000
+#define BG_EY_FLAG_RESPAWN_TIME 10000 //10 seconds
+#define BG_EY_FPOINTS_TICK_TIME 2000 //2 seconds
+
+enum BG_EY_WorldStates
+{
+ EY_ALLIANCE_RESOURCES = 2749,
+ EY_HORDE_RESOURCES = 2750,
+ EY_ALLIANCE_BASE = 2752,
+ EY_HORDE_BASE = 2753,
+ DRAENEI_RUINS_HORDE_CONTROL = 2733,
+ DRAENEI_RUINS_ALLIANCE_CONTROL = 2732,
+ DRAENEI_RUINS_UNCONTROL = 2731,
+ MAGE_TOWER_ALLIANCE_CONTROL = 2730,
+ MAGE_TOWER_HORDE_CONTROL = 2729,
+ MAGE_TOWER_UNCONTROL = 2728,
+ FEL_REAVER_HORDE_CONTROL = 2727,
+ FEL_REAVER_ALLIANCE_CONTROL = 2726,
+ FEL_REAVER_UNCONTROL = 2725,
+ BLOOD_ELF_HORDE_CONTROL = 2724,
+ BLOOD_ELF_ALLIANCE_CONTROL = 2723,
+ BLOOD_ELF_UNCONTROL = 2722,
+ PROGRESS_BAR_PERCENT_GREY = 2720, //100 = empty (only grey), 0 = blue|red (no grey)
+ PROGRESS_BAR_STATUS = 2719, //50 init!, 48 ... hordak bere .. 33 .. 0 = full 100% hordacky , 100 = full alliance
+ PROGRESS_BAR_SHOW = 2718, //1 init, 0 druhy send - bez messagu, 1 = controlled aliance
+ NETHERSTORM_FLAG = 2757,
+ //set to 2 when flag is picked up, and to 1 if it is dropped
+ NETHERSTORM_FLAG_STATE_ALLIANCE = 2769,
+ NETHERSTORM_FLAG_STATE_HORDE = 2770
+};
+
+enum BG_EY_ProgressBarConsts
+{
+ BG_EY_POINT_MAX_CAPTURERS_COUNT = 5,
+ BG_EY_POINT_RADIUS = 70,
+ BG_EY_PROGRESS_BAR_DONT_SHOW = 0,
+ BG_EY_PROGRESS_BAR_SHOW = 1,
+ BG_EY_PROGRESS_BAR_PERCENT_GREY = 40,
+ BG_EY_PROGRESS_BAR_STATE_MIDDLE = 50,
+ BG_EY_PROGRESS_BAR_HORDE_CONTROLLED = 0,
+ BG_EY_PROGRESS_BAR_NEUTRAL_LOW = 30,
+ BG_EY_PROGRESS_BAR_NEUTRAL_HIGH = 70,
+ BG_EY_PROGRESS_BAR_ALI_CONTROLLED = 100
+};
+
+enum BG_EY_Sounds
+{
+ //strange ids, but sure about them
+ BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212,
+ BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213,
+ BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174,
+ BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173,
+ BG_EY_SOUND_FLAG_RESET = 8192
+};
+
+enum BG_EY_Spells
+{
+ BG_EY_NETHERSTORM_FLAG_SPELL = 34976,
+ BG_EY_PLAYER_DROPPED_FLAG_SPELL = 34991
+};
+
+enum EYBattleGroundObjectEntry
+{
+ BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door
+ BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door
+ BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic)
+ BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand)
+ BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop)
+ BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance)
+ BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde)
+ BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral)
+ BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt
+ BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt
+ BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt
+ BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt
+};
+
+enum EYBattleGroundPointsTrigger
+{
+ TR_BLOOD_ELF_POINT = 4476,
+ TR_FEL_REALVER_POINT = 4514,
+ TR_MAGE_TOWER_POINT = 4516,
+ TR_DRAENEI_RUINS_POINT = 4518,
+ TR_BLOOD_ELF_BUFF = 4568,
+ TR_FEL_REALVER_BUFF = 4569,
+ TR_MAGE_TOWER_BUFF = 4570,
+ TR_DRAENEI_RUINS_BUFF = 4571
+};
+
+enum EYBattleGroundGaveyards
+{
+ EY_GRAVEYARD_MAIN_ALLIANCE = 1103,
+ EY_GRAVEYARD_MAIN_HORDE = 1104,
+ EY_GRAVEYARD_FEL_REALVER = 1105,
+ EY_GRAVEYARD_BLOOD_ELF = 1106,
+ EY_GRAVEYARD_DRAENEI_RUINS = 1107,
+ EY_GRAVEYARD_MAGE_TOWER = 1108
+};
+
+enum EYBattleGroundPoints
+{
+ FEL_REALVER = 0,
+ BLOOD_ELF = 1,
+ DRAENEI_RUINS = 2,
+ MAGE_TOWER = 3,
+
+ EY_PLAYERS_OUT_OF_POINTS = 4,
+ EY_POINTS_MAX = 4
+};
+
+enum EYBattleGroundCreaturesTypes
+{
+ EY_SPIRIT_FEL_REALVER = 0,
+ EY_SPIRIT_BLOOD_ELF = 1,
+ EY_SPIRIT_DRAENEI_RUINS = 2,
+ EY_SPIRIT_MAGE_TOWER = 3,
+ EY_SPIRIT_MAIN_ALLIANCE = 4,
+ EY_SPIRIT_MAIN_HORDE = 5,
+
+ BG_EY_CREATURES_MAX = 6
+};
+
+enum EYBattleGroundObjectTypes
+{
+ BG_EY_OBJECT_DOOR_A = 0,
+ BG_EY_OBJECT_DOOR_H = 1,
+ BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER = 2,
+ BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT = 3,
+ BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT = 4,
+ BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER = 5,
+ BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT = 6,
+ BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT = 7,
+ BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER = 8,
+ BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT = 9,
+ BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT = 10,
+ BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER = 11,
+ BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT = 12,
+ BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT = 13,
+ BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER = 14,
+ BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT = 15,
+ BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT = 16,
+ BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER = 17,
+ BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT = 18,
+ BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT = 19,
+ BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER = 20,
+ BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT = 21,
+ BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT = 22,
+ BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER = 23,
+ BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT = 24,
+ BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT = 25,
+ BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER = 26,
+ BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT = 27,
+ BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT = 28,
+ BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER = 29,
+ BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT = 30,
+ BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT = 31,
+ BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER = 32,
+ BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT = 33,
+ BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT = 34,
+ BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER = 35,
+ BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT = 36,
+ BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT = 37,
+ BG_EY_OBJECT_TOWER_CAP_FEL_REALVER = 38,
+ BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF = 39,
+ BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS = 40,
+ BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER = 41,
+ BG_EY_OBJECT_FLAG_NETHERSTORM = 42,
+ BG_EY_OBJECT_FLAG_FEL_REALVER = 43,
+ BG_EY_OBJECT_FLAG_BLOOD_ELF = 44,
+ BG_EY_OBJECT_FLAG_DRAENEI_RUINS = 45,
+ BG_EY_OBJECT_FLAG_MAGE_TOWER = 46,
+ //buffs
+ BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER = 47,
+ BG_EY_OBJECT_REGENBUFF_FEL_REALVER = 48,
+ BG_EY_OBJECT_BERSERKBUFF_FEL_REALVER = 49,
+ BG_EY_OBJECT_SPEEDBUFF_BLOOD_ELF = 50,
+ BG_EY_OBJECT_REGENBUFF_BLOOD_ELF = 51,
+ BG_EY_OBJECT_BERSERKBUFF_BLOOD_ELF = 52,
+ BG_EY_OBJECT_SPEEDBUFF_DRAENEI_RUINS = 53,
+ BG_EY_OBJECT_REGENBUFF_DRAENEI_RUINS = 54,
+ BG_EY_OBJECT_BERSERKBUFF_DRAENEI_RUINS = 55,
+ BG_EY_OBJECT_SPEEDBUFF_MAGE_TOWER = 56,
+ BG_EY_OBJECT_REGENBUFF_MAGE_TOWER = 57,
+ BG_EY_OBJECT_BERSERKBUFF_MAGE_TOWER = 58,
+ BG_EY_OBJECT_MAX = 59
+};
+
+enum BG_EY_FlagState
+{
+ BG_EY_FLAG_STATE_ON_BASE = 0,
+ BG_EY_FLAG_STATE_WAIT_RESPAWN = 1,
+ BG_EY_FLAG_STATE_ON_PLAYER = 2,
+ BG_EY_FLAG_STATE_ON_GROUND = 3
+};
+
+enum EYBattleGroundPointState
+{
+ EY_POINT_NO_OWNER = 0,
+ EY_POINT_STATE_UNCONTROLLED = 0,
+ EY_POINT_UNDER_CONTROL = 3
+};
+
+struct BattleGroundEYPointIconsStruct
+{
+ BattleGroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex)
+ : WorldStateControlIndex(_WorldStateControlIndex), WorldStateAllianceControlledIndex(_WorldStateAllianceControlledIndex), WorldStateHordeControlledIndex(_WorldStateHordeControlledIndex) {}
+ uint32 WorldStateControlIndex;
+ uint32 WorldStateAllianceControlledIndex;
+ uint32 WorldStateHordeControlledIndex;
+};
+
+struct BattleGroundEYLoosingPointStruct
+{
+ BattleGroundEYLoosingPointStruct(uint32 _SpawnNeutralObjectType, uint32 _DespawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _DespawnObjectTypeHorde, uint32 _MessageIdHorde)
+ : SpawnNeutralObjectType(_SpawnNeutralObjectType), DespawnObjectTypeAlliance(_DespawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), DespawnObjectTypeHorde(_DespawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde) {}
+ uint32 SpawnNeutralObjectType;
+ uint32 DespawnObjectTypeAlliance;
+ uint32 DespawnObjectTypeHorde;
+ uint32 MessageIdHorde;
+ uint32 MessageIdAlliance;
+};
+
+struct BattleGroundEYCapturingPointStruct
+{
+ BattleGroundEYCapturingPointStruct(uint32 _DespawnNeutralObjectType, uint32 _SpawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _SpawnObjectTypeHorde, uint32 _MessageIdHorde, uint32 _GraveYardId)
+ : DespawnNeutralObjectType(_DespawnNeutralObjectType), SpawnObjectTypeAlliance(_SpawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), SpawnObjectTypeHorde(_SpawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde), GraveYardId(_GraveYardId) {}
+ uint32 DespawnNeutralObjectType;
+ uint32 SpawnObjectTypeAlliance;
+ uint32 SpawnObjectTypeHorde;
+ uint32 MessageIdHorde;
+ uint32 MessageIdAlliance;
+ uint32 GraveYardId;
+};
+
+const uint8 BG_EY_TickPoints[EY_POINTS_MAX] = {1, 2, 5, 10};
+const uint32 BG_EY_FlagPoints[EY_POINTS_MAX] = {75, 85, 100, 500};
+
+//constant arrays:
+const BattleGroundEYPointIconsStruct m_PointsIconStruct[EY_POINTS_MAX] =
+{
+ BattleGroundEYPointIconsStruct(FEL_REAVER_UNCONTROL, FEL_REAVER_ALLIANCE_CONTROL, FEL_REAVER_HORDE_CONTROL),
+ BattleGroundEYPointIconsStruct(BLOOD_ELF_UNCONTROL, BLOOD_ELF_ALLIANCE_CONTROL, BLOOD_ELF_HORDE_CONTROL),
+ BattleGroundEYPointIconsStruct(DRAENEI_RUINS_UNCONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL, DRAENEI_RUINS_HORDE_CONTROL),
+ BattleGroundEYPointIconsStruct(MAGE_TOWER_UNCONTROL, MAGE_TOWER_ALLIANCE_CONTROL, MAGE_TOWER_HORDE_CONTROL)
+};
+const BattleGroundEYLoosingPointStruct m_LoosingPointTypes[EY_POINTS_MAX] =
+{
+ BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_H_F_RUINS),
+ BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_H_B_TOWER),
+ BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_H_D_RUINS),
+ BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_H_M_TOWER)
+};
+const BattleGroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] =
+{
+ BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_H_F_RUINS, EY_GRAVEYARD_FEL_REALVER),
+ BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_H_B_TOWER, EY_GRAVEYARD_BLOOD_ELF),
+ BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_H_D_RUINS, EY_GRAVEYARD_DRAENEI_RUINS),
+ BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_H_M_TOWER, EY_GRAVEYARD_MAGE_TOWER)
+};
+
+class BattleGroundEYScore : public BattleGroundScore
+{
+ public:
+ BattleGroundEYScore () : FlagCaptures(0) {};
+ virtual ~BattleGroundEYScore() {};
+ uint32 FlagCaptures;
+};
+
+class BattleGroundEY : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundEY();
+ ~BattleGroundEY();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ /* BG Flags */
+ uint64 GetFlagPickerGUID() const { return m_FlagKeeper; }
+ void SetFlagPicker(uint64 guid) { m_FlagKeeper = guid; }
+ bool IsFlagPickedup() const { return m_FlagKeeper != 0; }
+ uint8 GetFlagState() const { return m_FlagState; }
+ void RespawnFlag(bool send_message);
+ void RespawnFlagAfterDrop();
+
+ void RemovePlayer(Player *plr,uint64 guid);
+ void HandleBuffUse(uint64 const& buff_guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ void HandleKillPlayer(Player *player, Player *killer);
+ virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team);
+ virtual bool SetupBattleGround();
+ virtual void ResetBGSubclass();
+ void UpdateTeamScore(uint32 Team);
+ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+ virtual void FillInitialWorldStates(WorldPacket& data);
+ void SetDroppedFlagGUID(uint64 guid) { m_DroppedFlagGUID = guid;}
+ uint64 GetDroppedFlagGUID() const { return m_DroppedFlagGUID;}
+
+ /* Battleground Events */
+ virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj);
+ virtual void EventPlayerDroppedFlag(Player *Source);
+
+ private:
+ void EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType);
+ void EventTeamCapturedPoint(Player *Source, uint32 Point);
+ void EventTeamLostPoint(Player *Source, uint32 Point);
+ void UpdatePointsCount(uint32 Team);
+ void UpdatePointsIcons(uint32 Team, uint32 Point);
+
+ /* Point status updating procedures */
+ void CheckSomeoneLeftPoint();
+ void CheckSomeoneJoinedPoint();
+ void UpdatePointStatuses();
+
+ /* Scorekeeping */
+ uint32 GetTeamScore(uint32 Team) const { return m_TeamScores[GetTeamIndexByTeamId(Team)]; }
+ void AddPoints(uint32 Team, uint32 Points);
+
+ 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];
+
+ uint32 m_Points_Trigger[EY_POINTS_MAX];
+
+ uint64 m_FlagKeeper; // keepers guid
+ uint64 m_DroppedFlagGUID;
+ uint32 m_FlagCapturedBgObjectType; // type that should be despawned when flag is captured
+ uint8 m_FlagState; // for checking flag state
+ int32 m_FlagsTimer;
+ int32 m_TowerCapCheckTimer;
+
+ uint32 m_PointOwnedByTeam[EY_POINTS_MAX];
+ uint8 m_PointState[EY_POINTS_MAX];
+ int32 m_PointBarStatus[EY_POINTS_MAX];
+ typedef std::vector<uint64> PlayersNearPointType;
+ PlayersNearPointType m_PlayersNearPoint[EY_POINTS_MAX + 1];
+ uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX];
+
+ int32 m_PointAddingTimer;
+};
+#endif
diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp
new file mode 100644
index 00000000000..85de6855563
--- /dev/null
+++ b/src/game/BattleGroundHandler.cpp
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Object.h"
+#include "BattleGroundMgr.h"
+#include "BattleGroundWS.h"
+#include "BattleGround.h"
+
+void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from: " I64FMT, guid);
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battlemaster
+ return;
+
+ // Stop the npc if moving
+ unit->StopMoving();
+
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(unit->GetEntry());
+
+ if(!_player->GetBGAccessByLevel(bgTypeId))
+ {
+ // temp, must be gossip message...
+ SendNotification("You don't meet Battleground level requirements");
+ return;
+ }
+
+ SendBattlegGroundList(guid, bgTypeId);
+}
+
+void WorldSession::SendBattlegGroundList( uint64 guid, uint32 bgTypeId )
+{
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+4+4+1);
+
+ uint64 guid;
+ uint32 bgTypeId;
+ uint32 instanceId;
+ uint8 joinAsGroup;
+
+ recv_data >> guid; // battlemaster guid
+ recv_data >> bgTypeId; // battleground type id (DBC id)
+ recv_data >> instanceId; // instance id, 0 if First Available selected
+ recv_data >> joinAsGroup; // join as group
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT " for BG (Type: %u)", guid, bgTypeId);
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating?
+ return;
+
+ // ignore if we already in BG or BG queue
+ if(_player->InBattleGround())
+ return;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battlemaster
+ return;
+
+ // check Deserter debuff
+ if( !_player->CanJoinToBattleground() )
+ {
+ WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
+ data << (uint32) 0xFFFFFFFE;
+ _player->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ // check existence
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId);
+ if(!bg)
+ return;
+
+ if(joinAsGroup && _player->GetGroup())
+ {
+ Group *grp = _player->GetGroup();
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member) continue;
+
+ if( !member->CanJoinToBattleground() )
+ {
+ WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
+ data << (uint32) 0xFFFFFFFE;
+ _player->GetSession()->SendPacket(&data);
+ continue;
+ }
+ if (member->InBattleGroundQueueForBattleGroundType(bgTypeId))
+ //player is already in this queue
+ continue;
+
+ WorldPacket data;
+ // add to queue
+ uint32 queueSlot = member->AddBattleGroundQueueId(bgTypeId);
+ if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES)
+ {
+ // fill data packet
+ //member->GetSession()->SendPacket(data);
+ continue;
+ }
+
+ // store entry point coords (same as leader entry point)
+ member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(member, bgTypeId);
+ }
+ }
+ else
+ {
+ if (_player->InBattleGroundQueueForBattleGroundType(bgTypeId))
+ //player is already in this queue
+ return;
+ uint32 queueSlot = _player->AddBattleGroundQueueId(bgTypeId);
+ if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES)
+ {
+ WorldPacket data;
+ // fill data packet
+ //SendPacket(data);
+ return;
+ }
+
+ // store entry point coords
+ _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, bgTypeId);
+ }
+}
+
+void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv_data*/ )
+{
+ // empty opcode
+ sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message");
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg) // can't be received if player not in battleground
+ return;
+
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ {
+ uint32 count1 = 0;
+ uint32 count2 = 0;
+
+ Player *ap = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID());
+ if(ap) ++count2;
+
+ Player *hp = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID());
+ if(hp) ++count2;
+
+ WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2));
+ data << count1; // alliance flag holders count
+ /*for(uint8 i = 0; i < count1; i++)
+ {
+ data << uint64(0); // guid
+ data << (float)0; // x
+ data << (float)0; // y
+ }*/
+ data << count2; // horde flag holders count
+ if(ap)
+ {
+ data << (uint64)ap->GetGUID();
+ data << (float)ap->GetPositionX();
+ data << (float)ap->GetPositionY();
+ }
+ if(hp)
+ {
+ data << (uint64)hp->GetGUID();
+ data << (float)hp->GetPositionX();
+ data << (float)hp->GetPositionY();
+ }
+
+ SendPacket(&data);
+ }
+}
+
+void WorldSession::HandleBattleGroundPVPlogdataOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd MSG_PVP_LOG_DATA Message");
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg);
+ SendPacket(&data);
+
+ sLog.outDebug( "WORLD: Sent MSG_PVP_LOG_DATA Message");
+}
+
+void WorldSession::HandleBattleGroundListOpcode( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
+
+ uint32 bgTypeId;
+ recv_data >> bgTypeId; // id from DBC
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating?
+ return;
+
+ // can't be received if player not in BG queue
+ if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId))
+ return;
+
+ BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
+
+ if(!bl)
+ return;
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundListPacket(&data, _player->GetGUID(), _player, bgTypeId);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+1+4+2+1);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message");
+
+ uint8 unk1;
+ uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1
+ uint32 bgTypeId; // type id from dbc
+ uint16 unk; // 0x1F90 constant?
+ uint8 action; // enter battle 0x1, leave queue 0x0
+
+ recv_data >> unk1 >> unk2 >> bgTypeId >> unk >> action;
+
+ if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating?
+ return;
+
+ if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId))
+ return;
+
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId);
+ if(!bg)
+ return;
+
+ uint32 queueSlot = 0;
+ WorldPacket data;
+ switch(action)
+ {
+ case 1: // port to battleground
+ // cheating?
+ if(!_player->IsInvitedForBattleGroundType(bgTypeId))
+ return;
+
+ // check if player is not deserter
+ if( !_player->CanJoinToBattleground() )
+ {
+ WorldPacket data2;
+ data2.Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
+ data2 << (uint32) 0xFFFFFFFE;
+ SendPacket(&data2);
+ return;
+ }
+
+ // if the player is dead, resurrect him before teleport
+ if(!_player->isAlive())
+ {
+ _player->ResurrectPlayer(1.0f,false);
+ _player->SpawnCorpseBones();
+ }
+
+ // leave current group
+ _player->RemoveFromGroup();
+
+ // packet to player about BG status
+ queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId);
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime());
+ _player->GetSession()->SendPacket(&data);
+
+ // remove battleground queue status from BGmgr
+ sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), false);
+
+ // this is still needed here if battleground "jumping" shouldn't add deserter debuff
+ // also this required to prevent stuck at old battleground after SetBattleGroundId set to new
+ if (BattleGround *currentBg = _player->GetBattleGround())
+ currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true);
+
+ _player->SetBattleGroundId(bg->GetTypeID());
+ sBattleGroundMgr.SendToBattleGround(_player, bgTypeId);
+ bg->AddPlayer(_player);
+ break;
+ case 0: // leave queue
+ queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId);
+ _player->RemoveBattleGroundQueueId(bgTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0);
+ sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), true);
+ SendPacket(&data);
+ break;
+ default:
+ sLog.outError("Battleground port: unknown action %u", action);
+ break;
+ }
+}
+
+void WorldSession::HandleBattleGroundLeaveOpcode( WorldPacket & /*recv_data*/ )
+{
+ //CHECK_PACKET_SIZE(recv_data, 1+1+4+2);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message");
+
+ //uint8 unk1, unk2;
+ //uint32 bgTypeId; // id from DBC
+ //uint16 unk3;
+
+ //recv_data >> unk1 >> unk2 >> bgTypeId >> unk3; - no used currently
+
+ //if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? but not important in this case
+ // return;
+
+ // not allow leave battleground in combat
+ if(_player->isInCombat())
+ if(BattleGround* bg = _player->GetBattleGround())
+ if(bg->GetStatus() != STATUS_WAIT_LEAVE)
+ return;
+
+ _player->LeaveBattleground();
+}
+
+void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ )
+{
+ // empty opcode
+ sLog.outDebug( "WORLD: Battleground status" );
+
+ WorldPacket data;
+
+ // TODO: we must put player back to battleground in case disconnect (< 5 minutes offline time) or teleport player on login(!) from battleground map to entry point
+ if(_player->InBattleGround())
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(bg)
+ {
+ uint32 queueSlot = _player->GetBattleGroundQueueIndex(bg->GetTypeID());
+ if((bg->GetStatus() <= STATUS_IN_PROGRESS))
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime());
+ SendPacket(&data);
+ }
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ uint32 queue_id = _player->GetBattleGroundQueueId(i);
+ if (i == queueSlot || !queue_id)
+ continue;
+ BattleGround *bg2 = sBattleGroundMgr.GetBattleGround(queue_id);
+ if(bg2)
+ {
+ //in this call is small bug, this call should be filled by player's waiting time in queue
+ //this call nulls all timers for client :
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0);
+ SendPacket(&data);
+ }
+ }
+ }
+ }
+ else
+ {
+ // we should update all queues? .. i'm not sure if this code is correct
+ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if(uint32 queue_id = _player->GetBattleGroundQueueId(i))
+ {
+ if(BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id))
+ {
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0);
+ SendPacket(&data);
+ }
+ }
+ }
+ }
+}
+
+void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isSpiritService()) // it's not spirit service
+ return;
+
+ sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, guid);
+}
+
+void WorldSession::HandleAreaSpiritHealerQueueOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE");
+
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isSpiritService()) // it's not spirit service
+ return;
+
+ bg->AddPlayerToResurrectQueue(guid, _player->GetGUID());
+}
+
+void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1+1+1);
+
+ sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
+ recv_data.hexlike();
+
+ // ignore if we already in BG or BG queue
+ if(_player->InBattleGround())
+ return;
+
+ for(int qId = 0; qId < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qId)
+ {
+ if(_player->GetBattleGroundQueueId(qId) != 0)
+ return;
+ }
+
+ uint64 guid; // arena Battlemaster guid
+ uint8 type; // 2v2, 3v3 or 5v5
+ uint8 asGroup; // asGroup
+ uint8 isRated; // isRated
+ recv_data >> guid >> type >> asGroup >> isRated;
+
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if(!unit)
+ return;
+
+ if(!unit->isBattleMaster()) // it's not battle master
+ return;
+
+ uint8 arenatype = 0;
+
+ switch(type)
+ {
+ case 0:
+ arenatype = ARENA_TYPE_2v2;
+ break;
+ case 1:
+ arenatype = ARENA_TYPE_3v3;
+ break;
+ case 2:
+ arenatype = ARENA_TYPE_5v5;
+ break;
+ default:
+ sLog.outError("Unknown arena type %u at HandleBattleGroundArenaJoin()", type);
+ return;
+ }
+
+ if(isRated && !_player->GetArenaTeamId(type)) // player not in arena team of that size
+ {
+ _player->GetSession()->SendNotInArenaTeamPacket(arenatype);
+ return;
+ }
+
+ if(asGroup && !_player->GetGroup()) // player not in group
+ return;
+
+ // check existence
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(BATTLEGROUND_AA);
+ if(!bg)
+ return;
+
+ bg->SetArenaType(arenatype);
+ bg->SetRated(isRated);
+
+ if(asGroup && _player->GetGroup())
+ {
+ Group *grp = _player->GetGroup();
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member) continue;
+
+ /*if (!member->CanJoinToBattleground())
+ //player has deserter aura .. do nothing
+ */
+
+ if (member->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA))
+ //player is already in this queue
+ continue;
+
+ // add to queue
+ uint32 queueSlot = member->AddBattleGroundQueueId(BATTLEGROUND_AA);
+ if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES)
+ {
+ WorldPacket data;
+ //fill data
+ //member->GetSession()->SendPacket(data);
+ continue;
+ }
+
+ // store entry point coords (same as leader entry point)
+ member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, BATTLEGROUND_AA);
+ member->GetSession()->SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(member, BATTLEGROUND_AA);
+ }
+ }
+ else
+ {
+ /*if (!member->CanJoinToBattleground())
+ //player has deserter aura .. do nothing
+ */
+
+ if (_player->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA))
+ //player is already in this queue
+ return;
+
+ uint32 queueSlot = _player->AddBattleGroundQueueId(BATTLEGROUND_AA);
+ if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES)
+ {
+ WorldPacket data;
+ //fill data (player is in 3 queues already)
+ //SendPacket(data);
+ return;
+ }
+
+ // store entry point coords
+ _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation());
+
+ WorldPacket data;
+ // send status packet (in queue)
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0);
+ SendPacket(&data);
+ sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(_player, BATTLEGROUND_AA);
+ }
+}
+
+void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 playerGuid;
+ recv_data >> playerGuid;
+ Player *reportedPlayer = objmgr.GetPlayer(playerGuid);
+
+ if(!reportedPlayer)
+ {
+ sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: player not found");
+ return;
+ }
+
+ sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
+
+ reportedPlayer->ReportedAfkBy(_player);
+}
diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp
new file mode 100644
index 00000000000..52dad08345b
--- /dev/null
+++ b/src/game/BattleGroundMgr.cpp
@@ -0,0 +1,867 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Player.h"
+#include "BattleGroundMgr.h"
+#include "BattleGroundAV.h"
+#include "BattleGroundAB.h"
+#include "BattleGroundEY.h"
+#include "BattleGroundWS.h"
+#include "BattleGroundNA.h"
+#include "BattleGroundBE.h"
+#include "BattleGroundAA.h"
+#include "BattleGroundRL.h"
+#include "SharedDefines.h"
+#include "Policies/SingletonImp.h"
+#include "MapManager.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "World.h"
+#include "Chat.h"
+
+INSTANTIATE_SINGLETON_1( BattleGroundMgr );
+
+/*********************************************************/
+/*** BATTLEGROUND QUEUE SYSTEM ***/
+/*********************************************************/
+
+BattleGroundQueue::BattleGroundQueue()
+{
+ //queues are empty, we don't have to call clear()
+ for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ m_QueuedPlayers[i].Horde = 0;
+ m_QueuedPlayers[i].Alliance = 0;
+ //m_QueuedPlayers[i].AverageTime = 0;
+ }
+}
+
+BattleGroundQueue::~BattleGroundQueue()
+{
+ for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ m_QueuedPlayers[i].clear();
+ }
+}
+
+void BattleGroundQueue::AddPlayer(Player *plr, uint32 bgTypeId)
+{
+ uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel();
+
+ //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak
+ PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()];
+ info.InviteTime = 0;
+ info.IsInvitedToBGInstanceGUID = 0;
+ info.LastInviteTime = 0;
+ info.LastOnlineTime = getMSTime();
+ info.Team = plr->GetTeam();
+
+ //add player to waiting order queue
+ m_PlayersSortedByWaitTime[queue_id].push_back(plr->GetGUID());
+
+ if(plr->GetTeam() == ALLIANCE)
+ ++m_QueuedPlayers[queue_id].Alliance;
+ else
+ ++m_QueuedPlayers[queue_id].Horde;
+
+ this->Update(bgTypeId, queue_id);
+
+ if( sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) )
+ {
+ BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgTypeId);
+ char const* bgName = bg->GetName();
+
+ uint32 q_min_level = Player::GetMinLevelForBattleGroundQueueId(queue_id);
+ uint32 q_max_level = Player::GetMaxLevelForBattleGroundQueueId(queue_id);
+
+ // replace hardcoded max level by player max level for nice output
+ if(q_max_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ q_max_level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
+
+ int8 MinPlayers = bg->GetMinPlayersPerTeam();
+
+ uint8 qHorde = m_QueuedPlayers[queue_id].Horde;
+ uint8 qAlliance = m_QueuedPlayers[queue_id].Alliance;
+
+ // Show queue status to player only (when joining queue)
+ if(sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY))
+ {
+ ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF,
+ bgName, q_min_level, q_max_level, qAlliance, MinPlayers - qAlliance, qHorde, MinPlayers - qHorde);
+ }
+ // System message
+ else
+ {
+ sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD,
+ bgName, q_min_level, q_max_level, qAlliance, MinPlayers - qAlliance, qHorde, MinPlayers - qHorde);
+ }
+ }
+}
+
+void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount)
+{
+ Player *plr = objmgr.GetPlayer(guid);
+
+ uint32 queue_id = 0;
+ QueuedPlayersMap::iterator itr;
+ bool IsSet = false;
+ if(!plr)
+ { //player is offline, we need to find him somewhere in queues
+ /// there is something wrong if this code is run, because we have in queue only online players!
+ sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash");
+ for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ itr = m_QueuedPlayers[i].find(guid);
+ if(itr != m_QueuedPlayers[i].end())
+ {
+ queue_id = i;
+ IsSet = true;
+ break;
+ }
+ }
+ }
+ else
+ { //player is online, we have his level, so we can find exact queue from his level
+ queue_id = plr->GetBattleGroundQueueIdFromLevel();
+ itr = m_QueuedPlayers[queue_id].find(guid);
+ IsSet = true;
+ }
+
+ //all variables are set, so remove player
+ //remove player from time queue
+ m_PlayersSortedByWaitTime[queue_id].remove(guid);
+
+ if (IsSet && itr != m_QueuedPlayers[queue_id].end())
+ {
+ if (!itr->second.IsInvitedToBGInstanceGUID)
+ {
+ if(itr->second.Team == ALLIANCE)
+ --m_QueuedPlayers[queue_id].Alliance;
+ else
+ --m_QueuedPlayers[queue_id].Horde;
+ }
+ else
+ {
+ if (decreaseInvitedCount)
+ {
+ BattleGround* bg = sBattleGroundMgr.GetBattleGround(itr->second.IsInvitedToBGInstanceGUID);
+ if (bg)
+ bg->DecreaseInvitedCount(itr->second.Team);
+ }
+ }
+ m_QueuedPlayers[queue_id].erase(itr);
+ }
+}
+
+/*
+this method is called when player is inserted, or removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue
+add method calls this by itself, the remove method could works in other way, so you have to call this method from other code after calling remove method
+*/
+void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id)
+{
+ if (queue_id >= MAX_BATTLEGROUND_QUEUES)
+ {
+ //this is error, that caused crashes (not in , but now it shouldn't)
+ sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash");
+ return;
+ }
+
+ //if no players in queue ... do nothing
+ if (this->m_QueuedPlayers[queue_id].Alliance == 0 && this->m_QueuedPlayers[queue_id].Horde == 0)
+ return;
+
+ //battleground with free slot for player should be always the last in this queue
+ for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); ++itr)
+ {
+ // battleground is running, so if:
+ // DO NOT allow queue manager to invite new player to running arena
+ if ((*itr)->isBattleGround() && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE)
+ {
+ //we must check both teams
+ BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!)
+ // and iterator is invalid
+
+ //check if there are some players in queue
+ if (m_QueuedPlayers[queue_id].Alliance > 0 || m_QueuedPlayers[queue_id].Horde > 0)
+ {
+ for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();)
+ {
+ Player* plr = objmgr.GetPlayer(*itr2);
+ if (!plr)
+ {
+ //something is wrong!, kick player from queue
+ sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug");
+ uint64 oldval = *itr2;
+ itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2);
+ RemovePlayer(oldval, true);
+ continue;
+ }
+
+ // player will be invited, if in bg there is a free slot for him
+ if (bg->HasFreeSlotsForTeam(plr->GetTeam()))
+ {
+ // iterator to player's queue status
+ QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2);
+
+ // remove him from time queue
+ itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2);
+
+ // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and pls report it)
+ if (itrPlayerStatus == m_QueuedPlayers[queue_id].end())
+ continue;
+
+ // check if player is not already invited
+ if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID)
+ {
+ itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg->GetInstanceID();
+ itrPlayerStatus->second.InviteTime = getMSTime();
+ itrPlayerStatus->second.LastInviteTime = getMSTime();
+ if(itrPlayerStatus->second.Team == ALLIANCE)
+ --m_QueuedPlayers[queue_id].Alliance;
+ else
+ --m_QueuedPlayers[queue_id].Horde;
+ sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID());
+
+ WorldPacket data;
+ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId);
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+ }
+ else
+ ++itr2;
+
+ //if battleground is FULL, then it is removed from free slot queue - not yet implemented!
+ if (!bg->HasFreeSlots())
+ {
+ //if bg is full, there is no need to invite other players, so break
+ break;
+ //remove BG from BGFreeSlotQueue - not used now, in this system we don't remove BGs from free queue
+ //bg->RemoveFromBGFreeSlotQueue() --- do not uncomment this - not yet implemented
+ }
+ }
+ }
+ }
+ }
+
+ /* THIS IS A CASE THAT IN QUEUE THERE IS ENOUGHT PLAYERS TO START NEW BG */
+ //itr->end is the last BG - template, which is not already started!
+
+ /* here will be a most of change, when we create battlegrounds instantiated */
+ /* if (there is enough players to start new BG)
+ Battleground* newbg = sBattleGroundMgr.CreateNewBattleGround(bgTypeId)
+ - that function will use the COPY constructor on BattleGround class ( in bg manager we should have one battleground as a template
+ (battleground template will be used only to create new BGs, it will be an instance of BG class, but it won't ever start) */
+
+ /* following code is working with current Battleground system and it should be removed, when BGs will work like instances */
+ BattleGround* bg2 = sBattleGroundMgr.GetBattleGround(bgTypeId);
+ if (bg2->GetQueueType() != MAX_BATTLEGROUND_QUEUES || bg2->GetStatus() != STATUS_WAIT_QUEUE)
+ return;
+ if (m_QueuedPlayers[queue_id].Alliance >= bg2->GetMinPlayersPerTeam() && m_QueuedPlayers[queue_id].Horde >= bg2->GetMinPlayersPerTeam())
+ {
+ bg2->SetStatus(STATUS_WAIT_JOIN);
+ bg2->SetQueueType(queue_id);
+
+ for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();)
+ {
+ Player* plr = objmgr.GetPlayer(*itr2);
+ if (!plr)
+ {
+ //something is wrong!, kick player from queue
+ sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug");
+ uint64 oldval = *itr2;
+ itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2);
+ RemovePlayer(oldval, true);
+ continue;
+ }
+
+ /* TODO: (i'm not sure this code will be useful:
+ here should be some condition like if (bg2->isArena() && bg2->isRated())
+ {
+ invite players from 1 certain group on each faction to play arena match
+ } else if ....and existing code
+ */
+ // player will be invited, if in bg there is a free slot for him
+ if (bg2->HasFreeSlotsForTeam(plr->GetTeam()))
+ {
+ // iterator to player's queue status
+ QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2);
+
+ // remove him from time queue
+ itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2);
+
+ // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and report it)
+ if (itrPlayerStatus == m_QueuedPlayers[queue_id].end())
+ continue;
+
+ //check if player is not already invited
+ if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID)
+ {
+ itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->GetInstanceID();
+ itrPlayerStatus->second.InviteTime = getMSTime();
+ itrPlayerStatus->second.LastInviteTime = getMSTime();
+
+ if(itrPlayerStatus->second.Team == ALLIANCE)
+ --m_QueuedPlayers[queue_id].Alliance;
+ else
+ --m_QueuedPlayers[queue_id].Horde;
+
+ sBattleGroundMgr.InvitePlayer(plr, bg2->GetInstanceID());
+
+ WorldPacket data;
+ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId);
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+ }
+ else
+ ++itr2;
+ }
+ bg2->StartBattleGround();
+ }
+}
+
+/*********************************************************/
+/*** BATTLEGROUND QUEUE EVENTS ***/
+/*********************************************************/
+
+bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 p_time)
+{
+ Player* plr = objmgr.GetPlayer( m_PlayerGuid );
+
+ // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
+ if (!plr)
+ return true;
+
+ // Player can be in another BG queue and must be removed in normal way in any case
+ // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG)
+ // if (plr->GetBattleGroundId() > 0)
+ // return true;
+
+ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
+ if (!bg)
+ return true;
+
+ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID());
+ if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
+ {
+ // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems
+ BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()];
+ BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid);
+ if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
+ {
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+ }
+ return true; //event will be deleted
+}
+
+void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
+{
+ //this should not be called
+ sLog.outError("Battleground invite event ABORTED!");
+}
+
+bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
+{
+ Player* plr = objmgr.GetPlayer( m_PlayerGuid );
+ if (!plr)
+ // player logged off (we should do nothing, he is correctly removed from queue in another procedure)
+ return true;
+
+ // Player can be in another BG queue and must be removed in normal way in any case
+ //if (plr->InBattleGround())
+ // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG)
+ // return true;
+
+ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID);
+ if (!bg)
+ return true;
+
+ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID());
+ if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue (base at player data
+ {
+ // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems
+ BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()];
+ BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid);
+ if (qItr!=qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID)
+ {
+ plr->RemoveBattleGroundQueueId(bg->GetTypeID());
+ sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true);
+ sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType());
+
+ WorldPacket data;
+ sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_NONE, 0, 0);
+ plr->GetSession()->SendPacket(&data);
+ }
+ }
+
+ //event will be deleted
+ return true;
+}
+
+void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
+{
+ //this should not be called
+ sLog.outError("Battleground remove event ABORTED!");
+}
+
+/*********************************************************/
+/*** BATTLEGROUND MANAGER ***/
+/*********************************************************/
+
+BattleGroundMgr::BattleGroundMgr()
+{
+ m_BattleGrounds.clear();
+}
+
+BattleGroundMgr::~BattleGroundMgr()
+{
+ for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr)
+ delete itr->second;
+ m_BattleGrounds.clear();
+}
+
+void BattleGroundMgr::Update(time_t diff)
+{
+ for(BattleGroundSet::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr)
+ itr->second->Update(diff);
+}
+
+void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2)
+{
+ // we can be in 3 queues in same time...
+ if(StatusID == 0)
+ {
+ data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3);
+ *data << uint32(QueueSlot); // queue id (0...2)
+ *data << uint64(0);
+ return;
+ }
+
+ data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4));
+ *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time
+ // uint64 in client
+ *data << uint64( uint64(bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) );
+ *data << uint32(0); // unknown
+ // alliance/horde for BG and skirmish/rated for Arenas
+ *data << uint8(bg->isArena() ? (bg->isRated() ? 1 : 0) : bg->GetTeamIndexByTeamId(team));
+ *data << uint32(StatusID); // status
+ switch(StatusID)
+ {
+ case STATUS_WAIT_QUEUE: // status_in_queue
+ *data << uint32(Time1); // average wait time, milliseconds
+ *data << uint32(Time2); // time in queue, updated every minute?
+ break;
+ case STATUS_WAIT_JOIN: // status_invite
+ *data << uint32(bg->GetMapId()); // map id
+ *data << uint32(Time1); // time to remove from queue, milliseconds
+ break;
+ case STATUS_IN_PROGRESS: // status_in_progress
+ *data << uint32(bg->GetMapId()); // map id
+ *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds
+ *data << uint32(Time2); // time from bg start, milliseconds
+ *data << uint8(0x1); // unk sometimes 0x0!
+ break;
+ default:
+ sLog.outError("Unknown BG status!");
+ break;
+ }
+}
+
+void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
+{
+ uint8 type = (bg->isArena() ? 1 : 0);
+ // last check on 2.4.1
+ data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize()));
+ *data << uint8(type); // seems to be type (battleground=0/arena=1)
+ if(type) // arena
+ {
+ for(uint8 i = 0; i < 2; i++)
+ {
+ *data << uint32(3000+1+i); // rating change: showed value - 3000
+ *data << uint32(0); // 2.4.0, has some to do with rating change...
+ *data << uint8(0); // some unknown string
+ }
+ }
+
+ if(bg->GetWinner() == 2)
+ {
+ *data << uint8(0); // bg in progress
+ }
+ else
+ {
+ *data << uint8(1); // bg ended
+ *data << uint8(bg->GetWinner()); // who win
+ }
+
+ *data << (int32)(bg->GetPlayerScoresSize());
+
+ for(std::map<uint64, BattleGroundScore*>::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr)
+ {
+ *data << (uint64)itr->first;
+ *data << (int32)itr->second->KillingBlows;
+ if(type)
+ {
+ // this value is team (green/gold)?
+ // that part probably wrong
+ Player *plr = objmgr.GetPlayer(itr->first);
+ if(plr)
+ {
+ if(plr->GetTeam() == HORDE)
+ *data << uint8(0);
+ else if(plr->GetTeam() == ALLIANCE)
+ *data << uint8(1);
+ else
+ *data << uint8(0);
+ }
+ else
+ *data << uint8(0);
+ }
+ else
+ {
+ *data << (int32)itr->second->HonorableKills;
+ *data << (int32)itr->second->Deaths;
+ *data << (int32)itr->second->BonusHonor; // bonus honor
+ }
+ *data << (int32)itr->second->DamageDone; // damage done
+ *data << (int32)itr->second->HealingDone; // healing done
+ switch(bg->GetTypeID()) // battleground specific things
+ {
+ case BATTLEGROUND_AV:
+ *data << (uint32)0x00000005; // count of next fields
+ *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted
+ *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended
+ *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted
+ *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended
+ *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured
+ break;
+ case BATTLEGROUND_WS:
+ *data << (uint32)0x00000002; // count of next fields
+ *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures
+ *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns
+ break;
+ case BATTLEGROUND_AB:
+ *data << (uint32)0x00000002; // count of next fields
+ *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted
+ *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended
+ break;
+ case BATTLEGROUND_EY:
+ *data << (uint32)0x00000001; // count of next fields
+ *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures
+ break;
+ case BATTLEGROUND_NA:
+ case BATTLEGROUND_BE:
+ case BATTLEGROUND_AA:
+ case BATTLEGROUND_RL:
+ *data << (int32)0; // 0
+ break;
+ default:
+ sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID());
+ *data << (int32)0;
+ break;
+ }
+ }
+}
+
+void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId)
+{
+ /*bgTypeId is:
+ 0 - Your group has joined a battleground queue, but you are not eligible
+ 1 - Your group has joined the queue for AV
+ 2 - Your group has joined the queue for WS
+ 3 - Your group has joined the queue for AB
+ 4 - Your group has joined the queue for NA
+ 5 - Your group has joined the queue for BE Arena
+ 6 - Your group has joined the queue for All Arenas
+ 7 - Your group has joined the queue for EotS*/
+ data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4);
+ *data << uint32(bgTypeId);
+}
+
+void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value)
+{
+ data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4);
+ *data << uint32(field);
+ *data << uint32(value);
+}
+
+void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid)
+{
+ data->Initialize(SMSG_PLAY_SOUND, 4);
+ *data << uint32(soundid);
+}
+
+void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr)
+{
+ data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8);
+ *data << uint64(plr->GetGUID());
+}
+
+void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr)
+{
+ data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
+ *data << uint64(plr->GetGUID());
+}
+
+void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID)
+{
+ // set invited player counters:
+ BattleGround* bg = this->GetBattleGround(bgInstanceGUID);
+ if(!bg)
+ return;
+
+ bg->IncreaseInvitedCount(plr->GetTeam());
+ plr->SetInviteForBattleGroundType(bg->GetTypeID());
+ // create invite events:
+ //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events
+ BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID);
+ plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2));
+ BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, plr->GetTeam());
+ plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME));
+}
+
+uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO)
+{
+ // Create the BG
+ BattleGround *bg = NULL;
+
+ switch(bgTypeId)
+ {
+ case BATTLEGROUND_AV: bg = new BattleGroundAV; break;
+ case BATTLEGROUND_WS: bg = new BattleGroundWS; break;
+ case BATTLEGROUND_AB: bg = new BattleGroundAB; break;
+ case BATTLEGROUND_NA: bg = new BattleGroundNA; break;
+ case BATTLEGROUND_BE: bg = new BattleGroundBE; break;
+ case BATTLEGROUND_AA: bg = new BattleGroundAA; break;
+ case BATTLEGROUND_EY: bg = new BattleGroundEY; break;
+ case BATTLEGROUND_RL: bg = new BattleGroundRL; break;
+ default:bg = new BattleGround; break; // placeholder for non implemented BG
+ }
+
+ bg->SetMapId(MapID);
+ bg->Reset();
+ if(!bg->SetupBattleGround())
+ {
+ delete bg;
+ return 0;
+ }
+
+ BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId);
+ //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed
+ if (bl)
+ {
+ bg->SetArenaorBGType(bl->type == TYPE_ARENA);
+ }
+
+ bg->SetTypeID(bgTypeId);
+ bg->SetInstanceID(bgTypeId); // temporary
+ bg->SetMinPlayersPerTeam(MinPlayersPerTeam);
+ bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam);
+ bg->SetMinPlayers(MinPlayersPerTeam*2);
+ bg->SetMaxPlayers(MaxPlayersPerTeam*2);
+ bg->SetName(BattleGroundName);
+ bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO);
+ bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO);
+ bg->SetLevelRange(LevelMin, LevelMax);
+ //add BaggleGround instance to FreeSlotQueue
+ bg->AddToBGFreeSlotQueue();
+
+ AddBattleGround(bg->GetInstanceID(), bg);
+ //sLog.outDetail("BattleGroundMgr: Created new battleground: %u %s (Map %u, %u players per team, Levels %u-%u)", bg_TypeID, bg->m_Name, bg->m_MapId, bg->m_MaxPlayersPerTeam, bg->m_LevelMin, bg->m_LevelMax);
+ return bg->GetInstanceID();
+}
+
+void BattleGroundMgr::CreateInitialBattleGrounds()
+{
+ float AStartLoc[4];
+ float HStartLoc[4];
+ uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2;
+ BattlemasterListEntry const *bl;
+ WorldSafeLocsEntry const *start;
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 bgTypeID = fields[0].GetUInt32();
+
+ // can be overwrited by values from DB
+ bl = sBattlemasterListStore.LookupEntry(bgTypeID);
+ if(!bl)
+ {
+ sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.",bgTypeID);
+ continue;
+ }
+
+ MaxPlayersPerTeam = bl->maxplayersperteam;
+ MinPlayersPerTeam = bl->maxplayersperteam/2;
+ MinLvl = bl->minlvl;
+ MaxLvl = bl->maxlvl;
+
+ if(fields[1].GetUInt32())
+ MinPlayersPerTeam = fields[1].GetUInt32();
+
+ if(fields[2].GetUInt32())
+ MaxPlayersPerTeam = fields[2].GetUInt32();
+
+ if(fields[3].GetUInt32())
+ MinLvl = fields[3].GetUInt32();
+
+ if(fields[4].GetUInt32())
+ MaxLvl = fields[4].GetUInt32();
+
+ start1 = fields[5].GetUInt32();
+
+ start = sWorldSafeLocsStore.LookupEntry(start1);
+ if(start)
+ {
+ AStartLoc[0] = start->x;
+ AStartLoc[1] = start->y;
+ AStartLoc[2] = start->z;
+ AStartLoc[3] = fields[6].GetFloat();
+ }
+ else if(bgTypeID == BATTLEGROUND_AA)
+ {
+ AStartLoc[0] = 0;
+ AStartLoc[1] = 0;
+ AStartLoc[2] = 0;
+ AStartLoc[3] = fields[6].GetFloat();
+ }
+ else
+ {
+ sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.",bgTypeID,start1);
+ continue;
+ }
+
+ start2 = fields[7].GetUInt32();
+
+ start = sWorldSafeLocsStore.LookupEntry(start2);
+ if(start)
+ {
+ HStartLoc[0] = start->x;
+ HStartLoc[1] = start->y;
+ HStartLoc[2] = start->z;
+ HStartLoc[3] = fields[8].GetFloat();
+ }
+ else if(bgTypeID == BATTLEGROUND_AA)
+ {
+ HStartLoc[0] = 0;
+ HStartLoc[1] = 0;
+ HStartLoc[2] = 0;
+ HStartLoc[3] = fields[8].GetFloat();
+ }
+ else
+ {
+ sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.",bgTypeID,start2);
+ continue;
+ }
+
+ //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl);
+ if(!CreateBattleGround(bgTypeID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3]))
+ continue;
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u battlegrounds", count );
+}
+
+void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId)
+{
+ uint32 PlayerLevel = 10;
+
+ if(plr)
+ PlayerLevel = plr->getLevel();
+
+ data->Initialize(SMSG_BATTLEFIELD_LIST);
+ *data << uint64(guid); // battlemaster guid
+ *data << uint32(bgTypeId); // battleground id
+ if(bgTypeId == BATTLEGROUND_AA) // arena
+ {
+ *data << uint8(5); // unk
+ *data << uint32(0); // unk
+ }
+ else // battleground
+ {
+ *data << uint8(0x00); // unk
+
+ size_t count_pos = data->wpos();
+ uint32 count = 0;
+ *data << uint32(0x00); // number of bg instances
+
+ for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr)
+ {
+ if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel()))
+ {
+ *data << uint32(itr->second->GetInstanceID());
+ ++count;
+ }
+ }
+ data->put<uint32>( count_pos , count);
+ }
+}
+
+void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 bgTypeId)
+{
+ BattleGround *bg = GetBattleGround(bgTypeId);
+ if(bg)
+ {
+ uint32 mapid = bg->GetMapId();
+ float x, y, z, O;
+ bg->GetTeamStartLoc(pl->GetTeam(), x, y, z, O);
+
+ sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O);
+ pl->TeleportTo(mapid, x, y, z, O);
+ }
+}
+
+void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid)
+{
+ WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
+ uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds
+ if(time_ == uint32(-1))
+ time_ = 0;
+ data << guid << time_;
+ pl->GetSession()->SendPacket(&data);
+}
diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h
new file mode 100644
index 00000000000..90eb4d2f775
--- /dev/null
+++ b/src/game/BattleGroundMgr.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __BATTLEGROUNDMGR_H
+#define __BATTLEGROUNDMGR_H
+
+#include "BattleGround.h"
+#include "Policies/Singleton.h"
+
+class BattleGround;
+
+//TODO it is not possible to have this structure, because we should have BattlegroundSet for each queue
+//so i propose to change this type to array 1..MAX_BATTLEGROUND_TYPES of sets or maps..
+typedef std::map<uint32, BattleGround*> BattleGroundSet;
+//typedef std::map<uint32, BattleGroundQueue*> BattleGroundQueueSet;
+typedef std::deque<BattleGround*> BGFreeSlotQueueType;
+
+#define MAX_BATTLEGROUND_QUEUES 7 // for level ranges 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70+
+
+#define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array
+
+struct PlayerQueueInfo
+{
+ uint32 InviteTime; // first invite time
+ uint32 LastInviteTime; // last invite time
+ uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG
+ uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes
+ uint32 Team; // Player team (ALLIANCE/HORDE)
+ bool IsRated;
+ bool AsGroup; // uint32 GroupId;
+ uint8 ArenaType;
+};
+
+struct PlayersCount
+{
+ uint32 Alliance;
+ uint32 Horde;
+};
+
+template<class _Kty, class _Ty> class bgqueue: public std::map<_Kty, _Ty>
+{
+ public:
+ uint32 Alliance;
+ uint32 Horde;
+ //bool Ready; // not used now
+ //uint32 AverageTime; //not already implemented (it should be average time in queue for last 10 players)
+};
+
+class BattleGroundQueue
+{
+ public:
+ BattleGroundQueue();
+ ~BattleGroundQueue();
+/*
+ uint32 GetType();
+ void SetType(uint32 type);*/
+
+ void Update(uint32 bgTypeId, uint32 queue_id);
+
+ void AddPlayer(Player *plr, uint32 bgTypeId);
+ void RemovePlayer(uint64 guid, bool decreaseInvitedCount);
+
+ typedef bgqueue<uint64, PlayerQueueInfo> QueuedPlayersMap;
+ QueuedPlayersMap m_QueuedPlayers[MAX_BATTLEGROUND_QUEUES];
+ typedef std::list<uint64> PlayerGuidsSortedByTimeQueue;
+ PlayerGuidsSortedByTimeQueue m_PlayersSortedByWaitTime[MAX_BATTLEGROUND_QUEUES];
+};
+
+/*
+ This class is used to invite player to BG again, when minute lasts from his first invitation
+ it is capable to solve all possibilities
+*/
+class BGQueueInviteEvent : public BasicEvent
+{
+ public:
+ BGQueueInviteEvent(uint64 pl_guid, uint32 BgInstanceGUID) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID) {};
+ virtual ~BGQueueInviteEvent() {};
+
+ virtual bool Execute(uint64 e_time, uint32 p_time);
+ virtual void Abort(uint64 e_time);
+ private:
+ uint64 m_PlayerGuid;
+ uint32 m_BgInstanceGUID;
+
+};
+
+/*
+ This class is used to remove player from BG queue after 2 minutes from first invitation
+*/
+class BGQueueRemoveEvent : public BasicEvent
+{
+ public:
+ BGQueueRemoveEvent(uint64 pl_guid, uint32 bgInstanceGUID, uint32 playersTeam) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_PlayersTeam(playersTeam) {};
+ virtual ~BGQueueRemoveEvent() {};
+
+ virtual bool Execute(uint64 e_time, uint32 p_time);
+ virtual void Abort(uint64 e_time);
+ private:
+ uint64 m_PlayerGuid;
+ uint32 m_BgInstanceGUID;
+ uint32 m_PlayersTeam;
+};
+
+
+class BattleGroundMgr
+{
+ public:
+ /* Construction */
+ BattleGroundMgr();
+ ~BattleGroundMgr();
+ void Update(time_t diff);
+
+ /* Packet Building */
+ void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr);
+ void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr);
+ void BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player *plr, uint32 bgTypeId);
+ void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId);
+ void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value);
+ void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg);
+ void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2);
+ void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid);
+
+ /* Player invitation */
+ // called from Queue update, or from Addplayer to queue
+ void InvitePlayer(Player* plr, uint32 bgInstanceGUID);
+
+ /* Battlegrounds */
+ BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); };
+ BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); };
+
+ BattleGround* GetBattleGround(uint8 ID)
+ {
+ BattleGroundSet::iterator i = m_BattleGrounds.find(ID);
+ if(i != m_BattleGrounds.end())
+ return i->second;
+ else
+ return NULL;
+ };
+
+ uint32 CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO);
+
+ inline void AddBattleGround(uint32 ID, BattleGround* BG) { m_BattleGrounds[ID] = BG; };
+
+ void CreateInitialBattleGrounds();
+
+ void SendToBattleGround(Player *pl, uint32 bgTypeId);
+
+ /* Battleground queues */
+ //these queues are instantiated when creating BattlegroundMrg
+ BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_TYPES]; // public, because we need to access them in BG handler code
+
+ BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPES];
+
+ void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid);
+
+ private:
+
+ /* Battlegrounds */
+ BattleGroundSet m_BattleGrounds;
+};
+
+#define sBattleGroundMgr MaNGOS::Singleton<BattleGroundMgr>::Instance()
+#endif
diff --git a/src/game/BattleGroundNA.cpp b/src/game/BattleGroundNA.cpp
new file mode 100644
index 00000000000..636efe7e586
--- /dev/null
+++ b/src/game/BattleGroundNA.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundNA.h"
+#include "Creature.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Language.h"
+
+BattleGroundNA::BattleGroundNA()
+{
+ m_BgObjects.resize(BG_NA_OBJECT_MAX);
+}
+
+BattleGroundNA::~BattleGroundNA()
+{
+
+}
+
+void BattleGroundNA::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+
+ // after bg start we get there
+ if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
+ {
+ ModifyStartDelayTime(diff);
+
+ if (!(m_Events & 0x01))
+ {
+ m_Events |= 0x01;
+ for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; i++)
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+
+ SetStartDelayTime(START_DELAY1);
+ SendMessageToAll(LANG_ARENA_ONE_MINUTE);
+ }
+ // After 30 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04))
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(LANG_ARENA_THIRTY_SECONDS);
+ }
+ // After 15 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08))
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS);
+ }
+ // delay expired (1 minute)
+ else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10))
+ {
+ m_Events |= 0x10;
+
+ for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++)
+ DoorOpen(i);
+
+ SendMessageToAll(LANG_ARENA_BEGUN);
+ SetStatus(STATUS_IN_PROGRESS);
+ SetStartDelayTime(0);
+
+ for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
+ if(Player *plr = objmgr.GetPlayer(itr->first))
+ plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
+ }
+ }
+
+ /*if(GetStatus() == STATUS_IN_PROGRESS)
+ {
+ // update something
+ }*/
+}
+
+void BattleGroundNA::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundNAScore* sc = new BattleGroundNAScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/)
+{
+
+}
+
+void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ if(!killer)
+ {
+ sLog.outError("BattleGroundNA: Killer player not found");
+ return;
+ }
+
+ BattleGround::HandleKillPlayer(player, killer);
+
+ uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam());
+
+ ++m_TeamKills[killer_team_index]; // add kills to killer's team
+
+ if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam()))
+ {
+ // all opponents killed
+ EndBattleGround(killer->GetTeam());
+ }
+}
+
+void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ //uint32 SpellId = 0;
+ //uint64 buff_guid = 0;
+ switch(Trigger)
+ {
+ case 4536: // buff trigger?
+ case 4537: // buff trigger?
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+
+ //if(buff_guid)
+ // HandleTriggerBuff(buff_guid,Source);
+}
+
+void BattleGroundNA::ResetBGSubclass()
+{
+ m_TeamKills[BG_TEAM_ALLIANCE] = 0;
+ m_TeamKills[BG_TEAM_HORDE] = 0;
+}
+
+bool BattleGroundNA::SetupBattleGround()
+{
+ // gates
+ if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854f, 2966.833f, 12.6462f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179f, 2874.97f, 12.39171f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709f, 2981.777f, 10.70117f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064f, 2858.438f, 10.23631f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY))
+ {
+ sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+20:12:14 id:036668 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86
+0000: 2f 02 00 00 72 0e 00 00 00 00 00 00 09 00 11 0a | /...r...........
+0010: 00 00 01 00 00 00 0f 0a 00 00 00 00 00 00 10 0a | ................
+0020: 00 00 00 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................
+0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................
+0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................
+0050: 00 00 00 00 00 00 | ......
+*/
diff --git a/src/game/BattleGroundNA.h b/src/game/BattleGroundNA.h
new file mode 100644
index 00000000000..1ca04e8ba2f
--- /dev/null
+++ b/src/game/BattleGroundNA.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef __BATTLEGROUNDNA_H
+#define __BATTLEGROUNDNA_H
+
+class BattleGround;
+
+enum BattleGroundNAObjectTypes
+{
+ BG_NA_OBJECT_DOOR_1 = 0,
+ BG_NA_OBJECT_DOOR_2 = 1,
+ BG_NA_OBJECT_DOOR_3 = 2,
+ BG_NA_OBJECT_DOOR_4 = 3,
+ BG_NA_OBJECT_MAX = 4
+};
+
+enum BattleGroundNAObjects
+{
+ BG_NA_OBJECT_TYPE_DOOR_1 = 183978,
+ BG_NA_OBJECT_TYPE_DOOR_2 = 183980,
+ BG_NA_OBJECT_TYPE_DOOR_3 = 183977,
+ BG_NA_OBJECT_TYPE_DOOR_4 = 183979
+};
+
+class BattleGroundNAScore : public BattleGroundScore
+{
+ public:
+ BattleGroundNAScore() {};
+ virtual ~BattleGroundNAScore() {};
+ //TODO fix me
+};
+
+class BattleGroundNA : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundNA();
+ ~BattleGroundNA();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ void RemovePlayer(Player *plr, uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ bool SetupBattleGround();
+ virtual void ResetBGSubclass();
+ void HandleKillPlayer(Player* player, Player *killer);
+
+ private:
+ uint32 m_TeamKills[2]; // count of kills for each team
+};
+#endif
diff --git a/src/game/BattleGroundRL.cpp b/src/game/BattleGroundRL.cpp
new file mode 100644
index 00000000000..68ba34e06ed
--- /dev/null
+++ b/src/game/BattleGroundRL.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundRL.h"
+#include "Creature.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Language.h"
+
+BattleGroundRL::BattleGroundRL()
+{
+ m_BgObjects.resize(BG_RL_OBJECT_MAX);
+}
+
+BattleGroundRL::~BattleGroundRL()
+{
+
+}
+
+void BattleGroundRL::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+
+ if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
+ {
+ ModifyStartDelayTime(diff);
+
+ if (!(m_Events & 0x01))
+ {
+ m_Events |= 0x01;
+
+ for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++)
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+
+ SetStartDelayTime(START_DELAY1);
+ SendMessageToAll(LANG_ARENA_ONE_MINUTE);
+ }
+ // After 30 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04))
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(LANG_ARENA_THIRTY_SECONDS);
+ }
+ // After 15 seconds, warning is signalled
+ else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08))
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS);
+ }
+ // delay expired (1 minute)
+ else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10))
+ {
+ m_Events |= 0x10;
+
+ for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++)
+ DoorOpen(i);
+
+ SendMessageToAll(LANG_ARENA_BEGUN);
+ SetStatus(STATUS_IN_PROGRESS);
+ SetStartDelayTime(0);
+
+ for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
+ if(Player *plr = objmgr.GetPlayer(itr->first))
+ plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
+ }
+ }
+
+ /*if(GetStatus() == STATUS_IN_PROGRESS)
+ {
+ // update something
+ }*/
+}
+
+void BattleGroundRL::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundRLScore* sc = new BattleGroundRLScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundRL::RemovePlayer(Player *plr, uint64 guid)
+{
+
+}
+
+void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ if(!killer)
+ {
+ sLog.outError("Killer player not found");
+ return;
+ }
+
+ BattleGround::HandleKillPlayer(player, killer);
+
+ uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam());
+
+ ++m_TeamKills[killer_team_index]; // add kills to killer's team
+
+ if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam()))
+ {
+ // all opponents killed
+ EndBattleGround(killer->GetTeam());
+ }
+}
+
+void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ // this is wrong way to implement these things. On official it done by gameobject spell cast.
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ //uint32 SpellId = 0;
+ //uint64 buff_guid = 0;
+ switch(Trigger)
+ {
+ case 4696: // buff trigger?
+ case 4697: // buff trigger?
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+
+ //if(buff_guid)
+ // HandleTriggerBuff(buff_guid,Source);
+}
+
+void BattleGroundRL::ResetBGSubclass()
+{
+ m_TeamKills[BG_TEAM_ALLIANCE] = 0;
+ m_TeamKills[BG_TEAM_HORDE] = 0;
+}
+
+bool BattleGroundRL::SetupBattleGround()
+{
+ // gates
+ if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561f, 1601.938f, 31.60557f, -1.457349f, 0, 0, -0.6658813f, 0.7460576f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648f, 1730.557f, 31.60557f, 1.684245f, 0, 0, 0.7460582f, 0.6658807f, RESPAWN_IMMEDIATELY))
+ {
+ sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+Packet S->C, id 600, SMSG_INIT_WORLD_STATES (706), len 86
+0000: 3C 02 00 00 80 0F 00 00 00 00 00 00 09 00 BA 0B | <...............
+0010: 00 00 01 00 00 00 B9 0B 00 00 02 00 00 00 B8 0B | ................
+0020: 00 00 00 00 00 00 D8 08 00 00 00 00 00 00 D7 08 | ................
+0030: 00 00 00 00 00 00 D6 08 00 00 00 00 00 00 D5 08 | ................
+0040: 00 00 00 00 00 00 D3 08 00 00 00 00 00 00 D4 08 | ................
+0050: 00 00 00 00 00 00 | ......
+*/
diff --git a/src/game/BattleGroundRL.h b/src/game/BattleGroundRL.h
new file mode 100644
index 00000000000..2fdbfc4c83c
--- /dev/null
+++ b/src/game/BattleGroundRL.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef __BATTLEGROUNDRL_H
+#define __BATTLEGROUNDRL_H
+
+class BattleGround;
+
+enum BattleGroundRLObjectTypes
+{
+ BG_RL_OBJECT_DOOR_1 = 0,
+ BG_RL_OBJECT_DOOR_2 = 1,
+ BG_RL_OBJECT_MAX = 2
+};
+
+enum BattleGroundRLObjects
+{
+ BG_RL_OBJECT_TYPE_DOOR_1 = 185918,
+ BG_RL_OBJECT_TYPE_DOOR_2 = 185917
+};
+
+class BattleGroundRLScore : public BattleGroundScore
+{
+ public:
+ BattleGroundRLScore() {};
+ virtual ~BattleGroundRLScore() {};
+ //TODO fix me
+};
+
+class BattleGroundRL : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ BattleGroundRL();
+ ~BattleGroundRL();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ void RemovePlayer(Player *plr, uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ bool SetupBattleGround();
+ virtual void ResetBGSubclass();
+ void HandleKillPlayer(Player* player, Player *killer);
+
+ private:
+ uint32 m_TeamKills[2]; // count of kills for each team
+};
+#endif
diff --git a/src/game/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp
new file mode 100644
index 00000000000..d907b6e3bcb
--- /dev/null
+++ b/src/game/BattleGroundWS.cpp
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Object.h"
+#include "Player.h"
+#include "BattleGround.h"
+#include "BattleGroundWS.h"
+#include "Creature.h"
+#include "GameObject.h"
+#include "Chat.h"
+#include "MapManager.h"
+#include "Language.h"
+
+BattleGroundWS::BattleGroundWS()
+{
+ m_BgObjects.resize(BG_WS_OBJECT_MAX);
+ m_BgCreatures.resize(BG_CREATURES_MAX_WS);
+}
+
+BattleGroundWS::~BattleGroundWS()
+{
+}
+
+void BattleGroundWS::Update(time_t diff)
+{
+ BattleGround::Update(diff);
+
+ // after bg start we get there (once)
+ if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
+ {
+ ModifyStartDelayTime(diff);
+
+ if(!(m_Events & 0x01))
+ {
+ m_Events |= 0x01;
+
+ for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++)
+ {
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+ DoorClose(i);
+ }
+ for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++)
+ SpawnBGObject(i, RESPAWN_ONE_DAY);
+
+ SetStartDelayTime(START_DELAY0);
+ }
+ // After 1 minute, warning is signalled
+ else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04))
+ {
+ m_Events |= 0x04;
+ SendMessageToAll(GetMangosString(LANG_BG_WS_ONE_MINUTE));
+ }
+ // After 1,5 minute, warning is signalled
+ else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08))
+ {
+ m_Events |= 0x08;
+ SendMessageToAll(GetMangosString(LANG_BG_WS_HALF_MINUTE));
+ }
+ // After 2 minutes, gates OPEN ! x)
+ else if(GetStartDelayTime() < 0 && !(m_Events & 0x10))
+ {
+ m_Events |= 0x10;
+ for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; i++)
+ DoorOpen(i);
+ for(uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; i++)
+ DoorOpen(i);
+
+ SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY);
+ SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY);
+ SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY);
+ SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY);
+
+ for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++)
+ SpawnBGObject(i, RESPAWN_IMMEDIATELY);
+
+ SendMessageToAll(GetMangosString(LANG_BG_WS_BEGIN));
+
+ PlaySoundToAll(SOUND_BG_START);
+ SetStatus(STATUS_IN_PROGRESS);
+
+ 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(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN)
+ {
+ m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff;
+
+ if(m_FlagsTimer[BG_TEAM_ALLIANCE] < 0)
+ {
+ m_FlagsTimer[BG_TEAM_ALLIANCE] = 0;
+ RespawnFlag(ALLIANCE, true);
+ }
+ }
+ if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND)
+ {
+ m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff;
+
+ if(m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0)
+ {
+ m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0;
+ RespawnFlagAfterDrop(ALLIANCE);
+ }
+ }
+ if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN)
+ {
+ m_FlagsTimer[BG_TEAM_HORDE] -= diff;
+
+ if(m_FlagsTimer[BG_TEAM_HORDE] < 0)
+ {
+ m_FlagsTimer[BG_TEAM_HORDE] = 0;
+ RespawnFlag(HORDE, true);
+ }
+ }
+ if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND)
+ {
+ m_FlagsDropTimer[BG_TEAM_HORDE] -= diff;
+
+ if(m_FlagsDropTimer[BG_TEAM_HORDE] < 0)
+ {
+ m_FlagsDropTimer[BG_TEAM_HORDE] = 0;
+ RespawnFlagAfterDrop(HORDE);
+ }
+ }
+ }
+}
+
+void BattleGroundWS::AddPlayer(Player *plr)
+{
+ BattleGround::AddPlayer(plr);
+ //create score and add it to map, default values are set in constructor
+ BattleGroundWGScore* sc = new BattleGroundWGScore;
+
+ m_PlayerScores[plr->GetGUID()] = sc;
+}
+
+void BattleGroundWS::RespawnFlag(uint32 Team, bool captured)
+{
+ if(Team == ALLIANCE)
+ {
+ sLog.outDebug("Respawn Alliance flag");
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE;
+ }
+ else
+ {
+ sLog.outDebug("Respawn Horde flag");
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE;
+ }
+
+ if(captured)
+ {
+ //when map_update will be allowed for battlegrounds this code will be useless
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY);
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY);
+ SendMessageToAll(GetMangosString(LANG_BG_WS_F_PLACED));
+ PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); // flag respawned sound...
+ }
+}
+
+void BattleGroundWS::RespawnFlagAfterDrop(uint32 team)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ RespawnFlag(team,false);
+ if(team == ALLIANCE)
+ {
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY);
+ SendMessageToAll(GetMangosString(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED));
+ }
+ else
+ {
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY);
+ SendMessageToAll(GetMangosString(LANG_BG_WS_HORDE_FLAG_RESPAWNED));
+ }
+
+ PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED);
+
+ GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID(team));
+ if(obj)
+ obj->Delete();
+ else
+ sLog.outError("unknown droped flag bg, guid: %u",GetDroppedFlagGUID(team));
+
+ SetDroppedFlagGUID(0,team);
+}
+
+void BattleGroundWS::EventPlayerCapturedFlag(Player *Source)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ uint8 type = 0;
+ uint32 winner = 0;
+ const char *message = "";
+
+ //TODO FIX reputation and honor gains for low level players!
+
+ Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ if (!this->IsHordeFlagPickedup())
+ return;
+ SetHordeFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time
+ // horde flag in base (but not respawned yet)
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN;
+ // Drop Horde Flag from Player
+ Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG);
+ message = GetMangosString(LANG_BG_WS_CAPTURED_HF);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ if(GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE)
+ AddPoint(ALLIANCE, 1);
+ PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE);
+ RewardReputationToTeam(890, 35, ALLIANCE); // +35 reputation
+ RewardHonorToTeam(40, ALLIANCE); // +40 bonushonor
+ }
+ else
+ {
+ if (!this->IsAllianceFlagPickedup())
+ return;
+ SetAllianceFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time
+ // alliance flag in base (but not respawned yet)
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN;
+ // Drop Alliance Flag from Player
+ Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG);
+ message = GetMangosString(LANG_BG_WS_CAPTURED_AF);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ if(GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE)
+ AddPoint(HORDE, 1);
+ PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE);
+ RewardReputationToTeam(889, 35, HORDE); // +35 reputation
+ RewardHonorToTeam(40, HORDE); // +40 bonushonor
+ }
+
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME);
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME);
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+
+ UpdateFlagState(Source->GetTeam(), 1); // flag state none
+ UpdateTeamScore(Source->GetTeam());
+ // only flag capture should be updated
+ UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures...
+
+ if(GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE)
+ winner = ALLIANCE;
+
+ if(GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE)
+ winner = HORDE;
+
+ if(winner)
+ {
+ UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 0);
+ UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 0);
+ UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, 1);
+ UpdateWorldState(BG_WS_FLAG_STATE_HORDE, 1);
+
+ EndBattleGround(winner);
+ }
+ else
+ {
+ m_FlagsTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_RESPAWN_TIME;
+ }
+}
+
+void BattleGroundWS::EventPlayerDroppedFlag(Player *Source)
+{
+ // Drop allowed in any BG state
+
+ const char *message = "";
+ uint8 type = 0;
+ bool set = false;
+
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ if(!this->IsHordeFlagPickedup())
+ return;
+ if(GetHordeFlagPickerGUID() == Source->GetGUID())
+ {
+ SetHordeFlagPicker(0);
+ Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG);
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND;
+ message = GetMangosString(LANG_BG_WS_DROPPED_HF);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true);
+ set = true;
+ }
+ }
+ else
+ {
+ if(!this->IsAllianceFlagPickedup())
+ return;
+ if(GetAllianceFlagPickerGUID() == Source->GetGUID())
+ {
+ SetAllianceFlagPicker(0);
+ Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG);
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND;
+ message = GetMangosString(LANG_BG_WS_DROPPED_AF);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true);
+ set = true;
+ }
+ }
+
+ if (set)
+ {
+ Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true);
+ UpdateFlagState(Source->GetTeam(), 1);
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+
+ if(Source->GetTeam() == ALLIANCE)
+ UpdateWorldState(BG_WS_FLAG_UNK_HORDE, uint32(-1));
+ else
+ UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, uint32(-1));
+
+ m_FlagsDropTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_DROP_TIME;
+ }
+}
+
+void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ const char *message;
+ uint8 type = 0;
+
+ //alliance flag picked up from base
+ if(Source->GetTeam() == HORDE && this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_BASE
+ && this->m_BgObjects[BG_WS_OBJECT_A_FLAG] == target_obj->GetGUID())
+ {
+ message = GetMangosString(LANG_BG_WS_PICKEDUP_AF);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP);
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY);
+ SetAllianceFlagPicker(Source->GetGUID());
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER;
+ //update world state to show correct flag carrier
+ UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER);
+ UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1);
+ Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true);
+ }
+
+ //horde flag picked up from base
+ if (Source->GetTeam() == ALLIANCE && this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_BASE
+ && this->m_BgObjects[BG_WS_OBJECT_H_FLAG] == target_obj->GetGUID())
+ {
+ message = GetMangosString(LANG_BG_WS_PICKEDUP_HF);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP);
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY);
+ SetHordeFlagPicker(Source->GetGUID());
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER;
+ //update world state to show correct flag carrier
+ UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER);
+ UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1);
+ Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true);
+ }
+
+ //Alliance flag on ground(not in base) (returned or picked up again from ground!)
+ if(this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10))
+ {
+ if(Source->GetTeam() == ALLIANCE)
+ {
+ message = GetMangosString(LANG_BG_WS_RETURNED_AF);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ UpdateFlagState(HORDE, BG_WS_FLAG_STATE_WAIT_RESPAWN);
+ RespawnFlag(ALLIANCE, false);
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY);
+ PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED);
+ UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1);
+ }
+ else
+ {
+ message = GetMangosString(LANG_BG_WS_PICKEDUP_AF);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP);
+ SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY);
+ SetAllianceFlagPicker(Source->GetGUID());
+ Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true);
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER;
+ UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER);
+ UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1);
+ }
+ //called in HandleGameObjectUseOpcode:
+ //target_obj->Delete();
+ }
+
+ //Horde flag on ground(not in base) (returned or picked up again)
+ if(this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10))
+ {
+ if(Source->GetTeam() == HORDE)
+ {
+ message = GetMangosString(LANG_BG_WS_RETURNED_HF);
+ type = CHAT_MSG_BG_SYSTEM_HORDE;
+ UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_WAIT_RESPAWN);
+ RespawnFlag(HORDE, false);
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY);
+ PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED);
+ UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1);
+ }
+ else
+ {
+ message = GetMangosString(LANG_BG_WS_PICKEDUP_HF);
+ type = CHAT_MSG_BG_SYSTEM_ALLIANCE;
+ PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP);
+ SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY);
+ SetHordeFlagPicker(Source->GetGUID());
+ Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true);
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER;
+ UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER);
+ UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1);
+ }
+ //called in HandleGameObjectUseOpcode:
+ //target_obj->Delete();
+ }
+
+ if (!type)
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL);
+ SendPacketToAll(&data);
+ Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+}
+
+void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid)
+{
+ // sometimes flag aura not removed :(
+ if(IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid)
+ {
+ if(!plr)
+ {
+ sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!");
+ this->SetAllianceFlagPicker(0);
+ this->RespawnFlag(ALLIANCE, false);
+ }
+ else
+ this->EventPlayerDroppedFlag(plr);
+ }
+ if(IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid)
+ {
+ if(!plr)
+ {
+ sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!");
+ this->SetHordeFlagPicker(0);
+ this->RespawnFlag(HORDE, false);
+ }
+ else
+ this->EventPlayerDroppedFlag(plr);
+ }
+}
+
+void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value)
+{
+ if(team == ALLIANCE)
+ UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, value);
+ else
+ UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value);
+}
+
+void BattleGroundWS::UpdateTeamScore(uint32 team)
+{
+ if(team == ALLIANCE)
+ UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, GetTeamScore(team));
+ else
+ UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, GetTeamScore(team));
+}
+
+void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger)
+{
+ // this is wrong way to implement these things. On official it done by gameobject spell cast.
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ //uint32 SpellId = 0;
+ //uint64 buff_guid = 0;
+ switch(Trigger)
+ {
+ case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update().
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_1];
+ break;
+ case 3687: // Horde elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update().
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_2];
+ break;
+ case 3706: // Alliance elixir of regeneration spawn
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_1];
+ break;
+ case 3708: // Horde elixir of regeneration spawn
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_2];
+ break;
+ case 3707: // Alliance elixir of berserk spawn
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_1];
+ break;
+ case 3709: // Horde elixir of berserk spawn
+ //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_2];
+ break;
+ case 3646: // Alliance Flag spawn
+ if(m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE])
+ if(GetHordeFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source);
+ break;
+ case 3647: // Horde Flag spawn
+ if(m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE])
+ if(GetAllianceFlagPickerGUID() == Source->GetGUID())
+ EventPlayerCapturedFlag(Source);
+ break;
+ case 3649: // unk1
+ case 3688: // unk2
+ case 4628: // unk3
+ case 4629: // unk4
+ break;
+ default:
+ sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
+ break;
+ }
+
+ //if(buff_guid)
+ // HandleTriggerBuff(buff_guid,Source);
+}
+
+bool BattleGroundWS::SetupBattleGround()
+{
+ // flags
+ if( !AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000)
+ || !AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, BG_WS_FLAG_RESPAWN_TIME/1000)
+ // buffs
+ || !AddObject(BG_WS_OBJECT_SPEEDBUFF_1, BG_OBJECTID_SPEEDBUFF_ENTRY, 1449.93f, 1470.71f, 342.6346f, -1.64061f, 0, 0, 0.7313537f, -0.6819983f, BUFF_RESPAWN_TIME)
+ || !AddObject(BG_WS_OBJECT_SPEEDBUFF_2, BG_OBJECTID_SPEEDBUFF_ENTRY, 1005.171f, 1447.946f, 335.9032f, 1.64061f, 0, 0, 0.7313537f, 0.6819984f, BUFF_RESPAWN_TIME)
+ || !AddObject(BG_WS_OBJECT_REGENBUFF_1, BG_OBJECTID_REGENBUFF_ENTRY, 1317.506f, 1550.851f, 313.2344f, -0.2617996f, 0, 0, 0.1305263f, -0.9914448f, BUFF_RESPAWN_TIME)
+ || !AddObject(BG_WS_OBJECT_REGENBUFF_2, BG_OBJECTID_REGENBUFF_ENTRY, 1110.451f, 1353.656f, 316.5181f, -0.6806787f, 0, 0, 0.333807f, -0.9426414f, BUFF_RESPAWN_TIME)
+ || !AddObject(BG_WS_OBJECT_BERSERKBUFF_1, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1320.09f, 1378.79f, 314.7532f, 1.186824f, 0, 0, 0.5591929f, 0.8290376f, BUFF_RESPAWN_TIME)
+ || !AddObject(BG_WS_OBJECT_BERSERKBUFF_2, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1139.688f, 1560.288f, 306.8432f, -2.443461f, 0, 0, 0.9396926f, -0.3420201f, BUFF_RESPAWN_TIME)
+ // alliance gates
+ || !AddObject(BG_WS_OBJECT_DOOR_A_1, BG_OBJECT_DOOR_A_1_WS_ENTRY, 1503.335f, 1493.466f, 352.1888f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_A_2, BG_OBJECT_DOOR_A_2_WS_ENTRY, 1492.478f, 1457.912f, 342.9689f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_A_3, BG_OBJECT_DOOR_A_3_WS_ENTRY, 1468.503f, 1494.357f, 351.8618f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_A_4, BG_OBJECT_DOOR_A_4_WS_ENTRY, 1471.555f, 1458.778f, 362.6332f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_A_5, BG_OBJECT_DOOR_A_5_WS_ENTRY, 1492.347f, 1458.34f, 342.3712f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_A_6, BG_OBJECT_DOOR_A_6_WS_ENTRY, 1503.466f, 1493.367f, 351.7352f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY)
+ // horde gates
+ || !AddObject(BG_WS_OBJECT_DOOR_H_1, BG_OBJECT_DOOR_H_1_WS_ENTRY, 949.1663f, 1423.772f, 345.6241f, -0.5756807f, -0.01673368f, -0.004956111f, -0.2839723f, 0.9586737f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_H_2, BG_OBJECT_DOOR_H_2_WS_ENTRY, 953.0507f, 1459.842f, 340.6526f, -1.99662f, -0.1971825f, 0.1575096f, -0.8239487f, 0.5073641f, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_H_3, BG_OBJECT_DOOR_H_3_WS_ENTRY, 949.9523f, 1422.751f, 344.9273f, 0.0f, 0, 0, 0, 1, RESPAWN_IMMEDIATELY)
+ || !AddObject(BG_WS_OBJECT_DOOR_H_4, BG_OBJECT_DOOR_H_4_WS_ENTRY, 950.7952f, 1459.583f, 342.1523f, 0.05235988f, 0, 0, 0.02617695f, 0.9996573f, RESPAWN_IMMEDIATELY)
+ )
+ {
+ sLog.outErrorDb("BatteGroundWS: Failed to spawn some object BattleGround not created!");
+ return false;
+ }
+
+ WorldSafeLocsEntry const *sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE);
+ if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE))
+ {
+ sLog.outErrorDb("BatteGroundWS: Failed to spawn Alliance spirit guide! BattleGround not created!");
+ return false;
+ }
+
+ sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE);
+ if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE))
+ {
+ sLog.outErrorDb("BatteGroundWS: Failed to spawn Horde spirit guide! BattleGround not created!");
+ return false;
+ }
+
+ sLog.outDebug("BatteGroundWS: BG objects and spirit guides spawned");
+
+ return true;
+}
+
+void BattleGroundWS::ResetBGSubclass()
+{
+ m_FlagKeepers[BG_TEAM_ALLIANCE] = 0;
+ m_FlagKeepers[BG_TEAM_HORDE] = 0;
+ m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0;
+ m_DroppedFlagGUID[BG_TEAM_HORDE] = 0;
+ m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE;
+ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE;
+ m_TeamScores[BG_TEAM_ALLIANCE] = 0;
+ m_TeamScores[BG_TEAM_HORDE] = 0;
+
+ /* Spirit nodes is static at this BG and then not required deleting at BG reset.
+ if(m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE])
+ DelCreature(WS_SPIRIT_MAIN_ALLIANCE);
+
+ if(m_BgCreatures[WS_SPIRIT_MAIN_HORDE])
+ DelCreature(WS_SPIRIT_MAIN_HORDE);
+ */
+}
+
+void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer)
+{
+ if(GetStatus() != STATUS_IN_PROGRESS)
+ return;
+
+ EventPlayerDroppedFlag(player);
+
+ BattleGround::HandleKillPlayer(player, killer);
+}
+
+void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
+{
+
+ std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
+
+ if(itr == m_PlayerScores.end()) // player not found
+ return;
+
+ switch(type)
+ {
+ case SCORE_FLAG_CAPTURES: // flags captured
+ ((BattleGroundWGScore*)itr->second)->FlagCaptures += value;
+ break;
+ case SCORE_FLAG_RETURNS: // flags returned
+ ((BattleGroundWGScore*)itr->second)->FlagReturns += value;
+ break;
+ default:
+ BattleGround::UpdatePlayerScore(Source, type, value);
+ break;
+ }
+}
+
+void BattleGroundWS::FillInitialWorldStates(WorldPacket& data)
+{
+ data << uint32(BG_WS_FLAG_CAPTURES_ALLIANCE) << uint32(GetTeamScore(ALLIANCE));
+ data << uint32(BG_WS_FLAG_CAPTURES_HORDE) << uint32(GetTeamScore(HORDE));
+
+ if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND)
+ data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(-1);
+ else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER)
+ data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(1);
+ else
+ data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(0);
+
+ if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND)
+ data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(-1);
+ else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER)
+ data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(1);
+ else
+ data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(0);
+
+ data << uint32(BG_WS_FLAG_CAPTURES_MAX) << uint32(BG_WS_MAX_TEAM_SCORE);
+
+ if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER)
+ data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(2);
+ else
+ data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(1);
+
+ if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER)
+ data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(2);
+ else
+ data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(1);
+
+}
diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h
new file mode 100644
index 00000000000..d9f2b7f6142
--- /dev/null
+++ b/src/game/BattleGroundWS.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __BATTLEGROUNDWS_H
+#define __BATTLEGROUNDWS_H
+
+#include "BattleGround.h"
+
+#define BG_WS_MAX_TEAM_SCORE 3
+#define BG_WS_FLAG_RESPAWN_TIME 23000
+#define BG_WS_FLAG_DROP_TIME 10000
+
+enum BG_WS_Sound
+{
+ BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE = 8173,
+ BG_WS_SOUND_FLAG_CAPTURED_HORDE = 8213,
+ BG_WS_SOUND_FLAG_PLACED = 8232,
+ BG_WS_SOUND_FLAG_RETURNED = 8192,
+ BG_WS_SOUND_HORDE_FLAG_PICKED_UP = 8212,
+ BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP = 8174,
+ BG_WS_SOUND_FLAGS_RESPAWNED = 8232
+};
+
+enum BG_WS_SpellId
+{
+ BG_WS_SPELL_WARSONG_FLAG = 23333,
+ BG_WS_SPELL_WARSONG_FLAG_DROPPED = 23334,
+ BG_WS_SPELL_SILVERWING_FLAG = 23335,
+ BG_WS_SPELL_SILVERWING_FLAG_DROPPED = 23336
+};
+
+enum BG_WS_WorldStates
+{
+ BG_WS_FLAG_UNK_ALLIANCE = 1545,
+ BG_WS_FLAG_UNK_HORDE = 1546,
+// FLAG_UNK = 1547,
+ BG_WS_FLAG_CAPTURES_ALLIANCE = 1581,
+ BG_WS_FLAG_CAPTURES_HORDE = 1582,
+ BG_WS_FLAG_CAPTURES_MAX = 1601,
+ BG_WS_FLAG_STATE_HORDE = 2338,
+ BG_WS_FLAG_STATE_ALLIANCE = 2339
+};
+
+enum BG_WS_ObjectTypes
+{
+ BG_WS_OBJECT_DOOR_A_1 = 0,
+ BG_WS_OBJECT_DOOR_A_2 = 1,
+ BG_WS_OBJECT_DOOR_A_3 = 2,
+ BG_WS_OBJECT_DOOR_A_4 = 3,
+ BG_WS_OBJECT_DOOR_A_5 = 4,
+ BG_WS_OBJECT_DOOR_A_6 = 5,
+ BG_WS_OBJECT_DOOR_H_1 = 6,
+ BG_WS_OBJECT_DOOR_H_2 = 7,
+ BG_WS_OBJECT_DOOR_H_3 = 8,
+ BG_WS_OBJECT_DOOR_H_4 = 9,
+ BG_WS_OBJECT_A_FLAG = 10,
+ BG_WS_OBJECT_H_FLAG = 11,
+ BG_WS_OBJECT_SPEEDBUFF_1 = 12,
+ BG_WS_OBJECT_SPEEDBUFF_2 = 13,
+ BG_WS_OBJECT_REGENBUFF_1 = 14,
+ BG_WS_OBJECT_REGENBUFF_2 = 15,
+ BG_WS_OBJECT_BERSERKBUFF_1 = 16,
+ BG_WS_OBJECT_BERSERKBUFF_2 = 17,
+ BG_WS_OBJECT_MAX = 18
+};
+
+enum BG_WS_ObjectEntry
+{
+ BG_OBJECT_DOOR_A_1_WS_ENTRY = 179918,
+ BG_OBJECT_DOOR_A_2_WS_ENTRY = 179919,
+ BG_OBJECT_DOOR_A_3_WS_ENTRY = 179920,
+ BG_OBJECT_DOOR_A_4_WS_ENTRY = 179921,
+ BG_OBJECT_DOOR_A_5_WS_ENTRY = 180322,
+ BG_OBJECT_DOOR_A_6_WS_ENTRY = 180322,
+ BG_OBJECT_DOOR_H_1_WS_ENTRY = 179916,
+ BG_OBJECT_DOOR_H_2_WS_ENTRY = 179917,
+ BG_OBJECT_DOOR_H_3_WS_ENTRY = 180322,
+ BG_OBJECT_DOOR_H_4_WS_ENTRY = 180322,
+ BG_OBJECT_A_FLAG_WS_ENTRY = 179830,
+ BG_OBJECT_H_FLAG_WS_ENTRY = 179831
+};
+
+enum BG_WS_FlagState
+{
+ BG_WS_FLAG_STATE_ON_BASE = 0,
+ BG_WS_FLAG_STATE_WAIT_RESPAWN = 1,
+ BG_WS_FLAG_STATE_ON_PLAYER = 2,
+ BG_WS_FLAG_STATE_ON_GROUND = 3
+};
+
+enum BG_WS_Graveyards
+{
+ WS_GRAVEYARD_MAIN_ALLIANCE = 771,
+ WS_GRAVEYARD_MAIN_HORDE = 772
+};
+
+enum BG_WS_CreatureTypes
+{
+ WS_SPIRIT_MAIN_ALLIANCE = 0,
+ WS_SPIRIT_MAIN_HORDE = 1,
+
+ BG_CREATURES_MAX_WS = 2
+};
+
+class BattleGroundWGScore : public BattleGroundScore
+{
+ public:
+ BattleGroundWGScore() : FlagCaptures(0), FlagReturns(0) {};
+ virtual ~BattleGroundWGScore() {};
+ uint32 FlagCaptures;
+ uint32 FlagReturns;
+};
+
+class BattleGroundWS : public BattleGround
+{
+ friend class BattleGroundMgr;
+
+ public:
+ /* Construction */
+ BattleGroundWS();
+ ~BattleGroundWS();
+ void Update(time_t diff);
+
+ /* inherited from BattlegroundClass */
+ virtual void AddPlayer(Player *plr);
+
+ /* BG Flags */
+ uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; }
+ uint64 GetHordeFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_HORDE]; }
+ void SetAllianceFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_ALLIANCE] = guid; }
+ void SetHordeFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_HORDE] = guid; }
+ bool IsAllianceFlagPickedup() const { return m_FlagKeepers[BG_TEAM_ALLIANCE] != 0; }
+ bool IsHordeFlagPickedup() const { return m_FlagKeepers[BG_TEAM_HORDE] != 0; }
+ void RespawnFlag(uint32 Team, bool captured);
+ void RespawnFlagAfterDrop(uint32 Team);
+ uint8 GetFlagState(uint32 team) { return m_FlagState[GetTeamIndexByTeamId(team)]; }
+
+ /* Battleground Events */
+ virtual void EventPlayerDroppedFlag(Player *Source);
+ virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj);
+ virtual void EventPlayerCapturedFlag(Player *Source);
+
+ void RemovePlayer(Player *plr, uint64 guid);
+ void HandleAreaTrigger(Player *Source, uint32 Trigger);
+ void HandleKillPlayer(Player *player, Player *killer);
+ bool SetupBattleGround();
+ virtual void ResetBGSubclass();
+
+ void UpdateFlagState(uint32 team, uint32 value);
+ void UpdateTeamScore(uint32 team);
+ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+ void SetDroppedFlagGUID(uint64 guid, uint32 TeamID) { m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)] = guid;}
+ uint64 GetDroppedFlagGUID(uint32 TeamID) { return m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)];}
+ virtual void FillInitialWorldStates(WorldPacket& data);
+
+ /* Scorekeeping */
+ uint32 GetTeamScore(uint32 TeamID) const { return m_TeamScores[GetTeamIndexByTeamId(TeamID)]; }
+ 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];
+};
+#endif
diff --git a/src/game/Cell.h b/src/game/Cell.h
new file mode 100644
index 00000000000..873debabb14
--- /dev/null
+++ b/src/game/Cell.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_CELL_H
+#define MANGOS_CELL_H
+
+#include "GameSystem/TypeContainer.h"
+#include "GameSystem/TypeContainerVisitor.h"
+#include "GridDefines.h"
+#include <cmath>
+
+class Map;
+
+enum District
+{
+ UPPER_DISTRICT = 1,
+ LOWER_DISTRICT = 1 << 1,
+ LEFT_DISTRICT = 1 << 2,
+ RIGHT_DISTRICT = 1 << 3,
+ CENTER_DISTRICT = 1 << 4,
+ UPPER_LEFT_DISTRICT = (UPPER_DISTRICT | LEFT_DISTRICT),
+ UPPER_RIGHT_DISTRICT = (UPPER_DISTRICT | RIGHT_DISTRICT),
+ LOWER_LEFT_DISTRICT = (LOWER_DISTRICT | LEFT_DISTRICT),
+ LOWER_RIGHT_DISTRICT = (LOWER_DISTRICT | RIGHT_DISTRICT),
+ ALL_DISTRICT = (UPPER_DISTRICT | LOWER_DISTRICT | LEFT_DISTRICT | RIGHT_DISTRICT | CENTER_DISTRICT)
+};
+
+template<class T> struct CellLock;
+
+struct MANGOS_DLL_DECL Cell
+{
+ Cell() { data.All = 0; }
+ Cell(const Cell &cell) { data.All = cell.data.All; }
+ explicit Cell(CellPair const& p);
+
+ void operator|=(Cell &cell)
+ {
+ data.Part.reserved = 0;
+ cell.data.Part.reserved = 0;
+ uint32 x, y, old_x, old_y;
+ Compute(x, y);
+ cell.Compute(old_x, old_y);
+
+ if( std::abs(int(x-old_x)) > 1 || std::abs(int(y-old_y)) > 1)
+ {
+ data.Part.reserved = ALL_DISTRICT;
+ cell.data.Part.reserved = ALL_DISTRICT;
+ return;
+ }
+
+ if( x < old_x )
+ {
+ data.Part.reserved |= LEFT_DISTRICT;
+ cell.data.Part.reserved |= RIGHT_DISTRICT;
+ }
+ else if( old_x < x )
+ {
+ data.Part.reserved |= RIGHT_DISTRICT;
+ cell.data.Part.reserved |= LEFT_DISTRICT;
+ }
+ if( y < old_y )
+ {
+ data.Part.reserved |= UPPER_DISTRICT;
+ cell.data.Part.reserved |= LOWER_DISTRICT;
+ }
+ else if( old_y < y )
+ {
+ data.Part.reserved |= LOWER_DISTRICT;
+ cell.data.Part.reserved |= UPPER_DISTRICT;
+ }
+ }
+
+ void Compute(uint32 &x, uint32 &y) const
+ {
+ x = data.Part.grid_x*MAX_NUMBER_OF_CELLS + data.Part.cell_x;
+ y = data.Part.grid_y*MAX_NUMBER_OF_CELLS + data.Part.cell_y;
+ }
+
+ inline bool DiffCell(const Cell &cell) const
+ {
+ return( data.Part.cell_x != cell.data.Part.cell_x ||
+ data.Part.cell_y != cell.data.Part.cell_y );
+ }
+
+ inline bool DiffGrid(const Cell &cell) const
+ {
+ return( data.Part.grid_x != cell.data.Part.grid_x ||
+ data.Part.grid_y != cell.data.Part.grid_y );
+ }
+
+ uint32 CellX() const { return data.Part.cell_x; }
+ uint32 CellY() const { return data.Part.cell_y; }
+ uint32 GridX() const { return data.Part.grid_x; }
+ uint32 GridY() const { return data.Part.grid_y; }
+ bool NoCreate() const { return data.Part.nocreate; }
+ void SetNoCreate() { data.Part.nocreate = 1; }
+
+ CellPair cellPair() const
+ {
+ return CellPair(
+ data.Part.grid_x*MAX_NUMBER_OF_CELLS+data.Part.cell_x,
+ data.Part.grid_y*MAX_NUMBER_OF_CELLS+data.Part.cell_y);
+ }
+
+ Cell& operator=(const Cell &cell)
+ {
+ this->data.All = cell.data.All;
+ return *this;
+ }
+
+ bool operator==(const Cell &cell) const { return (data.All == cell.data.All); }
+ bool operator!=(const Cell &cell) const { return !operator==(cell); }
+ union
+ {
+ struct
+ {
+ unsigned grid_x : 6;
+ unsigned grid_y : 6;
+ unsigned cell_x : 4;
+ unsigned cell_y : 4;
+ unsigned nocreate : 1;
+ unsigned reserved : 11;
+ } Part;
+ uint32 All;
+ } data;
+
+ template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &, TypeContainerVisitor<T, CONTAINER> &visitor, Map &) const;
+};
+
+template<class T>
+struct MANGOS_DLL_DECL CellLock
+{
+ const Cell& i_cell;
+ const CellPair &i_cellPair;
+ CellLock(const Cell &c, const CellPair &p) : i_cell(c), i_cellPair(p) {}
+ CellLock(const CellLock<T> &cell) : i_cell(cell.i_cell), i_cellPair(cell.i_cellPair) {}
+ const Cell* operator->(void) const { return &i_cell; }
+ const Cell* operator->(void) { return &i_cell; }
+ operator const Cell &(void) const { return i_cell; }
+ CellLock<T>& operator=(const CellLock<T> &cell)
+ {
+ this->~CellLock();
+ new (this) CellLock<T>(cell);
+ return *this;
+ }
+};
+#endif
diff --git a/src/game/CellImpl.h b/src/game/CellImpl.h
new file mode 100644
index 00000000000..72c41e4b3c4
--- /dev/null
+++ b/src/game/CellImpl.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_CELLIMPL_H
+#define MANGOS_CELLIMPL_H
+
+#include "Cell.h"
+#include "Map.h"
+#include <cmath>
+
+inline Cell::Cell(CellPair const& p)
+{
+ data.Part.grid_x = p.x_coord / MAX_NUMBER_OF_CELLS;
+ data.Part.grid_y = p.y_coord / MAX_NUMBER_OF_CELLS;
+ data.Part.cell_x = p.x_coord % MAX_NUMBER_OF_CELLS;
+ data.Part.cell_y = p.y_coord % MAX_NUMBER_OF_CELLS;
+ data.Part.nocreate = 0;
+ data.Part.reserved = 0;
+}
+
+template<class LOCK_TYPE,class T, class CONTAINER>
+inline void
+Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m) const
+{
+ const CellPair &standing_cell = l.i_cellPair;
+ if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
+ return;
+
+ uint16 district = (District)this->data.Part.reserved;
+ if(district == CENTER_DISTRICT)
+ {
+ m.Visit(l, visitor);
+ return;
+ }
+
+ // set up the cell range based on the district
+ // the overloaded operators handle range checking
+ CellPair begin_cell = standing_cell;
+ CellPair end_cell = standing_cell;
+
+ switch( district )
+ {
+ case ALL_DISTRICT:
+ {
+ begin_cell << 1; begin_cell -= 1; // upper left
+ end_cell >> 1; end_cell += 1; // lower right
+ break;
+ }
+ case UPPER_LEFT_DISTRICT:
+ {
+ begin_cell << 1; begin_cell -= 1; // upper left
+ break;
+ }
+ case UPPER_RIGHT_DISTRICT:
+ {
+ begin_cell -= 1; // up
+ end_cell >> 1; // right
+ break;
+ }
+ case LOWER_LEFT_DISTRICT:
+ {
+ begin_cell << 1; // left
+ end_cell += 1; // down
+ break;
+ }
+ case LOWER_RIGHT_DISTRICT:
+ {
+ end_cell >> 1; end_cell += 1; // lower right
+ break;
+ }
+ case LEFT_DISTRICT:
+ {
+ begin_cell -= 1; // up
+ end_cell >> 1; end_cell += 1; // lower right
+ break;
+ }
+ case RIGHT_DISTRICT:
+ {
+ begin_cell << 1; begin_cell -= 1; // upper left
+ end_cell += 1; // down
+ break;
+ }
+ case UPPER_DISTRICT:
+ {
+ begin_cell << 1; begin_cell -= 1; // upper left
+ end_cell >> 1; // right
+ break;
+ }
+ case LOWER_DISTRICT:
+ {
+ begin_cell << 1; // left
+ end_cell >> 1; end_cell += 1; // lower right
+ break;
+ }
+ default:
+ {
+ assert( false );
+ break;
+ }
+ }
+
+ // loop the cell range
+ for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++)
+ {
+ for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++)
+ {
+ CellPair cell_pair(x,y);
+ Cell r_zone(cell_pair);
+ r_zone.data.Part.nocreate = l->data.Part.nocreate;
+ CellLock<LOCK_TYPE> lock(r_zone, cell_pair);
+ m.Visit(lock, visitor);
+ }
+ }
+}
+#endif
diff --git a/src/game/Channel.cpp b/src/game/Channel.cpp
new file mode 100644
index 00000000000..0fff8c7854b
--- /dev/null
+++ b/src/game/Channel.cpp
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Channel.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include "SocialMgr.h"
+
+Channel::Channel(std::string name, uint32 channel_id)
+: m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0)
+{
+ // set special flags if built-in channel
+ ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id);
+ if(ch) // it's built-in channel
+ {
+ channel_id = ch->ChannelID; // built-in channel
+ m_announce = false; // no join/leave announces
+
+ m_flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels
+
+ if(ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel
+ m_flags |= CHANNEL_FLAG_TRADE;
+
+ if(ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels
+ m_flags |= CHANNEL_FLAG_CITY;
+
+ if(ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel
+ m_flags |= CHANNEL_FLAG_LFG;
+ else // for all other channels
+ m_flags |= CHANNEL_FLAG_NOT_LFG;
+ }
+ else // it's custom channel
+ {
+ m_flags |= CHANNEL_FLAG_CUSTOM;
+ }
+}
+
+void Channel::Join(uint64 p, const char *pass)
+{
+ WorldPacket data;
+ if(IsOn(p))
+ {
+ if(!IsConstant()) // non send error message for built-in channels
+ {
+ MakePlayerAlreadyMember(&data, p);
+ SendToOne(&data, p);
+ }
+ return;
+ }
+
+ if(IsBanned(p))
+ {
+ MakeBanned(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(m_password.length() > 0 && strcmp(pass, m_password.c_str()))
+ {
+ MakeWrongPassword(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ Player *plr = objmgr.GetPlayer(p);
+
+ if(plr)
+ {
+ if(HasFlag(CHANNEL_FLAG_LFG) &&
+ sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER &&
+ (plr->GetGroup() || plr->m_lookingForGroup.Empty()) )
+ {
+ MakeNotInLfg(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(plr->GetGuildId() && (GetFlags() == 0x38))
+ return;
+
+ plr->JoinedChannel(this);
+ }
+
+ if(m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL) ))
+ {
+ MakeJoined(&data, p);
+ SendToAll(&data);
+ }
+
+ data.clear();
+
+ PlayerInfo pinfo;
+ pinfo.player = p;
+ pinfo.flags = 0;
+ players[p] = pinfo;
+
+ MakeYouJoined(&data);
+ SendToOne(&data, p);
+
+ JoinNotify(p);
+
+ // if no owner first logged will become
+ if(!IsConstant() && !m_ownerGUID)
+ {
+ SetOwner(p, (players.size() > 1 ? true : false));
+ players[p].SetModerator(true);
+ }
+}
+
+void Channel::Leave(uint64 p, bool send)
+{
+ if(!IsOn(p))
+ {
+ if(send)
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ }
+ else
+ {
+ Player *plr = objmgr.GetPlayer(p);
+
+ if(send)
+ {
+ WorldPacket data;
+ MakeYouLeft(&data);
+ SendToOne(&data, p);
+ if(plr)
+ plr->LeftChannel(this);
+ data.clear();
+ }
+
+ bool changeowner = players[p].IsOwner();
+
+ players.erase(p);
+ if(m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL) ))
+ {
+ WorldPacket data;
+ MakeLeft(&data, p);
+ SendToAll(&data);
+ }
+
+ LeaveNotify(p);
+
+ if(changeowner)
+ {
+ uint64 newowner = !players.empty() ? players.begin()->second.player : 0;
+ SetOwner(newowner);
+ }
+ }
+}
+
+void Channel::KickOrBan(uint64 good, const char *badname, bool ban)
+{
+ uint32 sec = 0;
+ Player *gplr = objmgr.GetPlayer(good);
+ if(gplr)
+ sec = gplr->GetSession()->GetSecurity();
+
+ if(!IsOn(good))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, good);
+ }
+ else if(!players[good].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, good);
+ }
+ else
+ {
+ Player *bad = objmgr.GetPlayer(badname);
+ if(bad == NULL || !IsOn(bad->GetGUID()))
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, badname);
+ SendToOne(&data, good);
+ }
+ else if(sec < SEC_GAMEMASTER && bad->GetGUID() == m_ownerGUID && good != m_ownerGUID)
+ {
+ WorldPacket data;
+ MakeNotOwner(&data);
+ SendToOne(&data, good);
+ }
+ else
+ {
+ bool changeowner = (m_ownerGUID == bad->GetGUID());
+
+ WorldPacket data;
+
+ if(ban && !IsBanned(bad->GetGUID()))
+ {
+ banned.push_back(bad->GetGUID());
+ MakePlayerBanned(&data, bad->GetGUID(), good);
+ }
+ else
+ MakePlayerKicked(&data, bad->GetGUID(), good);
+
+ SendToAll(&data);
+ players.erase(bad->GetGUID());
+ bad->LeftChannel(this);
+
+ if(changeowner)
+ {
+ uint64 newowner = !players.empty() ? good : false;
+ SetOwner(newowner);
+ }
+ }
+ }
+}
+
+void Channel::UnBan(uint64 good, const char *badname)
+{
+ uint32 sec = 0;
+ Player *gplr = objmgr.GetPlayer(good);
+ if(gplr)
+ sec = gplr->GetSession()->GetSecurity();
+
+ if(!IsOn(good))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, good);
+ }
+ else if(!players[good].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, good);
+ }
+ else
+ {
+ Player *bad = objmgr.GetPlayer(badname);
+ if(bad == NULL || !IsBanned(bad->GetGUID()))
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, badname);
+ SendToOne(&data, good);
+ }
+ else
+ {
+ banned.remove(bad->GetGUID());
+
+ WorldPacket data;
+ MakePlayerUnbanned(&data, bad->GetGUID(), good);
+ SendToAll(&data);
+ }
+ }
+}
+
+void Channel::Password(uint64 p, const char *pass)
+{
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ m_password = pass;
+
+ WorldPacket data;
+ MakePasswordChanged(&data, p);
+ SendToAll(&data);
+ }
+}
+
+void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
+{
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ Player *newp = objmgr.GetPlayer(p2n);
+ if(!newp)
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, p2n);
+ SendToOne(&data, p);
+ return;
+ }
+
+ PlayerInfo inf = players[newp->GetGUID()];
+ if(p == m_ownerGUID && newp->GetGUID() == m_ownerGUID && mod)
+ return;
+
+ if(!IsOn(newp->GetGUID()))
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, p2n);
+ SendToOne(&data, p);
+ return;
+ }
+
+ // allow make moderator from another team only if both is GMs
+ // at this moment this only way to show channel post for GM from another team
+ if( (plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || newp->GetSession()->GetSecurity() < SEC_GAMEMASTER) &&
+ plr->GetTeam() != newp->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL) )
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, p2n);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(m_ownerGUID == newp->GetGUID() && m_ownerGUID != p)
+ {
+ WorldPacket data;
+ MakeNotOwner(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(mod)
+ SetModerator(newp->GetGUID(), set);
+ else
+ SetMute(newp->GetGUID(), set);
+ }
+}
+
+void Channel::SetOwner(uint64 p, const char *newname)
+{
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(sec < SEC_GAMEMASTER && p != m_ownerGUID)
+ {
+ WorldPacket data;
+ MakeNotOwner(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ Player *newp = objmgr.GetPlayer(newname);
+ if(newp == NULL || !IsOn(newp->GetGUID()))
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, newname);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, newname);
+ SendToOne(&data, p);
+ return;
+ }
+
+ players[newp->GetGUID()].SetModerator(true);
+ SetOwner(newp->GetGUID());
+}
+
+void Channel::SendWhoOwner(uint64 p)
+{
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ WorldPacket data;
+ MakeChannelOwner(&data);
+ SendToOne(&data, p);
+ }
+}
+
+void Channel::List(Player* player)
+{
+ uint64 p = player->GetGUID();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+players.size()*(8+1));
+ data << uint8(1); // channel type?
+ data << GetName(); // channel name
+ data << uint8(GetFlags()); // channel flags?
+
+ 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 count = 0;
+ for(PlayerList::iterator i = players.begin(); i != players.end(); ++i)
+ {
+ Player *plr = objmgr.GetPlayer(i->first);
+
+ // 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) ) )
+ {
+ data << uint64(i->first);
+ data << uint8(i->second.flags); // flags seems to be changed...
+ ++count;
+ }
+ }
+
+ data.put<uint32>(pos,count);
+
+ SendToOne(&data, p);
+ }
+}
+
+void Channel::Announce(uint64 p)
+{
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ m_announce = !m_announce;
+
+ WorldPacket data;
+ if(m_announce)
+ MakeAnnouncementsOn(&data, p);
+ else
+ MakeAnnouncementsOff(&data, p);
+ SendToAll(&data);
+ }
+}
+
+void Channel::Moderate(uint64 p)
+{
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ m_moderate = !m_moderate;
+
+ WorldPacket data;
+ if(m_moderate)
+ MakeModerationOn(&data, p);
+ else
+ MakeModerationOff(&data, p);
+ SendToAll(&data);
+ }
+}
+
+void Channel::Say(uint64 p, const char *what, uint32 lang)
+{
+ if(!what)
+ return;
+ if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
+ lang = LANG_UNIVERSAL;
+
+ uint32 sec = 0;
+ Player *plr = objmgr.GetPlayer(p);
+ if(plr)
+ sec = plr->GetSession()->GetSecurity();
+
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ }
+ else if(players[p].IsMuted())
+ {
+ WorldPacket data;
+ MakeMuted(&data);
+ SendToOne(&data, p);
+ }
+ else if(m_moderate && !players[p].IsModerator() && sec < SEC_GAMEMASTER)
+ {
+ WorldPacket data;
+ MakeNotModerator(&data);
+ SendToOne(&data, p);
+ }
+ else
+ {
+ uint32 messageLength = strlen(what) + 1;
+
+ WorldPacket data(SMSG_MESSAGECHAT, 1+4+8+4+m_name.size()+1+8+4+messageLength+1);
+ data << (uint8)CHAT_MSG_CHANNEL;
+ data << (uint32)lang;
+ data << p; // 2.1.0
+ data << uint32(0); // 2.1.0
+ data << m_name;
+ data << p;
+ data << messageLength;
+ data << what;
+ data << uint8(plr ? plr->chatTag() : 0);
+
+ SendToAll(&data, !players[p].IsModerator() ? p : false);
+ }
+}
+
+void Channel::Invite(uint64 p, const char *newname)
+{
+ if(!IsOn(p))
+ {
+ WorldPacket data;
+ MakeNotMember(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ Player *newp = objmgr.GetPlayer(newname);
+ if(!newp)
+ {
+ WorldPacket data;
+ MakePlayerNotFound(&data, newname);
+ SendToOne(&data, p);
+ return;
+ }
+
+ Player *plr = objmgr.GetPlayer(p);
+ if (!plr)
+ return;
+
+ if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
+ {
+ WorldPacket data;
+ MakeInviteWrongFaction(&data);
+ SendToOne(&data, p);
+ return;
+ }
+
+ if(IsOn(newp->GetGUID()))
+ {
+ WorldPacket data;
+ MakePlayerAlreadyMember(&data, newp->GetGUID());
+ SendToOne(&data, p);
+ return;
+ }
+
+ WorldPacket data;
+ if(!newp->GetSocial()->HasIgnore(GUID_LOPART(p)))
+ {
+ MakeInvite(&data, p);
+ SendToOne(&data, newp->GetGUID());
+ data.clear();
+ }
+ MakePlayerInvited(&data, newp->GetGUID());
+ SendToOne(&data, p);
+}
+
+void Channel::SetOwner(uint64 guid, bool exclaim)
+{
+ if(m_ownerGUID)
+ {
+ // [] will re-add player after it possible removed
+ PlayerList::iterator p_itr = players.find(m_ownerGUID);
+ if(p_itr != players.end())
+ p_itr->second.SetOwner(false);
+ }
+
+ m_ownerGUID = guid;
+ if(m_ownerGUID)
+ {
+ uint8 oldFlag = GetPlayerFlags(m_ownerGUID);
+ players[m_ownerGUID].SetOwner(true);
+
+ WorldPacket data;
+ MakeModeChange(&data, m_ownerGUID, oldFlag);
+ SendToAll(&data);
+
+ if(exclaim)
+ {
+ MakeOwnerChanged(&data, m_ownerGUID);
+ SendToAll(&data);
+ }
+ }
+}
+
+void Channel::SendToAll(WorldPacket *data, uint64 p)
+{
+ for(PlayerList::iterator i = players.begin(); i != players.end(); ++i)
+ {
+ Player *plr = objmgr.GetPlayer(i->first);
+ if(plr)
+ {
+ if(!p || !plr->GetSocial()->HasIgnore(GUID_LOPART(p)))
+ plr->GetSession()->SendPacket(data);
+ }
+ }
+}
+
+void Channel::SendToAllButOne(WorldPacket *data, uint64 who)
+{
+ for(PlayerList::iterator i = players.begin(); i != players.end(); ++i)
+ {
+ if(i->first != who)
+ {
+ Player *plr = objmgr.GetPlayer(i->first);
+ if(plr)
+ plr->GetSession()->SendPacket(data);
+ }
+ }
+}
+
+void Channel::SendToOne(WorldPacket *data, uint64 who)
+{
+ Player *plr = objmgr.GetPlayer(who);
+ if(plr)
+ plr->GetSession()->SendPacket(data);
+}
+
+void Channel::Voice(uint64 guid1, uint64 guid2)
+{
+
+}
+
+void Channel::DeVoice(uint64 guid1, uint64 guid2)
+{
+
+}
+
+// done
+void Channel::MakeNotifyPacket(WorldPacket *data, uint8 notify_type)
+{
+ data->Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1);
+ *data << uint8(notify_type);
+ *data << m_name;
+}
+
+// done 0x00
+void Channel::MakeJoined(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_JOINED_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x01
+void Channel::MakeLeft(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_LEFT_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x02
+void Channel::MakeYouJoined(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE);
+ *data << uint8(GetFlags());
+ *data << uint32(GetChannelId());
+ *data << uint32(0);
+}
+
+// done 0x03
+void Channel::MakeYouLeft(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE);
+ *data << uint32(GetChannelId());
+ *data << uint8(0); // can be 0x00 and 0x01
+}
+
+// done 0x04
+void Channel::MakeWrongPassword(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE);
+}
+
+// done 0x05
+void Channel::MakeNotMember(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE);
+}
+
+// done 0x06
+void Channel::MakeNotModerator(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE);
+}
+
+// done 0x07
+void Channel::MakePasswordChanged(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x08
+void Channel::MakeOwnerChanged(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x09
+void Channel::MakePlayerNotFound(WorldPacket *data, std::string name)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE);
+ *data << name;
+}
+
+// done 0x0A
+void Channel::MakeNotOwner(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE);
+}
+
+// done 0x0B
+void Channel::MakeChannelOwner(WorldPacket *data)
+{
+ std::string name = "";
+
+ if(!objmgr.GetPlayerNameByGUID(m_ownerGUID, name) || name.empty())
+ name = "PLAYER_NOT_FOUND";
+
+ MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE);
+ *data << ((IsConstant() || !m_ownerGUID) ? "Nobody" : name);
+}
+
+// done 0x0C
+void Channel::MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags)
+{
+ MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE);
+ *data << uint64(guid);
+ *data << uint8(oldflags);
+ *data << uint8(GetPlayerFlags(guid));
+}
+
+// done 0x0D
+void Channel::MakeAnnouncementsOn(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x0E
+void Channel::MakeAnnouncementsOff(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x0F
+void Channel::MakeModerationOn(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_MODERATION_ON_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x10
+void Channel::MakeModerationOff(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_MODERATION_OFF_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x11
+void Channel::MakeMuted(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_MUTED_NOTICE);
+}
+
+// done 0x12
+void Channel::MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE);
+ *data << uint64(bad);
+ *data << uint64(good);
+}
+
+// done 0x13
+void Channel::MakeBanned(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_BANNED_NOTICE);
+}
+
+// done 0x14
+void Channel::MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE);
+ *data << uint64(bad);
+ *data << uint64(good);
+}
+
+// done 0x15
+void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE);
+ *data << uint64(bad);
+ *data << uint64(good);
+}
+
+// done 0x16
+void Channel::MakePlayerNotBanned(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x17
+void Channel::MakePlayerAlreadyMember(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x18
+void Channel::MakeInvite(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_INVITE_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x19
+void Channel::MakeInviteWrongFaction(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE);
+}
+
+// done 0x1A
+void Channel::MakeWrongFaction(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE);
+}
+
+// done 0x1B
+void Channel::MakeInvalidName(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE);
+}
+
+// done 0x1C
+void Channel::MakeNotModerated(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE);
+}
+
+// done 0x1D
+void Channel::MakePlayerInvited(WorldPacket *data, uint64 guid)
+{
+ std::string name;
+
+ if(!objmgr.GetPlayerNameByGUID(guid, name) || name.empty())
+ return; // player name not found
+
+ MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE);
+ *data << name;
+}
+
+// done 0x1E
+void Channel::MakePlayerInviteBanned(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x1F
+void Channel::MakeThrottled(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE);
+}
+
+// done 0x20
+void Channel::MakeNotInArea(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE);
+}
+
+// done 0x21
+void Channel::MakeNotInLfg(WorldPacket *data)
+{
+ MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE);
+}
+
+// done 0x22
+void Channel::MakeVoiceOn(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE);
+ *data << uint64(guid);
+}
+
+// done 0x23
+void Channel::MakeVoiceOff(WorldPacket *data, uint64 guid)
+{
+ MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE);
+ *data << uint64(guid);
+}
+
+void Channel::JoinNotify(uint64 guid)
+{
+ WorldPacket data;
+
+ if(IsConstant())
+ data.Initialize(SMSG_USERLIST_ADD, 8+1+1+4+GetName().size()+1);
+ else
+ data.Initialize(SMSG_USERLIST_UPDATE, 8+1+1+4+GetName().size()+1);
+
+ data << uint64(guid);
+ data << uint8(GetPlayerFlags(guid));
+ data << uint8(GetFlags());
+ data << uint32(GetNumPlayers());
+ data << GetName();
+ SendToAll(&data);
+}
+
+void Channel::LeaveNotify(uint64 guid)
+{
+ WorldPacket data(SMSG_USERLIST_REMOVE, 8+1+4+GetName().size()+1);
+ data << uint64(guid);
+ data << uint8(GetFlags());
+ data << uint32(GetNumPlayers());
+ data << GetName();
+ SendToAll(&data);
+}
diff --git a/src/game/Channel.h b/src/game/Channel.h
new file mode 100644
index 00000000000..342992411b6
--- /dev/null
+++ b/src/game/Channel.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _CHANNEL_H
+#define _CHANNEL_H
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Player.h"
+
+#include <list>
+#include <map>
+#include <string>
+
+class Channel
+{
+ enum ChatNotify
+ {
+ CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel.";
+ CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel.";
+ //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel.";
+ CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined
+ //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]";
+ CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left
+ CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s.";
+ CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s.";
+ CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s.";
+ CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s.";
+ CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s.";
+ CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found.";
+ CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner.";
+ CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s.";
+ CHAT_MODE_CHANGE_NOTICE = 0x0C, //?
+ CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s.";
+ CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s.";
+ CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s.";
+ CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s.";
+ CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak.";
+ CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s.";
+ CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel.";
+ CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s.";
+ CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s.";
+ CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned.";
+ CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel.";
+ CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'.";
+ CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s.";
+ CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s.";
+ CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name";
+ CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated";
+ CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel";
+ CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned.";
+ CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message.";
+ CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone.
+ CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels.
+ CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s.";
+ CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s.";
+ };
+
+ enum ChannelFlags
+ {
+ CHANNEL_FLAG_NONE = 0x00,
+ CHANNEL_FLAG_CUSTOM = 0x01,
+ // 0x02
+ CHANNEL_FLAG_TRADE = 0x04,
+ CHANNEL_FLAG_NOT_LFG = 0x08,
+ CHANNEL_FLAG_GENERAL = 0x10,
+ CHANNEL_FLAG_CITY = 0x20,
+ CHANNEL_FLAG_LFG = 0x40,
+ CHANNEL_FLAG_VOICE = 0x80
+ // General 0x18 = 0x10 | 0x08
+ // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04
+ // LocalDefence 0x18 = 0x10 | 0x08
+ // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08
+ // LookingForGroup 0x50 = 0x40 | 0x10
+ };
+
+ enum ChannelDBCFlags
+ {
+ CHANNEL_DBC_FLAG_NONE = 0x00000,
+ CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG
+ CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment
+ CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense
+ CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade
+ CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment
+ CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment
+ CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense
+ CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment
+ CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup
+ };
+
+ enum ChannelMemberFlags
+ {
+ MEMBER_FLAG_NONE = 0x00,
+ MEMBER_FLAG_OWNER = 0x01,
+ MEMBER_FLAG_MODERATOR = 0x02,
+ MEMBER_FLAG_VOICED = 0x04,
+ MEMBER_FLAG_MUTED = 0x08,
+ MEMBER_FLAG_CUSTOM = 0x10,
+ MEMBER_FLAG_MIC_MUTED = 0x20,
+ // 0x40
+ // 0x80
+ };
+
+ struct PlayerInfo
+ {
+ uint64 player;
+ uint8 flags;
+
+ bool HasFlag(uint8 flag) { return flags & flag; }
+ void SetFlag(uint8 flag) { if(!HasFlag(flag)) flags |= flag; }
+ bool IsOwner() { return flags & MEMBER_FLAG_OWNER; }
+ void SetOwner(bool state)
+ {
+ if(state) flags |= MEMBER_FLAG_OWNER;
+ else flags &= ~MEMBER_FLAG_OWNER;
+ }
+ bool IsModerator() { return flags & MEMBER_FLAG_MODERATOR; }
+ void SetModerator(bool state)
+ {
+ if(state) flags |= MEMBER_FLAG_MODERATOR;
+ else flags &= ~MEMBER_FLAG_MODERATOR;
+ }
+ bool IsMuted() { return flags & MEMBER_FLAG_MUTED; }
+ void SetMuted(bool state)
+ {
+ if(state) flags |= MEMBER_FLAG_MUTED;
+ else flags &= ~MEMBER_FLAG_MUTED;
+ }
+ };
+
+ typedef std::map<uint64, PlayerInfo> PlayerList;
+ PlayerList players;
+ typedef std::list<uint64> BannedList;
+ BannedList banned;
+ bool m_announce;
+ bool m_moderate;
+ std::string m_name;
+ std::string m_password;
+ uint8 m_flags;
+ uint32 m_channelId;
+ uint64 m_ownerGUID;
+
+ private:
+ // initial packet data (notify type and channel name)
+ void MakeNotifyPacket(WorldPacket *data, uint8 notify_type);
+ // type specific packet data
+ void MakeJoined(WorldPacket *data, uint64 guid); //+ 0x00
+ void MakeLeft(WorldPacket *data, uint64 guid); //+ 0x01
+ void MakeYouJoined(WorldPacket *data); //+ 0x02
+ void MakeYouLeft(WorldPacket *data); //+ 0x03
+ void MakeWrongPassword(WorldPacket *data); //? 0x04
+ void MakeNotMember(WorldPacket *data); //? 0x05
+ void MakeNotModerator(WorldPacket *data); //? 0x06
+ void MakePasswordChanged(WorldPacket *data, uint64 guid); //+ 0x07
+ void MakeOwnerChanged(WorldPacket *data, uint64 guid); //? 0x08
+ void MakePlayerNotFound(WorldPacket *data, std::string name); //+ 0x09
+ void MakeNotOwner(WorldPacket *data); //? 0x0A
+ void MakeChannelOwner(WorldPacket *data); //? 0x0B
+ void MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags); //+ 0x0C
+ void MakeAnnouncementsOn(WorldPacket *data, uint64 guid); //+ 0x0D
+ void MakeAnnouncementsOff(WorldPacket *data, uint64 guid); //+ 0x0E
+ void MakeModerationOn(WorldPacket *data, uint64 guid); //+ 0x0F
+ void MakeModerationOff(WorldPacket *data, uint64 guid); //+ 0x10
+ void MakeMuted(WorldPacket *data); //? 0x11
+ void MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good); //? 0x12
+ void MakeBanned(WorldPacket *data); //? 0x13
+ void MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x14
+ void MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x15
+ void MakePlayerNotBanned(WorldPacket *data, uint64 guid); //? 0x16
+ void MakePlayerAlreadyMember(WorldPacket *data, uint64 guid); //+ 0x17
+ void MakeInvite(WorldPacket *data, uint64 guid); //? 0x18
+ void MakeInviteWrongFaction(WorldPacket *data); //? 0x19
+ void MakeWrongFaction(WorldPacket *data); //? 0x1A
+ void MakeInvalidName(WorldPacket *data); //? 0x1B
+ void MakeNotModerated(WorldPacket *data); //? 0x1C
+ void MakePlayerInvited(WorldPacket *data, uint64 guid); //+ 0x1D
+ void MakePlayerInviteBanned(WorldPacket *data, uint64 guid); //? 0x1E
+ void MakeThrottled(WorldPacket *data); //? 0x1F
+ void MakeNotInArea(WorldPacket *data); //? 0x20
+ void MakeNotInLfg(WorldPacket *data); //? 0x21
+ void MakeVoiceOn(WorldPacket *data, uint64 guid); //+ 0x22
+ void MakeVoiceOff(WorldPacket *data, uint64 guid); //+ 0x23
+
+ void SendToAll(WorldPacket *data, uint64 p = 0);
+ void SendToAllButOne(WorldPacket *data, uint64 who);
+ void SendToOne(WorldPacket *data, uint64 who);
+
+ bool IsOn(uint64 who) const { return players.count(who) > 0; }
+
+ bool IsBanned(const uint64 guid) const
+ {
+ for(BannedList::const_iterator i = banned.begin(); i != banned.end(); ++i)
+ if(*i == guid)
+ return true;
+ return false;
+ }
+
+ bool IsFirst() const { return !(players.size() > 1); }
+
+ uint8 GetPlayerFlags(uint64 p) const
+ {
+ PlayerList::const_iterator p_itr = players.find(p);
+ if(p_itr == players.end())
+ return 0;
+
+ return p_itr->second.flags;
+ }
+
+ void SetModerator(uint64 p, bool set)
+ {
+ if(players[p].IsModerator() != set)
+ {
+ uint8 oldFlag = GetPlayerFlags(p);
+ players[p].SetModerator(set);
+
+ WorldPacket data;
+ MakeModeChange(&data, p, oldFlag);
+ SendToAll(&data);
+ }
+ }
+
+ void SetMute(uint64 p, bool set)
+ {
+ if(players[p].IsMuted() != set)
+ {
+ uint8 oldFlag = GetPlayerFlags(p);
+ players[p].SetMuted(set);
+
+ WorldPacket data;
+ MakeModeChange(&data, p, oldFlag);
+ SendToAll(&data);
+ }
+ }
+
+ public:
+ Channel(std::string name, uint32 channel_id);
+ std::string GetName() const { return m_name; }
+ uint32 GetChannelId() const { return m_channelId; }
+ bool IsConstant() const { return m_channelId != 0; }
+ bool IsAnnounce() const { return m_announce; }
+ bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; }
+ std::string GetPassword() const { return m_password; }
+ void SetPassword(std::string npassword) { m_password = npassword; }
+ void SetAnnounce(bool nannounce) { m_announce = nannounce; }
+ uint32 GetNumPlayers() const { return players.size(); }
+ uint8 GetFlags() const { return m_flags; }
+ bool HasFlag(uint8 flag) { return m_flags & flag; }
+
+ void Join(uint64 p, const char *pass);
+ void Leave(uint64 p, bool send = true);
+ void KickOrBan(uint64 good, const char *badname, bool ban);
+ void Kick(uint64 good, const char *badname) { KickOrBan(good, badname, false); }
+ void Ban(uint64 good, const char *badname) { KickOrBan(good, badname, true); }
+ void UnBan(uint64 good, const char *badname);
+ void Password(uint64 p, const char *pass);
+ void SetMode(uint64 p, const char *p2n, bool mod, bool set);
+ void SetOwner(uint64 p, bool exclaim = true);
+ void SetOwner(uint64 p, const char *newname);
+ void SendWhoOwner(uint64 p);
+ void SetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, true); }
+ void UnsetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, false); }
+ void SetMute(uint64 p, const char *newname) { SetMode(p, newname, false, true); }
+ void UnsetMute(uint64 p, const char *newname) { SetMode(p, newname, false, false); }
+ void List(Player* p);
+ void Announce(uint64 p);
+ void Moderate(uint64 p);
+ void Say(uint64 p, const char *what, uint32 lang);
+ void Invite(uint64 p, const char *newp);
+ void Voice(uint64 guid1, uint64 guid2);
+ void DeVoice(uint64 guid1, uint64 guid2);
+ void JoinNotify(uint64 guid); // invisible notify
+ void LeaveNotify(uint64 guid); // invisible notify
+};
+#endif
diff --git a/src/game/ChannelHandler.cpp b/src/game/ChannelHandler.cpp
new file mode 100644
index 00000000000..4c8bdd1c516
--- /dev/null
+++ b/src/game/ChannelHandler.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ObjectMgr.h" // for normalizePlayerName
+#include "ChannelMgr.h"
+#include "Policies/SingletonImp.h"
+
+INSTANTIATE_SINGLETON_1( AllianceChannelMgr );
+INSTANTIATE_SINGLETON_1( HordeChannelMgr );
+
+void WorldSession::HandleChannelJoin(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 4+1+1+1);
+
+ uint32 channel_id;
+ uint8 unknown1, unknown2;
+ std::string channelname, pass;
+
+ recvPacket >> channel_id >> unknown1 >> unknown2;
+ recvPacket >> channelname;
+
+ if(channelname.empty())
+ return;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, 4+1+1+(channelname.size()+1)+1);
+
+ recvPacket >> pass;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetJoinChannel(channelname, channel_id))
+ chn->Join(_player->GetGUID(), pass.c_str());
+}
+
+void WorldSession::HandleChannelLeave(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 4+1);
+
+ uint32 unk;
+ std::string channelname;
+ recvPacket >> unk; // channel id?
+ recvPacket >> channelname;
+
+ if(channelname.empty())
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ {
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Leave(_player->GetGUID(), true);
+ cMgr->LeftChannel(channelname);
+ }
+}
+
+void WorldSession::HandleChannelList(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->List(_player);
+}
+
+void WorldSession::HandleChannelPassword(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, pass;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> pass;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Password(_player->GetGUID(), pass.c_str());
+}
+
+void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, newp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> newp;
+
+ if(!normalizePlayerName(newp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->SetOwner(_player->GetGUID(), newp.c_str());
+}
+
+void WorldSession::HandleChannelOwner(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->SendWhoOwner(_player->GetGUID());
+}
+
+void WorldSession::HandleChannelModerator(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->SetModerator(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->UnsetModerator(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelMute(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->SetMute(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->UnsetMute(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelInvite(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Invite(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelKick(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Kick(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelBan(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Ban(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelUnban(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1+1);
+
+ std::string channelname, otp;
+ recvPacket >> channelname;
+
+ // recheck
+ CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1);
+
+ recvPacket >> otp;
+
+ if(!normalizePlayerName(otp))
+ return;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->UnBan(_player->GetGUID(), otp.c_str());
+}
+
+void WorldSession::HandleChannelAnnounce(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Announce(_player->GetGUID());
+}
+
+void WorldSession::HandleChannelModerate(WorldPacket& recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->Moderate(_player->GetGUID());
+}
+
+void WorldSession::HandleChannelRosterQuery(WorldPacket &recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->List(_player);
+}
+
+void WorldSession::HandleChannelInfoQuery(WorldPacket &recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ {
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ {
+ WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4);
+ data << chn->GetName();
+ data << uint8(chn->GetFlags());
+ data << uint32(chn->GetNumPlayers());
+ SendPacket(&data);
+ }
+ }
+}
+
+void WorldSession::HandleChannelJoinNotify(WorldPacket &recvPacket)
+{
+ sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+ //recvPacket.hexlike();
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string channelname;
+ recvPacket >> channelname;
+ /*if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ if(Channel *chn = cMgr->GetChannel(channelname, _player))
+ chn->JoinNotify(_player->GetGUID());*/
+}
diff --git a/src/game/ChannelMgr.h b/src/game/ChannelMgr.h
new file mode 100644
index 00000000000..eb15f08a39e
--- /dev/null
+++ b/src/game/ChannelMgr.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef MANGOSSERVER_CHANNELMGR_H
+#define MANGOSSERVER_CHANNELMGR_H
+
+#include "Channel.h"
+#include "Policies/Singleton.h"
+#include "World.h"
+
+#include <map>
+#include <string>
+
+class ChannelMgr
+{
+ public:
+ typedef std::map<std::string,Channel *> ChannelMap;
+ ChannelMgr() {}
+ ~ChannelMgr()
+ {
+ for(ChannelMap::iterator itr = channels.begin();itr!=channels.end(); ++itr)
+ delete itr->second;
+ channels.clear();
+ }
+ Channel *GetJoinChannel(std::string name, uint32 channel_id)
+ {
+ if(channels.count(name) == 0)
+ {
+ Channel *nchan = new Channel(name,channel_id);
+ channels[name] = nchan;
+ }
+ return channels[name];
+ }
+ Channel *GetChannel(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(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;
+ }
+ }
+ private:
+ ChannelMap channels;
+ void MakeNotOnPacket(WorldPacket *data, std::string name)
+ {
+ data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size
+ (*data) << (uint8)0x05 << 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 &MaNGOS::Singleton<AllianceChannelMgr>::Instance();
+
+ if(team==ALLIANCE)
+ return &MaNGOS::Singleton<AllianceChannelMgr>::Instance();
+ if(team==HORDE)
+ return &MaNGOS::Singleton<HordeChannelMgr>::Instance();
+ return NULL;
+}
+#endif
diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp
new file mode 100644
index 00000000000..f63af7d7f81
--- /dev/null
+++ b/src/game/CharacterHandler.cpp
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSocket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Guild.h"
+#include "UpdateMask.h"
+#include "Auth/md5.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Group.h"
+#include "Database/DatabaseImpl.h"
+#include "PlayerDump.h"
+#include "SocialMgr.h"
+#include "Util.h"
+
+class LoginQueryHolder : public SqlQueryHolder
+{
+ private:
+ uint32 m_accountId;
+ uint64 m_guid;
+ public:
+ LoginQueryHolder(uint32 accountId, uint64 guid)
+ : m_accountId(accountId), m_guid(guid) { }
+ uint64 GetGuid() const { return m_guid; }
+ uint32 GetAccountId() const { return m_accountId; }
+ bool Initialize();
+};
+
+bool LoginQueryHolder::Initialize()
+{
+ SetSize(MAX_PLAYER_LOGIN_QUERY);
+
+ bool res = true;
+
+ // 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, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid));
+ 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_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID);
+ 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_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", 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));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid));
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid));
+ // in other case still be dummy query
+ res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid));
+
+ return res;
+}
+
+// don't call WorldSession directly
+// it may get deleted before the query callbacks get executed
+// instead pass an account id to this handler
+class CharacterHandler
+{
+ public:
+ void HandleCharEnumCallback(QueryResult * result, uint32 account)
+ {
+ WorldSession * session = sWorld.FindSession(account);
+ if(!session)
+ {
+ delete result;
+ return;
+ }
+ session->HandleCharEnum(result);
+ }
+ void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
+ {
+ if (!holder) return;
+ WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId());
+ if(!session)
+ {
+ delete holder;
+ return;
+ }
+ session->HandlePlayerLogin((LoginQueryHolder*)holder);
+ }
+} chrHandler;
+
+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());
+
+ WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
+
+ uint8 num = 0;
+
+ data << num;
+
+ if( result )
+ {
+ Player *plr = new Player(this);
+ do
+ {
+ sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId());
+
+ if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() ))
+ {
+ plr->BuildEnumData( result, &data );
+ ++num;
+ }
+ }
+ while( result->NextRow() );
+
+ delete plr;
+ delete result;
+ }
+
+ data.put<uint8>(0, num);
+
+ SendPacket( &data );
+}
+
+void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
+{
+ /// get all the data necessary for loading all characters (along with their pets) on the account
+ 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.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
+ // 9 10 11
+ "character_pet.entry, character_pet.modelid, character_pet.level "
+ "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' "
+ "WHERE characters.account = '%u' ORDER BY characters.guid"
+ :
+ // --------- Query With Declined Names ---------
+ // 0 1 2 3 4 5 6 7 8
+ "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
+ // 9 10 11 12
+ "character_pet.entry, character_pet.modelid, character_pet.level, genitive "
+ "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' "
+ "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
+ "WHERE characters.account = '%u' ORDER BY characters.guid",
+ GetAccountId());
+}
+
+void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1);
+
+ std::string name;
+ uint8 race_,class_;
+ bool pTbc = this->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+ recv_data >> name;
+
+ // recheck with known string size
+ CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1);
+
+ recv_data >> race_;
+ recv_data >> class_;
+
+ WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases
+
+ if(GetSecurity() == SEC_PLAYER)
+ {
+ if(uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED))
+ {
+ bool disabled = false;
+
+ uint32 team = Player::TeamForRace(race_);
+ switch(team)
+ {
+ case ALLIANCE: disabled = mask & (1<<0); break;
+ case HORDE: disabled = mask & (1<<1); break;
+ }
+
+ if(disabled)
+ {
+ data << (uint8)CHAR_CREATE_DISABLED;
+ SendPacket( &data );
+ return;
+ }
+ }
+ }
+
+ if (!sChrClassesStore.LookupEntry(class_)||
+ !sChrRacesStore.LookupEntry(race_))
+ {
+ data << (uint8)CHAR_CREATE_FAILED;
+ SendPacket( &data );
+ sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_);
+ return;
+ }
+
+ // prevent character creating Expansion race without Expansion account
+ if (!pTbc&&(race_>RACE_TROLL))
+ {
+ data << (uint8)CHAR_CREATE_EXPANSION;
+ sLog.outError("No Expansion Account:[%d] but tried to Create TBC character",GetAccountId());
+ SendPacket( &data );
+ return;
+ }
+
+ // prevent character creating with invalid name
+ if(!normalizePlayerName(name))
+ {
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
+ return;
+ }
+
+ // check name limitations
+ if(!ObjectMgr::IsValidName(name,true))
+ {
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name))
+ {
+ data << (uint8)CHAR_NAME_RESERVED;
+ SendPacket( &data );
+ return;
+ }
+
+ 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 )
+ {
+ Field *fields=resultacct->Fetch();
+ uint32 acctcharcount = fields[0].GetUInt32();
+ delete resultacct;
+
+ if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT))
+ {
+ data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT;
+ SendPacket( &data );
+ return;
+ }
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
+ uint8 charcount = 0;
+ if ( result )
+ {
+ Field *fields=result->Fetch();
+ charcount = fields[0].GetUInt8();
+ delete result;
+
+ if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM))
+ {
+ data << (uint8)CHAR_CREATE_SERVER_LIMIT;
+ SendPacket( &data );
+ return;
+ }
+ }
+
+ bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER;
+ uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS);
+
+ bool have_same_race = false;
+ if(!AllowTwoSideAccounts || skipCinematics == 1)
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1");
+ if(result2)
+ {
+ uint32 team_= Player::TeamForRace(race_);
+
+ Field* field = result2->Fetch();
+ uint8 race = field[0].GetUInt32();
+
+ // need to check team only for first character
+ // TODO: what to if account already has characters of both races?
+ if (!AllowTwoSideAccounts)
+ {
+ uint32 team=0;
+ if(race > 0)
+ team = Player::TeamForRace(race);
+
+ if(team != team_)
+ {
+ data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
+ SendPacket( &data );
+ delete result2;
+ return;
+ }
+ }
+
+ if (skipCinematics == 1)
+ {
+ // TODO: check if cinematic already shown? (already logged in?; cinematic field)
+ while (race_ != race && result2->NextRow())
+ {
+ field = result2->Fetch();
+ race = field[0].GetUInt32();
+ }
+ have_same_race = race_ == race;
+ }
+ delete result2;
+ }
+ }
+
+ // extract other data required for player creating
+ uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId;
+ recv_data >> gender >> skin >> face;
+ recv_data >> hairStyle >> hairColor >> facialHair >> outfitId;
+
+ Player * pNewChar = new Player(this);
+ if(!pNewChar->Create( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId ))
+ {
+ // Player not create (race/class problem?)
+ delete pNewChar;
+
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+
+ return;
+ }
+
+ if(have_same_race && skipCinematics == 1 || skipCinematics == 2)
+ pNewChar->setCinematic(1); // not show intro
+
+ // Player created, save it now
+ 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);
+
+ delete pNewChar; // created only to call SaveToDB()
+
+ data << (uint8)CHAR_CREATE_SUCCESS;
+ SendPacket( &data );
+
+ std::string IP_str = _socket ? _socket->GetRemoteAddress().c_str() : "-";
+ sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
+ sLog.outChar("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
+}
+
+void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // can't delete loaded character
+ if(objmgr.GetPlayer(guid))
+ return;
+
+ uint32 accountId = 0;
+ std::string name;
+
+ // is guild leader
+ if(objmgr.GetGuildByLeader(guid))
+ {
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER;
+ SendPacket( &data );
+ return;
+ }
+
+ // is arena team captain
+ if(objmgr.GetArenaTeamByCapitan(guid))
+ {
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN;
+ SendPacket( &data );
+ return;
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ accountId = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ delete result;
+ }
+
+ // prevent deleting other players' characters using cheating tools
+ if(accountId != GetAccountId())
+ return;
+
+ std::string IP_str = _socket ? _socket->GetRemoteAddress().c_str() : "-";
+ sLog.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
+ sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
+
+ if(sLog.IsOutCharDump()) // optimize GetPlayerDump call
+ {
+ std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid));
+ sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str());
+ }
+
+ Player::DeleteFromDB(guid, GetAccountId());
+
+ WorldPacket data(SMSG_CHAR_DELETE, 1);
+ data << (uint8)CHAR_DELETE_SUCCESS;
+ SendPacket( &data );
+}
+
+void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ m_playerLoading = true;
+ uint64 playerGuid = 0;
+
+ DEBUG_LOG( "WORLD: Recvd Player Logon Message" );
+
+ recv_data >> playerGuid;
+
+ LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid);
+ if(!holder->Initialize())
+ {
+ delete holder; // delete all unprocessed queries
+ m_playerLoading = false;
+ return;
+ }
+
+ CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
+}
+
+void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
+{
+ uint64 playerGuid = holder->GetGuid();
+
+ Player* pCurrChar = new Player(this);
+ pCurrChar->GetMotionMaster()->Initialize();
+
+ // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
+ if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
+ {
+ KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
+ delete pCurrChar; // delete it manually
+ delete holder; // delete all unprocessed queries
+ m_playerLoading = false;
+ return;
+ }
+
+ SetPlayer(pCurrChar);
+
+ pCurrChar->SendDungeonDifficulty(false);
+
+ WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
+ data << pCurrChar->GetMapId();
+ data << pCurrChar->GetPositionX();
+ data << pCurrChar->GetPositionY();
+ data << pCurrChar->GetPositionZ();
+ data << pCurrChar->GetOrientation();
+ SendPacket(&data);
+
+ data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 );
+ for(int i = 0; i < 32; i++)
+ data << uint32(0);
+ SendPacket(&data);
+
+ data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0
+ data << uint8(2); // unknown value
+ data << uint8(0); // enable(1)/disable(0) voice chat interface in client
+ SendPacket(&data);
+
+ // Send MOTD
+ {
+ data.Initialize(SMSG_MOTD, 50); // new in 2.0.1
+ data << (uint32)0;
+
+ uint32 linecount=0;
+ std::string str_motd = sWorld.GetMotd();
+ std::string::size_type pos, nextpos;
+
+ pos = 0;
+ while ( (nextpos= str_motd.find('@',pos)) != std::string::npos )
+ {
+ if (nextpos != pos)
+ {
+ data << str_motd.substr(pos,nextpos-pos);
+ ++linecount;
+ }
+ pos = nextpos+1;
+ }
+
+ if (pos<str_motd.length())
+ {
+ data << str_motd.substr(pos);
+ ++linecount;
+ }
+
+ data.put(0, linecount);
+
+ SendPacket( &data );
+ DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
+ }
+
+ if(pCurrChar->GetGuildId() != 0)
+ {
+ Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId());
+ if(guild)
+ {
+ data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1));
+ data << (uint8)GE_MOTD;
+ data << (uint8)1;
+ data << guild->GetMOTD();
+ SendPacket(&data);
+ DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" );
+
+ data.Initialize(SMSG_GUILD_EVENT, (5+10)); // we guess size
+ data<<(uint8)GE_SIGNED_ON;
+ data<<(uint8)1;
+ data<<pCurrChar->GetName();
+ data<<pCurrChar->GetGUID();
+ guild->BroadcastPacket(&data);
+ DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
+
+ // Increment online members of the guild
+ guild->IncOnlineMemberCount();
+ }
+ else
+ {
+ // remove wrong guild data
+ sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId());
+ pCurrChar->SetUInt32Value(PLAYER_GUILDID,0);
+ pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID());
+ }
+ }
+
+ if(!pCurrChar->isAlive())
+ pCurrChar->SendCorpseReclaimDelay(true);
+
+ pCurrChar->SendInitialPacketsBeforeAddToMap();
+
+ //Show cinematic at the first time that player login
+ if( !pCurrChar->getCinematic() )
+ {
+ pCurrChar->setCinematic(1);
+
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace());
+ if(rEntry)
+ {
+ data.Initialize( SMSG_TRIGGER_CINEMATIC,4 );
+ data << uint32(rEntry->startmovie);
+ SendPacket( &data );
+ }
+ }
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
+ QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD);
+
+ if(resultGuild)
+ {
+ Field *fields = resultGuild->Fetch();
+ pCurrChar->SetInGuild(fields[0].GetUInt32());
+ pCurrChar->SetRank(fields[1].GetUInt32());
+ delete resultGuild;
+ }
+ else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
+ {
+ pCurrChar->SetInGuild(0);
+ pCurrChar->SetRank(0);
+ }
+
+ if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar))
+ {
+ AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId());
+ if(at)
+ pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
+ else
+ pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
+ }
+
+ ObjectAccessor::Instance().AddObject(pCurrChar);
+ //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName());
+ pCurrChar->GetSocial()->SendSocialList();
+
+ 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());
+ pCurrChar->SetInGameTime( getMSTime() );
+
+ // announce group about member online (must be after add to player list to receive announce to self)
+ if(Group *group = pCurrChar->GetGroup())
+ {
+ //pCurrChar->groupInfo.group->SendInit(this); // useless
+ group->SendUpdate();
+ }
+
+ // friend status
+ sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), "", true);
+
+ // Place character in world (and load zone) before some object loading
+ pCurrChar->LoadCorpse();
+
+ // setting Ghost+speed if dead
+ //if ( pCurrChar->m_deathState == DEAD )
+ if (pCurrChar->m_deathState != ALIVE)
+ {
+ // not blizz like, we must correctly save and load player instead...
+ if(pCurrChar->getRace() == RACE_NIGHTELF)
+ pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
+ pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
+
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535);
+ //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825);
+ //if (pCurrChar->getRace() == RACE_NIGHTELF)
+ //{
+ // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true);
+ // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true);
+ //}
+ //else
+ //{
+ // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true);
+ // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true);
+ //}
+ pCurrChar->SetMovement(MOVE_WATER_WALK);
+ }
+
+ if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource())
+ {
+
+ sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() );
+
+ uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam());
+ uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath();
+
+ // search appropriate start path node
+ uint32 startNode = 0;
+
+ TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
+
+ float distPrev = MAP_SIZE*MAP_SIZE;
+ float distNext =
+ (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+
+ (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+
+ (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ());
+
+ for(uint32 i = 1; i < nodeList.size(); ++i)
+ {
+ TaxiPathNode const& node = nodeList[i];
+ TaxiPathNode const& prevNode = nodeList[i-1];
+
+ // skip nodes at another map
+ if(node.mapid != pCurrChar->GetMapId())
+ continue;
+
+ distPrev = distNext;
+
+ distNext =
+ (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+
+ (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+
+ (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ());
+
+ float distNodes =
+ (node.x-prevNode.x)*(node.x-prevNode.x)+
+ (node.y-prevNode.y)*(node.y-prevNode.y)+
+ (node.z-prevNode.z)*(node.z-prevNode.z);
+
+ if(distNext + distPrev < distNodes)
+ {
+ startNode = i;
+ break;
+ }
+ }
+
+ SendDoFlight( MountId, path, startNode );
+ }
+
+ // Load pet if any and player is alive and not in taxi flight
+ if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0)
+ pCurrChar->LoadPet();
+
+ // Set FFA PvP for non GM in non-rest mode
+ if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) )
+ pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
+ pCurrChar->SetContestedPvP();
+
+ // Apply at_login requests
+ if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
+ {
+ pCurrChar->resetSpells();
+ SendNotification("Spells has been reset.");
+ }
+
+ if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
+ {
+ pCurrChar->resetTalents(true);
+ SendNotification("Talents has been reset.");
+ }
+
+ // show time before shutdown if shutdown planned.
+ if(sWorld.IsShutdowning())
+ sWorld.ShutdownMsg(true,pCurrChar);
+
+ if(pCurrChar->isGameMaster())
+ SendNotification("GM mode is ON");
+
+ std::string IP_str = _socket ? _socket->GetRemoteAddress().c_str() : "-";
+ sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUID());
+
+ m_playerLoading = false;
+ delete holder;
+}
+
+void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+1);
+
+ DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
+
+ uint32 repListID;
+ uint8 flag;
+
+ recv_data >> repListID;
+ recv_data >> flag;
+
+ FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID);
+ if (itr == GetPlayer()->m_factions.end())
+ return;
+
+ // always invisible or hidden faction can't change war state
+ if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) )
+ return;
+
+ GetPlayer()->SetFactionAtWar(&itr->second,flag);
+}
+
+//I think this function is never used :/ I dunno, but i guess this opcode not exists
+void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
+{
+ //CHECK_PACKET_SIZE(recv_data,4+4);
+
+ //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat");
+ /*
+ uint32 FactionID;
+ uint32 Standing;
+
+ recv_data >> FactionID;
+ recv_data >> Standing;
+
+ std::list<struct Factions>::iterator itr;
+
+ for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
+ {
+ if(itr->ReputationListID == FactionID)
+ {
+ itr->Standing += Standing;
+ itr->Flags = (itr->Flags | 1);
+ break;
+ }
+ }
+ */
+ GetPlayer()->UpdateReputation();
+}
+
+void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
+
+ WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5);
+ data << uint32(0) << uint8(6);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 iFlag;
+ recv_data >> iFlag;
+
+ uint32 wInt = (iFlag / 32);
+ if (wInt >= 8)
+ {
+ //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
+ return;
+ }
+ uint32 rInt = (iFlag % 32);
+
+ uint32 tutflag = GetPlayer()->GetTutorialInt( wInt );
+ tutflag |= (1 << rInt);
+ GetPlayer()->SetTutorialInt( wInt, tutflag );
+
+ //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
+}
+
+void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
+{
+ for ( uint32 iI = 0; iI < 8; iI++)
+ GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF );
+}
+
+void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
+{
+ for ( uint32 iI = 0; iI < 8; iI++)
+ GetPlayer()->SetTutorialInt( iI, 0x00000000 );
+}
+
+void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
+ uint32 fact;
+ recv_data >> fact;
+ GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact);
+}
+
+void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+1);
+
+ DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
+ uint32 replistid;
+ uint8 inactive;
+ recv_data >> replistid >> inactive;
+
+ FactionStateList::iterator itr = _player->m_factions.find(replistid);
+ if (itr == _player->m_factions.end())
+ return;
+
+ _player->SetFactionInactive(&itr->second, inactive);
+}
+
+void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName());
+ _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
+}
+
+void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName());
+ _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
+}
+
+void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ uint64 guid;
+ std::string newname;
+ std::string oldname;
+
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ recv_data >> guid;
+ recv_data >> newname;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
+ if (result)
+ {
+ uint32 at_loginFlags;
+ Field *fields = result->Fetch();
+ at_loginFlags = fields[0].GetUInt32();
+ delete result;
+
+ if (!(at_loginFlags & AT_LOGIN_RENAME))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+ }
+ else
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+
+ if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_LOGIN_NO_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ // prevent character rename to invalid name
+ if(!normalizePlayerName(newname))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_NO_NAME;
+ SendPacket( &data );
+ return;
+ }
+
+ if(!ObjectMgr::IsValidName(newname,true))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_INVALID_CHARACTER;
+ SendPacket( &data );
+ return;
+ }
+
+ // check name limitations
+ if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_RESERVED;
+ SendPacket( &data );
+ return;
+ }
+
+ if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_CREATE_ERROR;
+ SendPacket( &data );
+ return;
+ }
+
+ if(newname == oldname) // checked by client
+ {
+ WorldPacket data(SMSG_CHAR_RENAME, 1);
+ data << (uint8)CHAR_NAME_FAILURE;
+ SendPacket( &data );
+ return;
+ }
+
+ // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe)
+
+ CharacterDatabase.escape_string(newname);
+ CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid));
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
+
+ std::string IP_str = _socket ? _socket->GetRemoteAddress().c_str() : "-";
+ sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str());
+
+ WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1));
+ data << (uint8)RESPONSE_SUCCESS;
+ data << guid;
+ data << newname;
+ SendPacket(&data);
+}
+
+void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data)
+{
+ uint64 guid;
+
+ CHECK_PACKET_SIZE(recv_data, 8+6);
+ recv_data >> guid;
+
+ // not accept declined names for unsupported languages
+ std::string name;
+ if(!objmgr.GetPlayerNameByGUID(guid,name))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ std::string name2;
+ DeclinedName declinedname;
+
+ recv_data >> name2;
+
+ if(name2!=name) // character have different name
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ recv_data >> declinedname.name[i];
+ if(!normalizePlayerName(declinedname.name[i]))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+ }
+
+ if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
+ {
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)1;
+ data << guid;
+ SendPacket(&data);
+ return;
+ }
+
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.escape_string(declinedname.name[i]);
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid));
+ CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')",
+ GUID_LOPART(guid), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str());
+ CharacterDatabase.CommitTransaction();
+
+ WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
+ data << (uint32)0; // OK
+ data << guid;
+ SendPacket(&data);
+}
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
new file mode 100644
index 00000000000..9635542cf4f
--- /dev/null
+++ b/src/game/Chat.cpp
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "Chat.h"
+#include "MapManager.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+bool ChatHandler::load_command_table = true;
+
+LanguageDesc lang_description[LANGUAGES_COUNT] =
+{
+ { LANG_ADDON, 0, 0 },
+ { LANG_UNIVERSAL, 0, 0 },
+ { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
+ { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
+ { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
+ { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
+ { LANG_COMMON, 668, SKILL_LANG_COMMON },
+ { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
+ { LANG_TITAN, 816, SKILL_LANG_TITAN },
+ { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
+ { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
+ { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
+ { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
+ { LANG_TROLL, 7341, SKILL_LANG_TROLL },
+ { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
+ { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
+ { LANG_ZOMBIE, 0, 0 },
+ { LANG_GNOMISH_BINARY, 0, 0 },
+ { LANG_GOBLIN_BINARY, 0, 0 }
+};
+
+LanguageDesc const* GetLanguageDescByID(uint32 lang)
+{
+ for(int i = 0; i < LANGUAGES_COUNT; ++i)
+ {
+ if(uint32(lang_description[i].lang_id) == lang)
+ return &lang_description[i];
+ }
+
+ return NULL;
+}
+
+LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id)
+{
+ for(int i = 0; i < LANGUAGES_COUNT; ++i)
+ {
+ if(lang_description[i].spell_id == spell_id)
+ return &lang_description[i];
+ }
+
+ return NULL;
+}
+
+LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id)
+{
+ for(int i = 0; i < LANGUAGES_COUNT; ++i)
+ {
+ if(lang_description[i].skill_id == skill_id)
+ return &lang_description[i];
+ }
+
+ return NULL;
+}
+
+ChatCommand * ChatHandler::getCommandTable()
+{
+ static ChatCommand serverCommandTable[] =
+ {
+ { "idlerestart", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleRestartCommand, "", NULL },
+ { "idleshutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleShutDownCommand, "", NULL },
+ { "info", SEC_PLAYER, &ChatHandler::HandleInfoCommand, "", NULL },
+ { "restart", SEC_ADMINISTRATOR, &ChatHandler::HandleRestartCommand, "", NULL },
+ { "shutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleShutDownCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand modifyCommandTable[] =
+ {
+ { "hp", SEC_MODERATOR, &ChatHandler::HandleModifyHPCommand, "", NULL },
+ { "mana", SEC_MODERATOR, &ChatHandler::HandleModifyManaCommand, "", NULL },
+ { "rage", SEC_MODERATOR, &ChatHandler::HandleModifyRageCommand, "", NULL },
+ { "energy", SEC_MODERATOR, &ChatHandler::HandleModifyEnergyCommand, "", NULL },
+ { "money", SEC_MODERATOR, &ChatHandler::HandleModifyMoneyCommand, "", NULL },
+ { "speed", SEC_MODERATOR, &ChatHandler::HandleModifySpeedCommand, "", NULL },
+ { "swim", SEC_MODERATOR, &ChatHandler::HandleModifySwimCommand, "", NULL },
+ { "scale", SEC_MODERATOR, &ChatHandler::HandleModifyScaleCommand, "", NULL },
+ { "bit", SEC_MODERATOR, &ChatHandler::HandleModifyBitCommand, "", NULL },
+ { "bwalk", SEC_MODERATOR, &ChatHandler::HandleModifyBWalkCommand, "", NULL },
+ { "fly", SEC_MODERATOR, &ChatHandler::HandleModifyFlyCommand, "", NULL },
+ { "aspeed", SEC_MODERATOR, &ChatHandler::HandleModifyASpeedCommand, "", NULL },
+ { "faction", SEC_MODERATOR, &ChatHandler::HandleModifyFactionCommand, "", NULL },
+ { "spell", SEC_MODERATOR, &ChatHandler::HandleModifySpellCommand, "", NULL },
+ { "tp", SEC_MODERATOR, &ChatHandler::HandleModifyTalentCommand, "", NULL },
+ { "titles", SEC_MODERATOR, &ChatHandler::HandleModifyKnownTitlesCommand, "", NULL },
+ { "mount", SEC_MODERATOR, &ChatHandler::HandleModifyMountCommand, "", NULL },
+ { "honor", SEC_MODERATOR, &ChatHandler::HandleModifyHonorCommand, "", NULL },
+ { "rep", SEC_MODERATOR, &ChatHandler::HandleModifyRepCommand, "", NULL },
+ { "arena", SEC_MODERATOR, &ChatHandler::HandleModifyArenaCommand, "", NULL },
+ { "drunk", SEC_MODERATOR, &ChatHandler::HandleDrunkCommand, "", NULL },
+ { "standstate", SEC_GAMEMASTER, &ChatHandler::HandleStandStateCommand, "", NULL },
+ { "morph", SEC_GAMEMASTER, &ChatHandler::HandleMorphCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand wpCommandTable[] =
+ {
+ { "show", SEC_GAMEMASTER, &ChatHandler::HandleWpShowCommand, "", NULL },
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleWpAddCommand, "", NULL },
+ { "modify", SEC_GAMEMASTER, &ChatHandler::HandleWpModifyCommand, "", NULL },
+ { "export", SEC_ADMINISTRATOR, &ChatHandler::HandleWpExportCommand, "", NULL },
+ { "import", SEC_ADMINISTRATOR, &ChatHandler::HandleWpImportCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand debugCommandTable[] =
+ {
+ { "inarc", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugInArcCommand, "", NULL },
+ { "spellfail", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugSpellFailCommand, "", NULL },
+ { "setpoi", SEC_ADMINISTRATOR, &ChatHandler::HandleSetPoiCommand, "", NULL },
+ { "qpartymsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestPartyMsgCommand, "", NULL },
+ { "qinvalidmsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestInvalidMsgCommand, "", NULL },
+ { "equiperr", SEC_ADMINISTRATOR, &ChatHandler::HandleEquipErrorCommand, "", NULL },
+ { "sellerr", SEC_ADMINISTRATOR, &ChatHandler::HandleSellErrorCommand, "", NULL },
+ { "buyerr", SEC_ADMINISTRATOR, &ChatHandler::HandleBuyErrorCommand, "", NULL },
+ { "sendopcode", SEC_ADMINISTRATOR, &ChatHandler::HandleSendOpcodeCommand, "", NULL },
+ { "uws", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdateWorldStateCommand, "", NULL },
+ { "ps", SEC_ADMINISTRATOR, &ChatHandler::HandlePlaySound2Command, "", NULL },
+ { "scn", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChannelNotifyCommand, "", NULL },
+ { "scm", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChatMsgCommand, "", NULL },
+ { "getitemstate", SEC_ADMINISTRATOR, &ChatHandler::HandleGetItemState, "", NULL },
+ { "playsound", SEC_MODERATOR, &ChatHandler::HandlePlaySoundCommand, "", NULL },
+ { "update", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdate, "", NULL },
+ { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL },
+ { "getvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleGetValue, "", NULL },
+ { "Mod32Value", SEC_ADMINISTRATOR, &ChatHandler::HandleMod32Value, "", NULL },
+ { "anim", SEC_GAMEMASTER, &ChatHandler::HandleAnimCommand, "", NULL },
+ { "lootrecipient", SEC_GAMEMASTER, &ChatHandler::HandleGetLootRecipient, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand eventCommandTable[] =
+ {
+ { "activelist", SEC_GAMEMASTER, &ChatHandler::HandleEventActiveListCommand, "", NULL },
+ { "start", SEC_GAMEMASTER, &ChatHandler::HandleEventStartCommand, "", NULL },
+ { "stop", SEC_GAMEMASTER, &ChatHandler::HandleEventStopCommand, "", NULL },
+ { "", SEC_GAMEMASTER, &ChatHandler::HandleEventInfoCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand learnCommandTable[] =
+ {
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllCommand, "", NULL },
+ { "all_gm", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllGMCommand, "", NULL },
+ { "all_crafts", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllCraftsCommand, "", NULL },
+ { "all_default", SEC_MODERATOR, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL },
+ { "all_lang", SEC_MODERATOR, &ChatHandler::HandleLearnAllLangCommand, "", NULL },
+ { "all_myclass", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL },
+ { "all_myspells", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL },
+ { "all_mytalents", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL },
+ { "all_recipes", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand reloadCommandTable[] =
+ {
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllCommand, "", NULL },
+ { "all_quest", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllQuestCommand, "", NULL },
+ { "all_loot", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllLootCommand, "", NULL },
+ { "all_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL },
+ { "all_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllSpellCommand, "", NULL },
+ { "all_item", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllItemCommand, "", NULL },
+
+ { "config", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadConfigCommand, "", NULL },
+
+ { "areatrigger_tavern", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL },
+ { "areatrigger_teleport", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL },
+ { "areatrigger_involvedrelation",SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestAreaTriggersCommand, "", NULL },
+ { "event_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL },
+ { "command", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommandCommand, "", NULL },
+ { "creature_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL },
+ { "creature_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL },
+ { "creature_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL },
+ { "disenchant_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL },
+ { "fishing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL },
+ { "game_graveyard_zone", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL },
+ { "gameobject_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestInvRelationsCommand, "", NULL },
+ { "gameobject_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL },
+ { "gameobject_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL },
+ { "gameobject_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL },
+ { "item_enchantment_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
+ { "item_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
+ { "mangos_string", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
+ { "page_text", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadPageTextsCommand, "", NULL },
+ { "pickpocketing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL},
+ { "prospecting_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL },
+ { "quest_mail_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL },
+ { "quest_end_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL },
+ { "quest_start_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL },
+ { "quest_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL },
+ { "reference_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL },
+ { "reserved_name", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadReservedNameCommand, "", NULL },
+ { "skill_discovery_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL },
+ { "skill_extra_item_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL },
+ { "skill_fishing_base_level", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL },
+ { "skinning_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL },
+ { "spell_affect", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL },
+ { "spell_chain", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellChainCommand, "", NULL },
+ { "spell_elixir", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellElixirCommand, "", NULL },
+ { "spell_learn_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL },
+ { "spell_pet_auras", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL },
+ { "spell_proc_event", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL },
+ { "spell_script_target", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptTargetCommand, "", NULL },
+ { "spell_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL },
+ { "spell_target_position", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL },
+ { "spell_threats", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand honorCommandTable[] =
+ {
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddHonorCommand, "", NULL },
+ { "addkill", SEC_GAMEMASTER, &ChatHandler::HandleHonorAddKillCommand, "", NULL },
+ { "update", SEC_GAMEMASTER, &ChatHandler::HandleUpdateHonorFieldsCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand guildCommandTable[] =
+ {
+ { "create", SEC_GAMEMASTER, &ChatHandler::HandleGuildCreateCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleGuildDeleteCommand, "", NULL },
+ { "invite", SEC_GAMEMASTER, &ChatHandler::HandleGuildInviteCommand, "", NULL },
+ { "uninvite", SEC_GAMEMASTER, &ChatHandler::HandleGuildUninviteCommand, "", NULL },
+ { "rank", SEC_GAMEMASTER, &ChatHandler::HandleGuildRankCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand lookupCommandTable[] =
+ {
+ { "area", SEC_MODERATOR, &ChatHandler::HandleLookupAreaCommand, "", NULL },
+ { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupCreatureCommand, "", NULL },
+ { "event", SEC_GAMEMASTER, &ChatHandler::HandleLookupEventCommand, "", NULL },
+ { "faction", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupFactionCommand, "", NULL },
+ { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemCommand, "", NULL },
+ { "itemset", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemSetCommand, "", NULL },
+ { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupObjectCommand, "", NULL },
+ { "quest", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupQuestCommand, "", NULL },
+ { "skill", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSkillCommand, "", NULL },
+ { "spell", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSpellCommand, "", NULL },
+ { "tele", SEC_MODERATOR, &ChatHandler::HandleLookupTeleCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand resetCommandTable[] =
+ {
+ { "honor", SEC_ADMINISTRATOR, &ChatHandler::HandleResetHonorCommand, "", NULL },
+ { "level", SEC_ADMINISTRATOR, &ChatHandler::HandleResetLevelCommand, "", NULL },
+ { "spells", SEC_ADMINISTRATOR, &ChatHandler::HandleResetSpellsCommand, "", NULL },
+ { "stats", SEC_ADMINISTRATOR, &ChatHandler::HandleResetStatsCommand, "", NULL },
+ { "talents", SEC_ADMINISTRATOR, &ChatHandler::HandleResetTalentsCommand, "", NULL },
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleResetAllCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand castCommandTable[] =
+ {
+ { "back", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL },
+ { "dist", SEC_ADMINISTRATOR, &ChatHandler::HandleCastDistCommand, "", NULL },
+ { "self", SEC_ADMINISTRATOR, &ChatHandler::HandleCastSelfCommand, "", NULL },
+ { "target", SEC_ADMINISTRATOR, &ChatHandler::HandleCastTargetCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleCastCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand pdumpCommandTable[] =
+ {
+ { "load", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadPDumpCommand, "", NULL },
+ { "write", SEC_ADMINISTRATOR, &ChatHandler::HandleWritePDumpCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand listCommandTable[] =
+ {
+ { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleListCreatureCommand, "", NULL },
+ { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleListItemCommand, "", NULL },
+ { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleListObjectCommand, "", NULL },
+ { "auras", SEC_ADMINISTRATOR, &ChatHandler::HandleListAurasCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand teleCommandTable[] =
+ {
+ { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddTeleCommand, "", NULL },
+ { "del", SEC_ADMINISTRATOR, &ChatHandler::HandleDelTeleCommand, "", NULL },
+ { "name", SEC_MODERATOR, &ChatHandler::HandleNameTeleCommand, "", NULL },
+ { "group", SEC_MODERATOR, &ChatHandler::HandleGroupTeleCommand, "", NULL },
+ { "", SEC_MODERATOR, &ChatHandler::HandleTeleCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand npcCommandTable[] =
+ {
+ { "say", SEC_MODERATOR, &ChatHandler::HandleSayCommand, "", NULL },
+ { "whisper", SEC_MODERATOR, &ChatHandler::HandleNpcWhisperCommand, "", NULL },
+ { "yell", SEC_MODERATOR, &ChatHandler::HandleYellCommand, "", NULL },
+ { "textemote", SEC_MODERATOR, &ChatHandler::HandleTextEmoteCommand, "", NULL },
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddSpwCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelCreatureCommand, "", NULL },
+ { "spawndist", SEC_GAMEMASTER, &ChatHandler::HandleSpawnDistCommand, "", NULL },
+ { "spawntime", SEC_GAMEMASTER, &ChatHandler::HandleSpawnTimeCommand, "", NULL },
+ { "factionid", SEC_GAMEMASTER, &ChatHandler::HandleFactionIdCommand, "", NULL },
+ { "addmove", SEC_GAMEMASTER, &ChatHandler::HandleAddMoveCommand, "", NULL },
+ { "setmovetype", SEC_GAMEMASTER, &ChatHandler::HandleSetMoveTypeCommand, "", NULL },
+ { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveCreatureCommand, "", NULL },
+ { "changelevel", SEC_GAMEMASTER, &ChatHandler::HandleChangeLevelCommand, "", NULL },
+ { "setmodel", SEC_GAMEMASTER, &ChatHandler::HandleSetModelCommand, "", NULL },
+ { "additem", SEC_GAMEMASTER, &ChatHandler::HandleAddVendorItemCommand, "", NULL },
+ { "delitem", SEC_GAMEMASTER, &ChatHandler::HandleDelVendorItemCommand, "", NULL },
+ { "flag", SEC_GAMEMASTER, &ChatHandler::HandleNPCFlagCommand, "", NULL },
+ { "changeentry", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeEntryCommand, "", NULL },
+ { "info", SEC_ADMINISTRATOR, &ChatHandler::HandleNpcInfoCommand, "", NULL },
+ { "playemote", SEC_ADMINISTRATOR, &ChatHandler::HandlePlayEmoteCommand, "", NULL },
+
+ //{ TODO: fix or remove this commands
+ { "name", SEC_GAMEMASTER, &ChatHandler::HandleNameCommand, "", NULL },
+ { "subname", SEC_GAMEMASTER, &ChatHandler::HandleSubNameCommand, "", NULL },
+ { "addweapon", SEC_ADMINISTRATOR, &ChatHandler::HandleAddWeaponCommand, "", NULL },
+ //}
+
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand goCommandTable[] =
+ {
+ { "grid", SEC_MODERATOR, &ChatHandler::HandleGoGridCommand, "", NULL },
+ { "creature", SEC_GAMEMASTER, &ChatHandler::HandleGoCreatureCommand, "", NULL },
+ { "object", SEC_GAMEMASTER, &ChatHandler::HandleGoObjectCommand, "", NULL },
+ { "trigger", SEC_GAMEMASTER, &ChatHandler::HandleGoTriggerCommand, "", NULL },
+ { "graveyard", SEC_GAMEMASTER, &ChatHandler::HandleGoGraveyardCommand, "", NULL },
+ { "zonexy", SEC_MODERATOR, &ChatHandler::HandleGoZoneXYCommand, "", NULL },
+ { "xy", SEC_MODERATOR, &ChatHandler::HandleGoXYCommand, "", NULL },
+ { "xyz", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
+ { "", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand gobjectCommandTable[] =
+ {
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleGameObjectCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelObjectCommand, "", NULL },
+ { "target", SEC_GAMEMASTER, &ChatHandler::HandleTargetObjectCommand, "", NULL },
+ { "turn", SEC_GAMEMASTER, &ChatHandler::HandleTurnObjectCommand, "", NULL },
+ { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveObjectCommand, "", NULL },
+ { "near", SEC_ADMINISTRATOR, &ChatHandler::HandleNearObjectCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand questCommandTable[] =
+ {
+ { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddQuest, "", NULL },
+ { "complete", SEC_ADMINISTRATOR, &ChatHandler::HandleCompleteQuest, "", NULL },
+ { "remove", SEC_ADMINISTRATOR, &ChatHandler::HandleRemoveQuest, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand gmCommandTable[] =
+ {
+ { "list", SEC_PLAYER, &ChatHandler::HandleGMListCommand, "", NULL },
+ { "visible", SEC_MODERATOR, &ChatHandler::HandleVisibleCommand, "", NULL },
+ { "fly", SEC_ADMINISTRATOR, &ChatHandler::HandleFlyModeCommand, "", NULL },
+ { "", SEC_MODERATOR, &ChatHandler::HandleGMmodeCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand instanceCommandTable[] =
+ {
+ { "listbinds", SEC_MODERATOR, &ChatHandler::HandleInstanceListBindsCommand, "", NULL },
+ { "unbind", SEC_MODERATOR, &ChatHandler::HandleInstanceUnbindCommand, "", NULL },
+ { "stats", SEC_MODERATOR, &ChatHandler::HandleInstanceStatsCommand, "", NULL },
+ { "savedata", SEC_MODERATOR, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "gm", SEC_MODERATOR, NULL, "", gmCommandTable },
+ { "npc", SEC_MODERATOR, NULL, "", npcCommandTable },
+ { "go", SEC_MODERATOR, NULL, "", goCommandTable },
+ { "learn", SEC_MODERATOR, NULL, "", learnCommandTable },
+ { "modify", SEC_MODERATOR, NULL, "", modifyCommandTable },
+ { "debug", SEC_MODERATOR, NULL, "", debugCommandTable },
+ { "tele", SEC_MODERATOR, NULL, "", teleCommandTable },
+ { "event", SEC_GAMEMASTER, NULL, "", eventCommandTable },
+ { "gobject", SEC_GAMEMASTER, NULL, "", gobjectCommandTable },
+ { "honor", SEC_GAMEMASTER, NULL, "", honorCommandTable },
+ { "wp", SEC_GAMEMASTER, NULL, "", wpCommandTable },
+ { "quest", SEC_ADMINISTRATOR, NULL, "", questCommandTable },
+ { "reload", SEC_ADMINISTRATOR, NULL, "", reloadCommandTable },
+ { "list", SEC_ADMINISTRATOR, NULL, "", listCommandTable },
+ { "lookup", SEC_ADMINISTRATOR, NULL, "", lookupCommandTable },
+ { "pdump", SEC_ADMINISTRATOR, NULL, "", pdumpCommandTable },
+ { "guild", SEC_ADMINISTRATOR, NULL, "", guildCommandTable },
+ { "cast", SEC_ADMINISTRATOR, NULL, "", castCommandTable },
+ { "reset", SEC_ADMINISTRATOR, NULL, "", resetCommandTable },
+ { "instance", SEC_ADMINISTRATOR, NULL, "", instanceCommandTable },
+ { "server", SEC_ADMINISTRATOR, NULL, "", serverCommandTable },
+
+ { "aura", SEC_ADMINISTRATOR, &ChatHandler::HandleAuraCommand, "", NULL },
+ { "unaura", SEC_ADMINISTRATOR, &ChatHandler::HandleUnAuraCommand, "", NULL },
+ { "acct", SEC_PLAYER, &ChatHandler::HandleAcctCommand, "", NULL },
+ { "announce", SEC_MODERATOR, &ChatHandler::HandleAnnounceCommand, "", NULL },
+ { "notify", SEC_MODERATOR, &ChatHandler::HandleNotifyCommand, "", NULL },
+ { "goname", SEC_MODERATOR, &ChatHandler::HandleGonameCommand, "", NULL },
+ { "namego", SEC_MODERATOR, &ChatHandler::HandleNamegoCommand, "", NULL },
+ { "groupgo", SEC_MODERATOR, &ChatHandler::HandleGroupgoCommand, "", NULL },
+ { "commands", SEC_PLAYER, &ChatHandler::HandleCommandsCommand, "", NULL },
+ { "demorph", SEC_GAMEMASTER, &ChatHandler::HandleDeMorphCommand, "", NULL },
+ { "die", SEC_ADMINISTRATOR, &ChatHandler::HandleDieCommand, "", NULL },
+ { "revive", SEC_ADMINISTRATOR, &ChatHandler::HandleReviveCommand, "", NULL },
+ { "dismount", SEC_PLAYER, &ChatHandler::HandleDismountCommand, "", NULL },
+ { "gps", SEC_MODERATOR, &ChatHandler::HandleGPSCommand, "", NULL },
+ { "guid", SEC_GAMEMASTER, &ChatHandler::HandleGUIDCommand, "", NULL },
+ { "help", SEC_PLAYER, &ChatHandler::HandleHelpCommand, "", NULL },
+ { "itemmove", SEC_GAMEMASTER, &ChatHandler::HandleItemMoveCommand, "", NULL },
+ { "cooldown", SEC_ADMINISTRATOR, &ChatHandler::HandleCooldownCommand, "", NULL },
+ { "unlearn", SEC_ADMINISTRATOR, &ChatHandler::HandleUnLearnCommand, "", NULL },
+ { "distance", SEC_ADMINISTRATOR, &ChatHandler::HandleGetDistanceCommand, "", NULL },
+ { "recall", SEC_MODERATOR, &ChatHandler::HandleRecallCommand, "", NULL },
+ { "save", SEC_PLAYER, &ChatHandler::HandleSaveCommand, "", NULL },
+ { "saveall", SEC_MODERATOR, &ChatHandler::HandleSaveAllCommand, "", NULL },
+ { "kick", SEC_GAMEMASTER, &ChatHandler::HandleKickPlayerCommand, "", NULL },
+ { "security", SEC_ADMINISTRATOR, &ChatHandler::HandleSecurityCommand, "", NULL },
+ { "ban", SEC_ADMINISTRATOR, &ChatHandler::HandleBanCommand, "", NULL },
+ { "unban", SEC_ADMINISTRATOR, &ChatHandler::HandleUnBanCommand, "", NULL },
+ { "baninfo", SEC_ADMINISTRATOR, &ChatHandler::HandleBanInfoCommand, "", NULL },
+ { "banlist", SEC_ADMINISTRATOR, &ChatHandler::HandleBanListCommand, "", NULL },
+ { "plimit", SEC_ADMINISTRATOR, &ChatHandler::HandlePLimitCommand, "", NULL },
+ { "start", SEC_PLAYER, &ChatHandler::HandleStartCommand, "", NULL },
+ { "taxicheat", SEC_MODERATOR, &ChatHandler::HandleTaxiCheatCommand, "", NULL },
+ { "allowmove", SEC_ADMINISTRATOR, &ChatHandler::HandleAllowMovementCommand, "", NULL },
+ { "linkgrave", SEC_ADMINISTRATOR, &ChatHandler::HandleLinkGraveCommand, "", NULL },
+ { "neargrave", SEC_ADMINISTRATOR, &ChatHandler::HandleNearGraveCommand, "", NULL },
+ { "transport", SEC_ADMINISTRATOR, &ChatHandler::HandleSpawnTransportCommand, "", NULL },
+ { "explorecheat", SEC_ADMINISTRATOR, &ChatHandler::HandleExploreCheatCommand, "", NULL },
+ { "hover", SEC_ADMINISTRATOR, &ChatHandler::HandleHoverCommand, "", NULL },
+ { "levelup", SEC_ADMINISTRATOR, &ChatHandler::HandleLevelUpCommand, "", NULL },
+ { "showarea", SEC_ADMINISTRATOR, &ChatHandler::HandleShowAreaCommand, "", NULL },
+ { "hidearea", SEC_ADMINISTRATOR, &ChatHandler::HandleHideAreaCommand, "", NULL },
+ { "additem", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemCommand, "", NULL },
+ { "additemset", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemSetCommand, "", NULL },
+ { "bank", SEC_ADMINISTRATOR, &ChatHandler::HandleBankCommand, "", NULL },
+ { "wchange", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeWeather, "", NULL },
+ { "ticket", SEC_GAMEMASTER, &ChatHandler::HandleTicketCommand, "", NULL },
+ { "delticket", SEC_GAMEMASTER, &ChatHandler::HandleDelTicketCommand, "", NULL },
+ { "maxskill", SEC_ADMINISTRATOR, &ChatHandler::HandleMaxSkillCommand, "", NULL },
+ { "setskill", SEC_ADMINISTRATOR, &ChatHandler::HandleSetSkillCommand, "", NULL },
+ { "whispers", SEC_MODERATOR, &ChatHandler::HandleWhispersCommand, "", NULL },
+ { "pinfo", SEC_GAMEMASTER, &ChatHandler::HandlePInfoCommand, "", NULL },
+ { "password", SEC_PLAYER, &ChatHandler::HandlePasswordCommand, "", NULL },
+ { "lockaccount", SEC_PLAYER, &ChatHandler::HandleLockAccountCommand, "", NULL },
+ { "respawn", SEC_ADMINISTRATOR, &ChatHandler::HandleRespawnCommand, "", NULL },
+ { "sendmail", SEC_MODERATOR, &ChatHandler::HandleSendMailCommand, "", NULL },
+ { "rename", SEC_GAMEMASTER, &ChatHandler::HandleRenameCommand, "", NULL },
+ { "loadscripts", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadScriptsCommand, "", NULL },
+ { "mute", SEC_GAMEMASTER, &ChatHandler::HandleMuteCommand, "", NULL },
+ { "unmute", SEC_GAMEMASTER, &ChatHandler::HandleUnmuteCommand, "", NULL },
+ { "movegens", SEC_ADMINISTRATOR, &ChatHandler::HandleMovegensCommand, "", NULL },
+ { "cometome", SEC_ADMINISTRATOR, &ChatHandler::HandleComeToMeCommand, "", NULL },
+ { "damage", SEC_ADMINISTRATOR, &ChatHandler::HandleDamageCommand, "", NULL },
+ { "combatstop", SEC_GAMEMASTER, &ChatHandler::HandleCombatStopCommand, "", NULL },
+
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ if(load_command_table)
+ {
+ load_command_table = false;
+
+ QueryResult *result = WorldDatabase.Query("SELECT name,security,help FROM command");
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ std::string name = fields[0].GetCppString();
+ for(uint32 i = 0; commandTable[i].Name != NULL; i++)
+ {
+ if (name == commandTable[i].Name)
+ {
+ commandTable[i].SecurityLevel = (uint16)fields[1].GetUInt16();
+ commandTable[i].Help = fields[2].GetCppString();
+ }
+ if(commandTable[i].ChildCommands != NULL)
+ {
+ ChatCommand *ptable = commandTable[i].ChildCommands;
+ for(uint32 j = 0; ptable[j].Name != NULL; j++)
+ {
+ // first case for "" named subcommand
+ if (ptable[j].Name[0]=='\0' && name == commandTable[i].Name ||
+ name == fmtstring("%s %s", commandTable[i].Name, ptable[j].Name) )
+ {
+ ptable[j].SecurityLevel = (uint16)fields[1].GetUInt16();
+ ptable[j].Help = fields[2].GetCppString();
+ }
+ }
+ }
+ }
+ } while(result->NextRow());
+ delete result;
+ }
+ }
+
+ return commandTable;
+}
+
+const char *ChatHandler::GetMangosString(int32 entry)
+{
+ return m_session->GetMangosString(entry);
+}
+
+bool ChatHandler::hasStringAbbr(const char* s1, const char* s2)
+{
+ for(;;)
+ {
+ if( !*s2 )
+ return true;
+ else if( !*s1 )
+ return false;
+ else if( tolower( *s1 ) != tolower( *s2 ) )
+ return false;
+ ++s1; ++s2;
+ }
+}
+
+void ChatHandler::SendSysMessage(const char *str)
+{
+ WorldPacket data;
+
+ // need copy to prevent corruption by strtok call in LineFromMessage original string
+ char* buf = strdup(str);
+ char* pos = buf;
+
+ while(char* line = LineFromMessage(pos))
+ {
+ FillSystemMessageData(&data, line);
+ m_session->SendPacket(&data);
+ }
+
+ free(buf);
+}
+
+void ChatHandler::SendGlobalSysMessage(const char *str)
+{
+ WorldPacket data;
+
+ // need copy to prevent corruption by strtok call in LineFromMessage original string
+ char* buf = strdup(str);
+ char* pos = buf;
+
+ while(char* line = LineFromMessage(pos))
+ {
+ FillSystemMessageData(&data, line);
+ sWorld.SendGlobalMessage(&data);
+ }
+
+ free(buf);
+}
+
+void ChatHandler::SendSysMessage(int32 entry)
+{
+ SendSysMessage(GetMangosString(entry));
+}
+
+void ChatHandler::PSendSysMessage(int32 entry, ...)
+{
+ const char *format = GetMangosString(entry);
+ va_list ap;
+ char str [1024];
+ va_start(ap, entry);
+ vsnprintf(str,1024,format, ap );
+ va_end(ap);
+ SendSysMessage(str);
+}
+
+void ChatHandler::PSendSysMessage(const char *format, ...)
+{
+ va_list ap;
+ char str [1024];
+ va_start(ap, format);
+ vsnprintf(str,1024,format, ap );
+ va_end(ap);
+ SendSysMessage(str);
+}
+
+bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcmd)
+{
+ char const* oldtext = text;
+ std::string cmd = "";
+
+ while (*text != ' ' && *text != '\0')
+ {
+ cmd += *text;
+ ++text;
+ }
+
+ while (*text == ' ') ++text;
+
+ if(!cmd.length())
+ return false;
+
+ for(uint32 i = 0; table[i].Name != NULL; i++)
+ {
+ // allow pass "" command name in table
+ if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, cmd.c_str()))
+ continue;
+
+ // select subcommand from child commands list
+ if(table[i].ChildCommands != NULL)
+ {
+ if(!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd))
+ {
+ if(text && text[0] != '\0')
+ SendSysMessage(LANG_NO_SUBCMD);
+ else
+ SendSysMessage(LANG_CMD_SYNTAX);
+
+ ShowHelpForCommand(table[i].ChildCommands,text);
+ }
+
+ return true;
+ }
+
+ // check security level only for simple command (without child commands)
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ SetSentErrorMessage(false);
+ // table[i].Name == "" is special case: send original command to handler
+ if((this->*(table[i].Handler))(strlen(table[i].Name)!=0 ? text : oldtext))
+ {
+ if(table[i].SecurityLevel > SEC_PLAYER)
+ {
+ Player* p = m_session->GetPlayer();
+ uint64 sel_guid = p->GetSelection();
+ sLog.outCommand("Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s (GUID: %u)]",
+ fullcmd.c_str(),p->GetName(),m_session->GetAccountId(),p->GetPositionX(),p->GetPositionY(),p->GetPositionZ(),p->GetMapId(),
+ GetLogNameForGuid(sel_guid),GUID_LOPART(sel_guid));
+ }
+ }
+ // some commands have custom error messages. Don't send the default one in these cases.
+ else if(!sentErrorMessage)
+ {
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+ else
+ SendSysMessage(LANG_CMD_SYNTAX);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+int ChatHandler::ParseCommands(const char* text)
+{
+ ASSERT(text);
+ ASSERT(*text);
+
+ //if(m_session->GetSecurity() == 0)
+ // return 0;
+
+ if(text[0] != '!' && text[0] != '.')
+ return 0;
+
+ // ignore single . and ! in line
+ if(strlen(text) < 2)
+ return 0;
+
+ // ignore messages staring from many dots.
+ if(text[0] == '.' && text[1] == '.' || text[0] == '!' && text[1] == '!')
+ return 0;
+
+ ++text;
+
+ std::string fullcmd = text; // original `text` can't be used. It content destroyed in command code processing.
+
+ if(!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
+ SendSysMessage(LANG_NO_CMD);
+
+ return 1;
+}
+
+bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
+{
+ std::string list;
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, subcmd))
+ continue;
+
+ (list += "\n ") += table[i].Name;
+ }
+
+ if(list.empty())
+ return false;
+
+ if(table==getCommandTable())
+ {
+ SendSysMessage(LANG_AVIABLE_CMD);
+ PSendSysMessage("%s",list.c_str());
+ }
+ else
+ PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str());
+
+ return true;
+}
+
+bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
+{
+ if(*cmd)
+ {
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, cmd))
+ continue;
+
+ // have subcommand
+ char const* subcmd = (*cmd) ? strtok(NULL, " ") : "";
+
+ if(table[i].ChildCommands && subcmd && *subcmd)
+ {
+ if(ShowHelpForCommand(table[i].ChildCommands, subcmd))
+ return true;
+ }
+
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+
+ if(table[i].ChildCommands)
+ if(ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : ""))
+ return true;
+
+ return !table[i].Help.empty();
+ }
+ }
+ else
+ {
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if(strlen(table[i].Name))
+ continue;
+
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+
+ if(table[i].ChildCommands)
+ if(ShowHelpForSubCommands(table[i].ChildCommands,"",""))
+ return true;
+
+ return !table[i].Help.empty();
+ }
+ }
+
+ return ShowHelpForSubCommands(table,"",cmd);
+}
+
+//Note: target_guid used only in CHAT_MSG_WHISPER_INFORM mode (in this case channelName ignored)
+void ChatHandler::FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker)
+{
+ uint32 messageLength = (message ? strlen(message) : 0) + 1;
+
+ data->Initialize(SMSG_MESSAGECHAT, 100); // guess size
+ *data << uint8(type);
+ if ((type != CHAT_MSG_CHANNEL && type != CHAT_MSG_WHISPER) || language == LANG_ADDON)
+ *data << uint32(language);
+ else
+ *data << uint32(LANG_UNIVERSAL);
+
+ switch(type)
+ {
+ case CHAT_MSG_SAY:
+ case CHAT_MSG_PARTY:
+ case CHAT_MSG_RAID:
+ case CHAT_MSG_GUILD:
+ case CHAT_MSG_OFFICER:
+ case CHAT_MSG_YELL:
+ case CHAT_MSG_WHISPER:
+ case CHAT_MSG_CHANNEL:
+ case CHAT_MSG_RAID_LEADER:
+ case CHAT_MSG_RAID_WARNING:
+ case CHAT_MSG_BG_SYSTEM_NEUTRAL:
+ case CHAT_MSG_BG_SYSTEM_ALLIANCE:
+ case CHAT_MSG_BG_SYSTEM_HORDE:
+ case CHAT_MSG_BATTLEGROUND:
+ case CHAT_MSG_BATTLEGROUND_LEADER:
+ target_guid = session ? session->GetPlayer()->GetGUID() : 0;
+ break;
+ case CHAT_MSG_MONSTER_SAY:
+ case CHAT_MSG_MONSTER_PARTY:
+ case CHAT_MSG_MONSTER_YELL:
+ case CHAT_MSG_MONSTER_WHISPER:
+ case CHAT_MSG_MONSTER_EMOTE:
+ case CHAT_MSG_RAID_BOSS_WHISPER:
+ case CHAT_MSG_RAID_BOSS_EMOTE:
+ {
+ *data << uint64(speaker->GetGUID());
+ *data << uint32(0); // 2.1.0
+ *data << uint32(strlen(speaker->GetName()) + 1);
+ *data << speaker->GetName();
+ uint64 listener_guid = 0;
+ *data << uint64(listener_guid);
+ if(listener_guid && !IS_PLAYER_GUID(listener_guid))
+ {
+ *data << uint32(1); // string listener_name_length
+ *data << uint8(0); // string listener_name
+ }
+ *data << uint32(messageLength);
+ *data << message;
+ *data << uint8(0);
+ return;
+ }
+ default:
+ if (type != CHAT_MSG_REPLY && type != CHAT_MSG_IGNORED && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
+ target_guid = 0; // only for CHAT_MSG_WHISPER_INFORM used original value target_guid
+ break;
+ }
+
+ *data << uint64(target_guid); // there 0 for BG messages
+ *data << uint32(0); // can be chat msg group or something
+
+ if (type == CHAT_MSG_CHANNEL)
+ {
+ ASSERT(channelName);
+ *data << channelName;
+ }
+
+ *data << uint64(target_guid);
+ *data << uint32(messageLength);
+ *data << message;
+ if(session != 0 && type != CHAT_MSG_REPLY && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
+ *data << uint8(session->GetPlayer()->chatTag());
+ else
+ *data << uint8(0);
+}
+
+Player * ChatHandler::getSelectedPlayer()
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ return m_session->GetPlayer();
+
+ return objmgr.GetPlayer(guid);
+}
+
+Unit* ChatHandler::getSelectedUnit()
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ return m_session->GetPlayer();
+
+ return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid);
+}
+
+Creature* ChatHandler::getSelectedCreature()
+{
+ return ObjectAccessor::GetCreatureOrPet(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection());
+}
+
+char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1)
+{
+ // skip empty
+ if(!text)
+ return NULL;
+
+ // skip speces
+ while(*text==' '||*text=='\t'||*text=='\b')
+ ++text;
+
+ if(!*text)
+ return NULL;
+
+ // return non link case
+ if(text[0]!='|')
+ return strtok(text, " ");
+
+ // [name] Shift-click form |color|linkType:key|h[name]|h|r
+ // or
+ // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
+
+ char* check = strtok(text, "|"); // skip color
+ if(!check)
+ return NULL; // end of data
+
+ char* cLinkType = strtok(NULL, ":"); // linktype
+ if(!cLinkType)
+ return NULL; // end of data
+
+ if(strcmp(cLinkType,linkType) != 0)
+ {
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
+ SendSysMessage(LANG_WRONG_LINK_TYPE);
+ return NULL;
+ }
+
+ char* cKeys = strtok(NULL, "|"); // extract keys and values
+ char* cKeysTail = strtok(NULL, "");
+
+ char* cKey = strtok(cKeys, ":|"); // extract key
+ if(something1)
+ *something1 = strtok(NULL, ":|"); // extract something
+
+ strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
+ return cKey;
+}
+
+char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1)
+{
+ // skip empty
+ if(!text)
+ return NULL;
+
+ // skip speces
+ while(*text==' '||*text=='\t'||*text=='\b')
+ ++text;
+
+ if(!*text)
+ return NULL;
+
+ // return non link case
+ if(text[0]!='|')
+ return strtok(text, " ");
+
+ // [name] Shift-click form |color|linkType:key|h[name]|h|r
+ // or
+ // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
+
+ char* check = strtok(text, "|"); // skip color
+ if(!check)
+ return NULL; // end of data
+
+ char* cLinkType = strtok(NULL, ":"); // linktype
+ if(!cLinkType)
+ return NULL; // end of data
+
+ for(int i = 0; linkTypes[i]; ++i)
+ {
+ if(strcmp(cLinkType,linkTypes[i]) == 0)
+ {
+ char* cKeys = strtok(NULL, "|"); // extract keys and values
+ char* cKeysTail = strtok(NULL, "");
+
+ char* cKey = strtok(cKeys, ":|"); // extract key
+ if(something1)
+ *something1 = strtok(NULL, ":|"); // extract something
+
+ strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
+ if(found_idx)
+ *found_idx = i;
+ return cKey;
+ }
+ }
+
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
+ SendSysMessage(LANG_WRONG_LINK_TYPE);
+ return NULL;
+}
+
+char const *fmtstring( char const *format, ... )
+{
+ va_list argptr;
+ #define MAX_FMT_STRING 32000
+ static char temp_buffer[MAX_FMT_STRING];
+ static char string[MAX_FMT_STRING];
+ static int index = 0;
+ char *buf;
+ int len;
+
+ va_start(argptr, format);
+ vsnprintf(temp_buffer,MAX_FMT_STRING, format, argptr);
+ va_end(argptr);
+
+ len = strlen(temp_buffer);
+
+ if( len >= MAX_FMT_STRING )
+ return "ERROR";
+
+ if (len + index >= MAX_FMT_STRING-1)
+ {
+ index = 0;
+ }
+
+ buf = &string[index];
+ memcpy( buf, temp_buffer, len+1 );
+
+ index += len + 1;
+
+ return buf;
+}
+
+GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry)
+{
+ Player* pl = m_session->GetPlayer();
+
+ GameObject* obj = ObjectAccessor::GetGameObject(*pl, MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT));
+
+ if(!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object
+ {
+ // search near player then
+ CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::GameObjectWithDbGUIDCheck go_check(*pl,lowguid);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(obj,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
+ }
+
+ return obj;
+}
+
+static char const* const spellTalentKeys[] = {
+ "Hspell",
+ "Htalent",
+ 0
+};
+
+uint32 ChatHandler::extractSpellIdFromLink(char* text)
+{
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ // number or [name] Shift-click form |color|Htalent:telen_id,rank|h[name]|h|r
+ int type = 0;
+ char* rankS = NULL;
+ char* idS = extractKeyFromLink(text,spellTalentKeys,&type,&rankS);
+ if(!idS)
+ return 0;
+
+ uint32 id = (uint32)atol(idS);
+
+ // spell
+ if(type==0)
+ return id;
+
+ // talent
+ TalentEntry const* talentEntry = sTalentStore.LookupEntry(id);
+ if(!talentEntry)
+ return 0;
+
+ int32 rank = rankS ? (uint32)atol(rankS) : 0;
+ if(rank >= 5)
+ return 0;
+
+ if(rank < 0)
+ rank = 0;
+
+ return talentEntry->RankID[rank];
+}
+
diff --git a/src/game/Chat.h b/src/game/Chat.h
new file mode 100644
index 00000000000..49b219e875e
--- /dev/null
+++ b/src/game/Chat.h
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_CHAT_H
+#define MANGOSSERVER_CHAT_H
+
+#include "SharedDefines.h"
+
+class ChatHandler;
+class WorldSession;
+class Creature;
+class Player;
+class Unit;
+
+struct LanguageDesc
+{
+ Language lang_id;
+ uint32 spell_id;
+ uint32 skill_id;
+};
+
+extern LanguageDesc lang_description[LANGUAGES_COUNT];
+
+LanguageDesc const* GetLanguageDescByID(uint32 lang);
+LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id);
+LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id);
+
+class ChatCommand
+{
+ public:
+ const char * Name;
+ uint32 SecurityLevel; // function pointer required correct align (use uint32)
+ bool (ChatHandler::*Handler)(const char* args);
+ std::string Help;
+ ChatCommand * ChildCommands;
+};
+
+class ChatHandler
+{
+ public:
+ explicit ChatHandler(WorldSession* session) : m_session(session) {}
+ explicit ChatHandler(Player* player) : m_session(player->GetSession()) {}
+ ~ChatHandler() {}
+
+ static void FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker);
+
+ void FillMessageData( WorldPacket *data, uint8 type, uint32 language, uint64 target_guid, const char* message)
+ {
+ FillMessageData( data, m_session, type, language, NULL, target_guid, message, NULL);
+ }
+
+ void FillSystemMessageData( WorldPacket *data, const char* message )
+ {
+ FillMessageData( data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, 0, message );
+ }
+
+ static char* LineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; }
+
+ const char *GetMangosString(int32 entry);
+
+ void SendSysMessage( const char *str);
+ void SendSysMessage( int32 entry);
+ void PSendSysMessage( const char *format, ...) ATTR_PRINTF(2,3);
+ void PSendSysMessage( int32 entry, ... );
+
+ int ParseCommands(const char* text);
+
+ protected:
+ bool hasStringAbbr(const char* s1, const char* s2);
+ void SendGlobalSysMessage(const char *str);
+
+ bool ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcommand);
+ bool ShowHelpForCommand(ChatCommand *table, const char* cmd);
+ bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd);
+
+ ChatCommand* getCommandTable();
+
+ bool HandleHelpCommand(const char* args);
+ bool HandleCommandsCommand(const char* args);
+ bool HandleAcctCommand(const char* args);
+ bool HandleStartCommand(const char* args);
+ bool HandleInfoCommand(const char* args);
+ bool HandleDismountCommand(const char* args);
+ bool HandleSaveCommand(const char* args);
+ bool HandleGMListCommand(const char* args);
+
+ bool HandleNamegoCommand(const char* args);
+ bool HandleGonameCommand(const char* args);
+ bool HandleGroupgoCommand(const char* args);
+ bool HandleRecallCommand(const char* args);
+ bool HandleAnnounceCommand(const char* args);
+ bool HandleNotifyCommand(const char* args);
+ bool HandleGMmodeCommand(const char* args);
+ bool HandleVisibleCommand(const char* args);
+ bool HandleGPSCommand(const char* args);
+ bool HandleTaxiCheatCommand(const char* args);
+ bool HandleWhispersCommand(const char* args);
+ bool HandleSayCommand(const char* args);
+ bool HandleNpcWhisperCommand(const char* args);
+ bool HandleYellCommand(const char* args);
+ bool HandlePlayEmoteCommand(const char* args);
+ bool HandleSendMailCommand(const char* args);
+ bool HandleNameTeleCommand(const char* args);
+ bool HandleGroupTeleCommand(const char* args);
+ bool HandleDrunkCommand(const char* args);
+
+ bool HandleEventActiveListCommand(const char* args);
+ bool HandleEventStartCommand(const char* args);
+ bool HandleEventStopCommand(const char* args);
+ bool HandleEventInfoCommand(const char* args);
+
+ bool HandleModifyKnownTitlesCommand(const char* args);
+ bool HandleModifyHPCommand(const char* args);
+ bool HandleModifyManaCommand(const char* args);
+ bool HandleModifyRageCommand(const char* args);
+ bool HandleModifyEnergyCommand(const char* args);
+ bool HandleModifyMoneyCommand(const char* args);
+ bool HandleModifyASpeedCommand(const char* args);
+ bool HandleModifySpeedCommand(const char* args);
+ bool HandleModifyBWalkCommand(const char* args);
+ bool HandleModifyFlyCommand(const char* args);
+ bool HandleModifySwimCommand(const char* args);
+ bool HandleModifyScaleCommand(const char* args);
+ bool HandleModifyMountCommand(const char* args);
+ bool HandleModifyBitCommand(const char* args);
+ bool HandleModifyFactionCommand(const char* args);
+ bool HandleModifySpellCommand(const char* args);
+ bool HandleModifyTalentCommand (const char* args);
+ bool HandleModifyHonorCommand (const char* args);
+ bool HandleModifyRepCommand(const char* args);
+ bool HandleModifyArenaCommand(const char* args);
+
+ bool HandleReloadCommand(const char* args);
+ bool HandleReloadAllCommand(const char* args);
+ bool HandleReloadAllAreaCommand(const char* args);
+ bool HandleReloadAllItemCommand(const char* args);
+ bool HandleReloadAllLootCommand(const char* args);
+ bool HandleReloadAllQuestCommand(const char* args);
+ bool HandleReloadAllScriptsCommand(const char* args);
+ bool HandleReloadAllSpellCommand(const char* args);
+
+ bool HandleReloadConfigCommand(const char* args);
+
+ bool HandleReloadAreaTriggerTavernCommand(const char* args);
+ bool HandleReloadAreaTriggerTeleportCommand(const char* args);
+ bool HandleReloadEventScriptsCommand(const char* args);
+ bool HandleReloadCommandCommand(const char* args);
+ bool HandleReloadCreatureQuestRelationsCommand(const char* args);
+ bool HandleReloadCreatureQuestInvRelationsCommand(const char* args);
+ bool HandleReloadGameGraveyardZoneCommand(const char* args);
+ bool HandleReloadGameObjectScriptsCommand(const char* args);
+ bool HandleReloadGOQuestRelationsCommand(const char* args);
+ bool HandleReloadGOQuestInvRelationsCommand(const char* args);
+ bool HandleReloadLootTemplatesCreatureCommand(const char* args);
+ bool HandleReloadLootTemplatesDisenchantCommand(const char* args);
+ bool HandleReloadLootTemplatesFishingCommand(const char* args);
+ bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
+ bool HandleReloadLootTemplatesItemCommand(const char* args);
+ bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
+ bool HandleReloadLootTemplatesProspectingCommand(const char* args);
+ bool HandleReloadLootTemplatesReferenceCommand(const char* args);
+ bool HandleReloadLootTemplatesQuestMailCommand(const char* args);
+ bool HandleReloadLootTemplatesSkinningCommand(const char* args);
+ bool HandleReloadMangosStringCommand(const char* args);
+ bool HandleReloadQuestAreaTriggersCommand(const char* args);
+ bool HandleReloadQuestEndScriptsCommand(const char* args);
+ bool HandleReloadQuestStartScriptsCommand(const char* args);
+ bool HandleReloadQuestTemplateCommand(const char* args);
+ bool HandleReloadReservedNameCommand(const char*);
+ bool HandleReloadSkillDiscoveryTemplateCommand(const char* args);
+ bool HandleReloadSkillExtraItemTemplateCommand(const char* args);
+ bool HandleReloadSkillFishingBaseLevelCommand(const char* args);
+ bool HandleReloadSpellAffectCommand(const char* args);
+ bool HandleReloadSpellChainCommand(const char* args);
+ bool HandleReloadSpellElixirCommand(const char* args);
+ bool HandleReloadSpellLearnSpellCommand(const char* args);
+ bool HandleReloadSpellProcEventCommand(const char* args);
+ bool HandleReloadSpellScriptTargetCommand(const char* args);
+ bool HandleReloadSpellScriptsCommand(const char* args);
+ bool HandleReloadSpellTargetPositionCommand(const char* args);
+ bool HandleReloadSpellThreatsCommand(const char* args);
+ bool HandleReloadSpellPetAurasCommand(const char* args);
+ bool HandleReloadPageTextsCommand(const char* args);
+ bool HandleReloadItemEnchantementsCommand(const char* args);
+
+ bool HandleInstanceListBindsCommand(const char* args);
+ bool HandleInstanceUnbindCommand(const char* args);
+ bool HandleInstanceStatsCommand(const char* args);
+ bool HandleInstanceSaveDataCommand(const char * args);
+
+ bool HandleAddHonorCommand(const char* args);
+ bool HandleHonorAddKillCommand(const char* args);
+ bool HandleUpdateHonorFieldsCommand(const char* args);
+
+ bool HandleLoadScriptsCommand(const char* args);
+ bool HandleSendQuestPartyMsgCommand(const char* args);
+ bool HandleSendQuestInvalidMsgCommand(const char* args);
+
+ bool HandleDebugInArcCommand(const char* args);
+ bool HandleDebugSpellFailCommand(const char* args);
+
+ bool HandleGUIDCommand(const char* args);
+ bool HandleNameCommand(const char* args);
+ bool HandleSubNameCommand(const char* args);
+ bool HandleItemMoveCommand(const char* args);
+ bool HandleDelCreatureCommand(const char* args);
+ bool HandleDeMorphCommand(const char* args);
+ bool HandleAddVendorItemCommand(const char* args);
+ bool HandleDelVendorItemCommand(const char* args);
+ bool HandleAddMoveCommand(const char* args);
+ bool HandleSetMoveTypeCommand(const char* args);
+ bool HandleChangeLevelCommand(const char* args);
+ bool HandleSetPoiCommand(const char* args);
+ bool HandleEquipErrorCommand(const char* args);
+ bool HandleNPCFlagCommand(const char* args);
+ bool HandleSetModelCommand(const char* args);
+ bool HandleFactionIdCommand(const char* args);
+ bool HandleAddSpwCommand(const char* args);
+ bool HandleSpawnDistCommand(const char* args);
+ bool HandleSpawnTimeCommand(const char* args);
+ bool HandleGoCreatureCommand(const char* args);
+ bool HandleGoObjectCommand(const char* args);
+ bool HandleGoTriggerCommand(const char* args);
+ bool HandleGoGraveyardCommand(const char* args);
+ bool HandleTargetObjectCommand(const char* args);
+ bool HandleDelObjectCommand(const char* args);
+ bool HandleMoveCreatureCommand(const char* args);
+ bool HandleMoveObjectCommand(const char* args);
+ bool HandleTurnObjectCommand(const char* args);
+ bool HandlePInfoCommand(const char* args);
+ bool HandlePLimitCommand(const char* args);
+ bool HandleMuteCommand(const char* args);
+ bool HandleUnmuteCommand(const char* args);
+ bool HandleMovegensCommand(const char* args);
+
+ bool HandleBanCommand(const char* args);
+ bool HandleUnBanCommand(const char* args);
+ bool HandleBanInfoCommand(const char* args);
+ bool HandleBanListCommand(const char* args);
+ bool HandleIdleRestartCommand(const char* args);
+ bool HandleIdleShutDownCommand(const char* args);
+ bool HandleShutDownCommand(const char* args);
+ bool HandleRestartCommand(const char* args);
+ bool HandleSecurityCommand(const char* args);
+ bool HandleGoXYCommand(const char* args);
+ bool HandleGoXYZCommand(const char* args);
+ bool HandleGoZoneXYCommand(const char* args);
+ bool HandleGoGridCommand(const char* args);
+ bool HandleAddWeaponCommand(const char* args);
+ bool HandleAllowMovementCommand(const char* args);
+ bool HandleGoCommand(const char* args);
+ bool HandleLearnCommand(const char* args);
+ bool HandleLearnAllCommand(const char* args);
+ bool HandleLearnAllGMCommand(const char* args);
+ bool HandleLearnAllCraftsCommand(const char* args);
+ bool HandleLearnAllRecipesCommand(const char* args);
+ bool HandleLearnAllDefaultCommand(const char* args);
+ bool HandleLearnAllLangCommand(const char* args);
+ bool HandleLearnAllMyClassCommand(const char* args);
+ bool HandleLearnAllMySpellsCommand(const char* args);
+ bool HandleLearnAllMyTalentsCommand(const char* args);
+ bool HandleCooldownCommand(const char* args);
+ bool HandleUnLearnCommand(const char* args);
+ bool HandleGetDistanceCommand(const char* args);
+ bool HandleGameObjectCommand(const char* args);
+ bool HandleAnimCommand(const char* args);
+ bool HandlePlaySoundCommand(const char* args);
+ bool HandleStandStateCommand(const char* args);
+ bool HandleDieCommand(const char* args);
+ bool HandleDamageCommand(const char *args);
+ bool HandleReviveCommand(const char* args);
+ bool HandleMorphCommand(const char* args);
+ bool HandleAuraCommand(const char* args);
+ bool HandleUnAuraCommand(const char* args);
+ bool HandleLinkGraveCommand(const char* args);
+ bool HandleNearGraveCommand(const char* args);
+ bool HandleSpawnTransportCommand(const char* args);
+ bool HandleExploreCheatCommand(const char* args);
+ bool HandleTextEmoteCommand(const char* args);
+ bool HandleNpcInfoCommand(const char* args);
+ bool HandleHoverCommand(const char* args);
+ bool HandleLevelUpCommand(const char* args);
+ bool HandleShowAreaCommand(const char* args);
+ bool HandleHideAreaCommand(const char* args);
+ bool HandleAddItemCommand(const char* args);
+ bool HandleAddItemSetCommand(const char* args);
+ bool HandleLookupItemCommand(const char * args);
+ bool HandleLookupItemSetCommand(const char * args);
+ bool HandleGuildCreateCommand(const char* args);
+ bool HandleGuildInviteCommand(const char* args);
+ bool HandleGuildUninviteCommand(const char* args);
+ bool HandleGuildRankCommand(const char* args);
+ bool HandleGuildDeleteCommand(const char* args);
+ bool HandleUpdate(const char* args);
+ bool HandleBankCommand(const char* args);
+ bool HandleChangeWeather(const char* args);
+ bool HandleKickPlayerCommand(const char * args);
+ bool HandleTeleCommand(const char * args);
+ bool HandleAddTeleCommand(const char * args);
+ bool HandleDelTeleCommand(const char * args);
+ bool HandleListAurasCommand(const char * args);
+ bool HandleLookupTeleCommand(const char * args);
+
+ bool HandleResetHonorCommand(const char * args);
+ bool HandleResetLevelCommand(const char * args);
+ bool HandleResetSpellsCommand(const char * args);
+
+ bool HandleResetStatsCommand(const char * args);
+ bool HandleResetTalentsCommand(const char * args);
+
+ bool HandleResetAllCommand(const char * args);
+ bool HandleTicketCommand(const char* args);
+ bool HandleDelTicketCommand(const char* args);
+ bool HandleMaxSkillCommand(const char* args);
+ bool HandleSetSkillCommand(const char* args);
+ bool HandleListCreatureCommand(const char* args);
+ bool HandleListItemCommand(const char* args);
+ bool HandleListObjectCommand(const char* args);
+ bool HandleNearObjectCommand(const char* args);
+ bool HandleLookupAreaCommand(const char* args);
+ bool HandleLookupCreatureCommand(const char* args);
+ bool HandleLookupEventCommand(const char* args);
+ bool HandleLookupFactionCommand(const char * args);
+ bool HandleLookupObjectCommand(const char* args);
+ bool HandleLookupQuestCommand(const char* args);
+ bool HandleLookupSkillCommand(const char* args);
+ bool HandleLookupSpellCommand(const char* args);
+ bool HandlePasswordCommand(const char* args);
+ bool HandleLockAccountCommand(const char* args);
+ bool HandleRespawnCommand(const char* args);
+ bool HandleWpAddCommand(const char* args);
+ bool HandleWpModifyCommand(const char* args);
+ bool HandleWpShowCommand(const char* args);
+ bool HandleWpExportCommand(const char* args);
+ bool HandleWpImportCommand(const char* args);
+ bool HandleFlyModeCommand(const char* args);
+ bool HandleSendOpcodeCommand(const char* args);
+ bool HandleSellErrorCommand(const char* args);
+ bool HandleBuyErrorCommand(const char* args);
+ bool HandleUpdateWorldStateCommand(const char* args);
+ bool HandlePlaySound2Command(const char* args);
+ bool HandleSendChannelNotifyCommand(const char* args);
+ bool HandleSendChatMsgCommand(const char* args);
+ bool HandleRenameCommand(const char * args);
+ bool HandleLoadPDumpCommand(const char *args);
+ bool HandleWritePDumpCommand(const char *args);
+ bool HandleChangeEntryCommand(const char *args);
+ bool HandleCastCommand(const char *args);
+ bool HandleCastBackCommand(const char *args);
+ bool HandleCastDistCommand(const char *args);
+ bool HandleCastSelfCommand(const char *args);
+ bool HandleCastTargetCommand(const char *args);
+ bool HandleComeToMeCommand(const char *args);
+ bool HandleCombatStopCommand(const char *args);
+
+ //! Development Commands
+ bool HandleSetValue(const char* args);
+ bool HandleGetValue(const char* args);
+ bool HandleSet32Bit(const char* args);
+ bool HandleMod32Value(const char* args);
+ bool HandleAddQuest(const char * args);
+ bool HandleRemoveQuest(const char * args);
+ bool HandleCompleteQuest(const char * args);
+ bool HandleSaveAllCommand(const char* args);
+ bool HandleGetItemState(const char * args);
+ bool HandleGetLootRecipient(const char * args);
+
+ Player* getSelectedPlayer();
+ Creature* getSelectedCreature();
+ Unit* getSelectedUnit();
+ char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
+ char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
+ uint32 extractSpellIdFromLink(char* text);
+
+ GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry);
+
+ WorldSession * m_session;
+
+ // Utility methods for commands
+ void ShowTicket(uint64 guid, char const* text, char const* time);
+ uint32 GetTicketIDByNum(uint32 num);
+
+ void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
+ private:
+ // common global flag
+ static bool load_command_table;
+ bool sentErrorMessage;
+};
+#endif
+
+char const *fmtstring( char const *format, ... );
diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp
new file mode 100644
index 00000000000..dd3a711858d
--- /dev/null
+++ b/src/game/ChatHandler.cpp
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "Chat.h"
+#include "Database/DatabaseEnv.h"
+#include "ChannelMgr.h"
+#include "Group.h"
+#include "Guild.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "ScriptCalls.h"
+#include "Player.h"
+#include "SpellAuras.h"
+#include "Language.h"
+#include "Util.h"
+
+void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1);
+
+ uint32 type;
+ uint32 lang;
+
+ recv_data >> type;
+ recv_data >> lang;
+
+ if(type >= MAX_CHAT_MSG_TYPE)
+ {
+ sLog.outError("CHAT: Wrong message type received: %u", type);
+ return;
+ }
+
+ //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang );
+
+ // prevent talking at unknown language (cheating)
+ LanguageDesc const* langDesc = GetLanguageDescByID(lang);
+ if(!langDesc)
+ {
+ SendNotification("Unknown language");
+ return;
+ }
+ if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id))
+ {
+ // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
+ Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE);
+ bool foundAura = false;
+ for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue == lang)
+ {
+ foundAura = true;
+ break;
+ }
+ }
+ if(!foundAura)
+ {
+ SendNotification("You don't know that language");
+ return;
+ }
+ }
+
+ if(lang == LANG_ADDON)
+ {
+ // Disabled addon channel?
+ if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL))
+ return;
+ }
+ // LANG_ADDON should not be changed nor be affected by flood control
+ else
+ {
+ // send in universal language if player in .gmon mode (ignore spell effects)
+ if (_player->isGameMaster())
+ lang = LANG_UNIVERSAL;
+ else
+ {
+ // send in universal language in two side iteration allowed mode
+ if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
+ lang = LANG_UNIVERSAL;
+ else
+ {
+ switch(type)
+ {
+ case CHAT_MSG_PARTY:
+ case CHAT_MSG_RAID:
+ case CHAT_MSG_RAID_LEADER:
+ case CHAT_MSG_RAID_WARNING:
+ // allow two side chat at group channel if two side group allowed
+ if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
+ lang = LANG_UNIVERSAL;
+ break;
+ case CHAT_MSG_GUILD:
+ case CHAT_MSG_OFFICER:
+ // allow two side chat at guild channel if two side guild allowed
+ if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
+ lang = LANG_UNIVERSAL;
+ break;
+ }
+ }
+
+ // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
+ Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE);
+ if(!ModLangAuras.empty())
+ lang = ModLangAuras.front()->GetModifier()->m_miscvalue;
+ }
+
+ if (!_player->CanSpeak())
+ {
+ std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
+ SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
+ return;
+ }
+
+ if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
+ GetPlayer()->UpdateSpeakTime();
+ }
+
+ switch(type)
+ {
+ case CHAT_MSG_SAY:
+ case CHAT_MSG_EMOTE:
+ case CHAT_MSG_YELL:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(type == CHAT_MSG_SAY)
+ GetPlayer()->Say(msg, lang);
+ else if(type == CHAT_MSG_EMOTE)
+ GetPlayer()->TextEmote(msg);
+ else if(type == CHAT_MSG_YELL)
+ GetPlayer()->Yell(msg, lang);
+ } break;
+
+ case CHAT_MSG_WHISPER:
+ {
+ std::string to, msg;
+ recv_data >> to;
+ CHECK_PACKET_SIZE(recv_data,4+4+(to.size()+1)+1);
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(!normalizePlayerName(to))
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ break;
+ }
+
+ Player *player = objmgr.GetPlayer(to.c_str());
+ uint32 tSecurity = GetSecurity();
+ uint32 pSecurity = player ? player->GetSession()->GetSecurity() : 0;
+ if(!player || tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers())
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ return;
+ }
+
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER )
+ {
+ uint32 sidea = GetPlayer()->GetTeam();
+ uint32 sideb = player->GetTeam();
+ if( sidea != sideb )
+ {
+ WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
+ data<<to;
+ SendPacket(&data);
+ return;
+ }
+ }
+
+ GetPlayer()->Whisper(msg, lang,player->GetGUID());
+ } break;
+
+ case CHAT_MSG_PARTY:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data, group->GetMemberGroup(GetPlayer()->GetGUID()));
+ }
+ break;
+ case CHAT_MSG_GUILD:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if (GetPlayer()->GetGuildId())
+ {
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if (guild)
+ guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
+ }
+
+ break;
+ }
+ case CHAT_MSG_OFFICER:
+ {
+ std::string msg = "";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if (GetPlayer()->GetGuildId())
+ {
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if (guild)
+ guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
+ }
+ break;
+ }
+ case CHAT_MSG_RAID:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+ case CHAT_MSG_RAID_LEADER:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ if(msg.empty())
+ break;
+
+ if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
+ break;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+ case CHAT_MSG_RAID_WARNING:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_BATTLEGROUND:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_BATTLEGROUND_LEADER:
+ {
+ std::string msg="";
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL);
+ group->BroadcastPacket(&data);
+ } break;
+
+ case CHAT_MSG_CHANNEL:
+ {
+ std::string channel = "", msg = "";
+ recv_data >> channel;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(channel.size()+1)+1);
+
+ recv_data >> msg;
+
+ // strip invisible characters for non-addon messages
+ if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if(msg.empty())
+ break;
+
+ if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
+ {
+ if(Channel *chn = cMgr->GetChannel(channel,_player))
+ chn->Say(_player->GetGUID(),msg.c_str(),lang);
+ }
+ } break;
+
+ case CHAT_MSG_AFK:
+ {
+ std::string msg;
+ recv_data >> msg;
+
+ if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() )
+ {
+ if(!_player->isAFK())
+ {
+ if(msg.empty())
+ msg = GetMangosString(LANG_PLAYER_AFK_DEFAULT);
+ _player->afkMsg = msg;
+ }
+ _player->ToggleAFK();
+ if(_player->isAFK() && _player->isDND())
+ _player->ToggleDND();
+ }
+ } break;
+
+ case CHAT_MSG_DND:
+ {
+ std::string msg;
+ recv_data >> msg;
+
+ if(msg.empty() || !_player->isDND())
+ {
+ if(!_player->isDND())
+ {
+ if(msg.empty())
+ msg = GetMangosString(LANG_PLAYER_DND_DEFAULT);
+ _player->dndMsg = msg;
+ }
+ _player->ToggleDND();
+ if(_player->isDND() && _player->isAFK())
+ _player->ToggleAFK();
+ }
+ } break;
+
+ default:
+ sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang);
+ break;
+ }
+}
+
+void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data )
+{
+ if(!GetPlayer()->isAlive())
+ return;
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 emote;
+ recv_data >> emote;
+ GetPlayer()->HandleEmoteCommand(emote);
+}
+
+void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
+{
+ if(!GetPlayer()->isAlive())
+ return;
+
+ if (!GetPlayer()->CanSpeak())
+ {
+ std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
+ SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
+ return;
+ }
+
+ CHECK_PACKET_SIZE(recv_data,4+4+8);
+
+ uint32 text_emote, emoteNum;
+ uint64 guid;
+
+ recv_data >> text_emote;
+ recv_data >> emoteNum;
+ recv_data >> guid;
+
+ const char *nam = 0;
+ uint32 namlen = 1;
+
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
+ Creature *pCreature = dynamic_cast<Creature *>(unit);
+ if(unit)
+ {
+ nam = unit->GetName();
+ namlen = (nam ? strlen(nam) : 0) + 1;
+ }
+
+ EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote);
+ if (em)
+ {
+ uint32 emote_anim = em->textid;
+
+ WorldPacket data;
+
+ switch(emote_anim)
+ {
+ case EMOTE_STATE_SLEEP:
+ case EMOTE_STATE_SIT:
+ case EMOTE_STATE_KNEEL:
+ case EMOTE_ONESHOT_NONE:
+ break;
+ default:
+ GetPlayer()->HandleEmoteCommand(emote_anim);
+ break;
+ }
+
+ data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
+ data << GetPlayer()->GetGUID();
+ data << (uint32)text_emote;
+ data << emoteNum;
+ data << (uint32)namlen;
+ if( namlen > 1 )
+ {
+ data.append(nam, namlen);
+ }
+ else
+ {
+ data << (uint8)0x00;
+ }
+
+ GetPlayer()->SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true);
+
+ //Send scripted event call
+ if (pCreature && Script)
+ Script->ReceiveEmote(GetPlayer(),pCreature,text_emote);
+ }
+}
+
+void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ uint64 iguid;
+ uint8 unk;
+ //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED");
+
+ recv_data >> iguid;
+ recv_data >> unk; // probably related to spam reporting
+
+ Player *player = objmgr.GetPlayer(iguid);
+ if(!player || !player->GetSession())
+ return;
+
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(),NULL);
+ player->GetSession()->SendPacket(&data);
+}
diff --git a/src/game/CombatHandler.cpp b/src/game/CombatHandler.cpp
new file mode 100644
index 00000000000..047c639e44e
--- /dev/null
+++ b/src/game/CombatHandler.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "ObjectDefines.h"
+
+void WorldSession::HandleAttackSwingOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ DEBUG_LOG( "WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid) );
+
+ Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid);
+
+ if(!pEnemy)
+ {
+ if(!IS_UNIT_GUID(guid))
+ sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
+ else
+ sLog.outError( "WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid));
+
+ // stop attack state at client
+ SendAttackStop(NULL);
+ return;
+ }
+
+ if(_player->IsFriendlyTo(pEnemy) || pEnemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
+ {
+ sLog.outError( "WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid));
+
+ // stop attack state at client
+ SendAttackStop(pEnemy);
+ return;
+ }
+
+ if(!pEnemy->isAlive())
+ {
+ // client can generate swing to known dead target if autoswitch between autoshot and autohit is enabled in client options
+ // stop attack state at client
+ SendAttackStop(pEnemy);
+ return;
+ }
+
+ _player->Attack(pEnemy,true);
+}
+
+void WorldSession::HandleAttackStopOpcode( WorldPacket & /*recv_data*/ )
+{
+ GetPlayer()->AttackStop();
+}
+
+void WorldSession::HandleSetSheathedOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 sheathed;
+ recv_data >> sheathed;
+
+ //sLog.outDebug( "WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed );
+
+ GetPlayer()->SetSheath(sheathed);
+}
+
+void WorldSession::SendAttackStop(Unit const* enemy)
+{
+ WorldPacket data( SMSG_ATTACKSTOP, (4+20) ); // we guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid
+ data << uint32(0); // unk, can be 1 also
+ SendPacket(&data);
+}
diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp
new file mode 100644
index 00000000000..f1e7c2c1548
--- /dev/null
+++ b/src/game/ConfusedMovementGenerator.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "MapManager.h"
+#include "Opcodes.h"
+#include "ConfusedMovementGenerator.h"
+#include "DestinationHolderImp.h"
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Initialize(T &unit)
+{
+ const float wander_distance=11;
+ float x,y,z;
+ x = unit.GetPositionX();
+ y = unit.GetPositionY();
+ z = unit.GetPositionZ();
+ uint32 mapid=unit.GetMapId();
+
+ Map const* map = MapManager::Instance().GetBaseMap(mapid);
+
+ i_nextMove = 1;
+
+ bool is_water_ok, is_land_ok;
+ _InitSpecific(unit, is_water_ok, is_land_ok);
+
+ for(unsigned int idx=0; idx < MAX_CONF_WAYPOINTS+1; ++idx)
+ {
+ const float wanderX=wander_distance*rand_norm() - wander_distance/2;
+ const float wanderY=wander_distance*rand_norm() - wander_distance/2;
+
+ i_waypoints[idx][0] = x + wanderX;
+ i_waypoints[idx][1] = y + wanderY;
+
+ // prevent invalid coordinates generation
+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]);
+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]);
+
+ bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ // if generated wrong path just ignore
+ if( is_water && !is_water_ok || !is_water && !is_land_ok )
+ {
+ i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
+ i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
+ }
+ unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ i_waypoints[idx][2] = z;
+ }
+
+ unit.StopMoving();
+ unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ unit.addUnitState(UNIT_STAT_CONFUSED);
+}
+
+template<>
+void
+ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = creature.canSwim();
+ is_land_ok = creature.canWalk();
+}
+
+template<>
+void
+ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Reset(T &unit)
+{
+ i_nextMove = 1;
+ i_nextMoveTime.Reset(0);
+ i_destinationHolder.ResetUpdate();
+ unit.StopMoving();
+}
+
+template<class T>
+bool
+ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if(!&unit)
+ return true;
+
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ if( i_nextMoveTime.Passed() )
+ {
+ // currently moving, update location
+ Traveller<T> traveller(unit);
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false))
+ {
+ if( i_destinationHolder.HasArrived())
+ {
+ // arrived, stop and wait a bit
+ unit.StopMoving();
+
+ i_nextMove = urand(1,MAX_CONF_WAYPOINTS);
+ i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher
+ }
+ }
+ }
+ else
+ {
+ // waiting for next move
+ i_nextMoveTime.Update(diff);
+ if( i_nextMoveTime.Passed() )
+ {
+ // start moving
+ assert( i_nextMove <= MAX_CONF_WAYPOINTS );
+ const float x = i_waypoints[i_nextMove][0];
+ const float y = i_waypoints[i_nextMove][1];
+ const float z = i_waypoints[i_nextMove][2];
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ }
+ }
+ return true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Finalize(T &unit)
+{
+ unit.clearUnitState(UNIT_STAT_CONFUSED);
+}
+
+template void ConfusedMovementGenerator<Player>::Initialize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Finalize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Finalize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Reset(Player &player);
+template void ConfusedMovementGenerator<Creature>::Reset(Creature &creature);
+template bool ConfusedMovementGenerator<Player>::Update(Player &player, const uint32 &diff);
+template bool ConfusedMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff);
diff --git a/src/game/ConfusedMovementGenerator.h b/src/game/ConfusedMovementGenerator.h
new file mode 100644
index 00000000000..78955297395
--- /dev/null
+++ b/src/game/ConfusedMovementGenerator.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_RANDOMMOTIONGENERATOR_H
+#define MANGOS_RANDOMMOTIONGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+#define MAX_CONF_WAYPOINTS 24
+
+template<class T>
+class MANGOS_DLL_SPEC ConfusedMovementGenerator
+: public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
+{
+ public:
+ explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+
+ MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
+ private:
+ void _InitSpecific(T &, bool &, bool &);
+ TimeTracker i_nextMoveTime;
+ float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ uint32 i_nextMove;
+};
+#endif
diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp
new file mode 100644
index 00000000000..785c6b4f784
--- /dev/null
+++ b/src/game/Corpse.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Corpse.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "GossipDef.h"
+#include "World.h"
+
+Corpse::Corpse(CorpseType type) : WorldObject()
+{
+ m_objectType |= TYPEMASK_CORPSE;
+ m_objectTypeId = TYPEID_CORPSE;
+ // 2.3.2 - 0x58
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+
+ m_valuesCount = CORPSE_END;
+
+ m_type = type;
+
+ m_time = time(NULL);
+
+ lootForBody = false;
+}
+
+Corpse::~Corpse()
+{
+}
+
+void Corpse::AddToWorld()
+{
+ ///- Register the corpse for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Object::AddToWorld();
+}
+
+void Corpse::RemoveFromWorld()
+{
+ ///- Remove the corpse from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Object::RemoveFromWorld();
+}
+
+bool Corpse::Create( uint32 guidlow )
+{
+ Object::_Create(guidlow, 0, HIGHGUID_CORPSE);
+ return true;
+}
+
+bool Corpse::Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang )
+{
+ SetInstanceId(owner->GetInstanceId());
+
+ WorldObject::_Create(guidlow, HIGHGUID_CORPSE, mapid);
+
+ Relocate(x,y,z,ang);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",guidlow,owner->GetName(),x,y);
+ return false;
+ }
+
+ SetFloatValue( OBJECT_FIELD_SCALE_X, 1 );
+ SetFloatValue( CORPSE_FIELD_POS_X, x );
+ SetFloatValue( CORPSE_FIELD_POS_Y, y );
+ SetFloatValue( CORPSE_FIELD_POS_Z, z );
+ SetFloatValue( CORPSE_FIELD_FACING, ang );
+ SetUInt64Value( CORPSE_FIELD_OWNER, owner->GetGUID() );
+
+ m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY());
+
+ return true;
+}
+
+void Corpse::SaveToDB()
+{
+ // prevent DB data inconsistance problems and duplicates
+ CharacterDatabase.BeginTransaction();
+ DeleteFromDB();
+
+ std::ostringstream ss;
+ ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,zone,map,data,time,corpse_type,instance) VALUES ("
+ << GetGUIDLow() << ", " << GUID_LOPART(GetOwnerGUID()) << ", " << GetPositionX() << ", " << GetPositionY() << ", " << GetPositionZ() << ", "
+ << GetOrientation() << ", " << GetZoneId() << ", " << GetMapId() << ", '";
+ for(uint16 i = 0; i < m_valuesCount; i++ )
+ ss << GetUInt32Value(i) << " ";
+ ss << "'," << uint64(m_time) <<", " << uint32(GetType()) << ", " << int(GetInstanceId()) << ")";
+ CharacterDatabase.Execute( ss.str().c_str() );
+ CharacterDatabase.CommitTransaction();
+}
+
+void Corpse::DeleteBonesFromWorld()
+{
+ assert(GetType()==CORPSE_BONES);
+ Corpse* corpse = ObjectAccessor::GetCorpse(*this, GetGUID());
+
+ if (!corpse)
+ {
+ sLog.outError("Bones %u not found in world.", GetGUIDLow());
+ return;
+ }
+
+ AddObjectToRemoveList();
+}
+
+void Corpse::DeleteFromDB()
+{
+ if(GetType() == CORPSE_BONES)
+ // only specific bones
+ CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%d'", GetGUIDLow());
+ else
+ // all corpses (not bones)
+ 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);
+ if (!external)
+ // 0 1 2 3 4 5 6 7 8
+ result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid);
+
+ if( ! result )
+ {
+ sLog.outError("ERROR: Corpse (GUID: %u) not found in table `corpse`, can't load. ",guid);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+
+ if(!LoadFromDB(guid,fields))
+ {
+ if (!external) delete result;
+ return false;
+ }
+
+ if (!external) delete result;
+ return true;
+}
+
+bool Corpse::LoadFromDB(uint32 guid, Field *fields)
+{
+ // 0 1 2 3 4 5 6 7 8
+ //result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid);
+ float positionX = fields[0].GetFloat();
+ float positionY = fields[1].GetFloat();
+ float positionZ = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ uint32 mapid = fields[4].GetUInt32();
+
+ if(!LoadValues( fields[5].GetString() ))
+ {
+ sLog.outError("ERROR: Corpse #%d have broken data in `data` field. Can't be loaded.",guid);
+ return false;
+ }
+
+ m_time = time_t(fields[6].GetUInt64());
+ m_type = CorpseType(fields[7].GetUInt32());
+ if(m_type >= MAX_CORPSE_TYPE)
+ {
+ sLog.outError("ERROR: Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID()));
+ return false;
+ }
+ uint32 instanceid = fields[8].GetUInt32();
+
+ // overwrite possible wrong/corrupted guid
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE));
+
+ // place
+ SetInstanceId(instanceid);
+ SetMapId(mapid);
+ Relocate(positionX,positionY,positionZ,ort);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",GetGUIDLow(),GUID_LOPART(GetOwnerGUID()),GetPositionX(),GetPositionY());
+ return false;
+ }
+
+ m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY());
+
+ return true;
+}
+
+bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const
+{
+ return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f));
+}
diff --git a/src/game/Corpse.h b/src/game/Corpse.h
new file mode 100644
index 00000000000..f99523779a7
--- /dev/null
+++ b/src/game/Corpse.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_CORPSE_H
+#define MANGOSSERVER_CORPSE_H
+
+#include "Object.h"
+#include "Database/DatabaseEnv.h"
+#include "GridDefines.h"
+#include "LootMgr.h"
+
+enum CorpseType
+{
+ CORPSE_BONES = 0,
+ CORPSE_RESURRECTABLE_PVE = 1,
+ CORPSE_RESURRECTABLE_PVP = 2
+};
+#define MAX_CORPSE_TYPE 3
+
+// Value equal client resurrection dialog show radius.
+#define CORPSE_RECLAIM_RADIUS 39
+
+enum CorpseFlags
+{
+ CORPSE_FLAG_NONE = 0x00,
+ CORPSE_FLAG_BONES = 0x01,
+ CORPSE_FLAG_UNK1 = 0x02,
+ CORPSE_FLAG_UNK2 = 0x04,
+ CORPSE_FLAG_HIDE_HELM = 0x08,
+ CORPSE_FLAG_HIDE_CLOAK = 0x10,
+ CORPSE_FLAG_LOOTABLE = 0x20
+};
+
+class Corpse : public WorldObject
+{
+ public:
+ explicit Corpse( CorpseType type = CORPSE_BONES );
+ ~Corpse( );
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create( uint32 guidlow );
+ bool Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang );
+
+ void SaveToDB();
+ bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId);
+ bool LoadFromDB(uint32 guid, Field *fields);
+
+ void DeleteBonesFromWorld();
+ void DeleteFromDB();
+
+ uint64 const& GetOwnerGUID() const { return GetUInt64Value(CORPSE_FIELD_OWNER); }
+
+ time_t const& GetGhostTime() const { return m_time; }
+ void ResetGhostTime() { m_time = time(NULL); }
+ CorpseType GetType() const { return m_type; }
+
+ GridPair const& GetGrid() const { return m_grid; }
+ void SetGrid(GridPair const& grid) { m_grid = grid; }
+
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
+ Loot loot; // remove insignia ONLY at BG
+ Player* lootRecipient;
+ bool lootForBody;
+
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
+ void Whisper(const char* text, uint64 receiver) { MonsterWhisper(text,receiver); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
+ void Whisper(int32 textId,uint64 receiver) { MonsterWhisper(textId,receiver); }
+
+ GridReference<Corpse> &GetGridRef() { return m_gridRef; }
+ private:
+ GridReference<Corpse> m_gridRef;
+
+ CorpseType m_type;
+ time_t m_time;
+ GridPair m_grid; // gride for corpse position for fast search
+};
+#endif
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp
new file mode 100644
index 00000000000..bab0c16b4e9
--- /dev/null
+++ b/src/game/Creature.cpp
@@ -0,0 +1,1993 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Creature.h"
+#include "QuestDef.h"
+#include "GossipDef.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "LootMgr.h"
+#include "MapManager.h"
+#include "CreatureAI.h"
+#include "CreatureAISelector.h"
+#include "Formulas.h"
+#include "SpellAuras.h"
+#include "WaypointMovementGenerator.h"
+#include "InstanceData.h"
+#include "BattleGround.h"
+#include "Util.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+// apply implementation of the singletons
+#include "Policies/SingletonImp.h"
+
+Creature::Creature() :
+Unit(), i_AI(NULL),
+lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
+m_itemsLoaded(false), m_trainerSpellsLoaded(false), m_trainer_type(0), m_lootMoney(0), m_lootRecipient(0),
+m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
+m_gossipOptionLoaded(false),m_NPCTextId(0), m_emoteState(0), m_isPet(false), m_isTotem(false),
+m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
+m_AlreadyCallAssistence(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
+m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL)
+{
+ m_valuesCount = UNIT_END;
+
+ for(int i =0; i<4; ++i)
+ m_spells[i] = 0;
+
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+ m_GlobalCooldown = 0;
+ m_unit_movement_flags = MOVEMENTFLAG_WALK_MODE;
+}
+
+Creature::~Creature()
+{
+ CleanupsBeforeDelete();
+
+ m_trainer_spells.clear();
+ m_vendor_items.clear();
+
+ delete i_AI;
+ i_AI = NULL;
+}
+
+void Creature::AddToWorld()
+{
+ ///- Register the creature for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Unit::AddToWorld();
+}
+
+void Creature::RemoveFromWorld()
+{
+ ///- Remove the creature from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Unit::RemoveFromWorld();
+}
+
+void Creature::LoadTrainerSpells()
+{
+ if(m_trainerSpellsLoaded)
+ return;
+
+ m_trainer_spells.clear();
+ m_trainer_type = 0;
+
+ Field *fields;
+ QueryResult *result = WorldDatabase.PQuery("SELECT spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer WHERE entry = '%u'", GetEntry());
+
+ if(!result)
+ return;
+
+ do
+ {
+ fields = result->Fetch();
+
+ uint32 spellid = fields[0].GetUInt32();
+ SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid);
+
+ if(!spellinfo)
+ {
+ sLog.outErrorDb("Trainer (Entry: %u ) has non existing spell %u",GetEntry(),spellid);
+ continue;
+ }
+
+ if(!SpellMgr::IsSpellValid(spellinfo))
+ {
+ sLog.outErrorDb("LoadTrainerSpells: Trainer (Entry: %u) has broken learning spell %u.", GetEntry(), spellid);
+ continue;
+ }
+
+ if(SpellMgr::IsProfessionSpell(spellinfo->Id))
+ m_trainer_type = 2;
+
+ TrainerSpell tspell;
+ tspell.spell = spellinfo;
+ tspell.spellcost = fields[1].GetUInt32();
+ tspell.reqskill = fields[2].GetUInt32();
+ tspell.reqskillvalue= fields[3].GetUInt32();
+ tspell.reqlevel = fields[4].GetUInt32();
+
+ m_trainer_spells.push_back(tspell);
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ m_trainerSpellsLoaded = true;
+}
+
+void Creature::RemoveCorpse()
+{
+ if( getDeathState()!=CORPSE && !m_isDeadByDefault || getDeathState()!=ALIVE && m_isDeadByDefault )
+ return;
+
+ m_deathTimer = 0;
+ setDeathState(DEAD);
+ ObjectAccessor::UpdateObjectVisibility(this);
+ loot.clear();
+ m_respawnTime = time(NULL) + m_respawnDelay;
+
+ float x,y,z,o;
+ GetRespawnCoord(x, y, z, &o);
+ MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation(this,x,y,z,o);
+}
+
+/**
+ * change the entry of creature until respawn
+ */
+bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
+{
+ CreatureInfo const *normalInfo = objmgr.GetCreatureTemplate(Entry);
+ if(!normalInfo)
+ {
+ sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry);
+ return false;
+ }
+
+ // get heroic mode entry
+ uint32 actualEntry = Entry;
+ CreatureInfo const *cinfo = normalInfo;
+ if(normalInfo->HeroicEntry)
+ {
+ Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
+ if(map && map->IsHeroic())
+ {
+ cinfo = objmgr.GetCreatureTemplate(normalInfo->HeroicEntry);
+ if(!cinfo)
+ {
+ sLog.outErrorDb("Creature::UpdateEntry creature heroic entry %u does not exist.", actualEntry);
+ return false;
+ }
+ }
+ }
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, Entry); // normal entry always
+ m_creatureInfo = cinfo; // map mode related always
+
+ if (cinfo->DisplayID_A == 0 || cinfo->DisplayID_H == 0) // Cancel load if no model defined
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has no model defined for Horde or Alliance in table `creature_template`, can't load. ",Entry);
+ return false;
+ }
+
+ uint32 display_id = objmgr.ChooseDisplayId(team, GetCreatureInfo(), data);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (!minfo)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has model %u not found in table `creature_model_info`, can't load. ", Entry, display_id);
+ return false;
+ }
+ else
+ display_id = minfo->modelid; // it can be different (for another gender)
+
+ SetDisplayId(display_id);
+ SetNativeDisplayId(display_id);
+ SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+
+ // Load creature equipment
+ if(!data || data->equipmentId == 0)
+ { // use default from the template
+ LoadEquipment(cinfo->equipmentId);
+ }
+ else if(data && data->equipmentId != -1)
+ { // override, -1 means no equipment
+ LoadEquipment(data->equipmentId);
+ }
+
+ SetName(normalInfo->Name); // at normal entry always
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
+ SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
+
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
+
+ SetSpeed(MOVE_WALK, cinfo->speed );
+ SetSpeed(MOVE_RUN, cinfo->speed );
+ SetSpeed(MOVE_SWIM, cinfo->speed );
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale);
+
+ // checked at loading
+ m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
+ if(!m_respawnradius && m_defaultMovementType==RANDOM_MOTION_TYPE)
+ m_defaultMovementType = IDLE_MOTION_TYPE;
+
+ return true;
+}
+
+bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
+{
+ if(!InitEntry(Entry,team,data))
+ return false;
+
+ m_regenHealth = GetCreatureInfo()->RegenHealth;
+
+ // creatures always have melee weapon ready if any
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS );
+
+ SelectLevel(GetCreatureInfo());
+ if (team == HORDE)
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_H);
+ else
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_A);
+
+ SetUInt32Value(UNIT_NPC_FLAGS,GetCreatureInfo()->npcflag);
+
+ SetAttackTime(BASE_ATTACK, GetCreatureInfo()->baseattacktime);
+ SetAttackTime(OFF_ATTACK, GetCreatureInfo()->baseattacktime);
+ SetAttackTime(RANGED_ATTACK,GetCreatureInfo()->rangeattacktime);
+
+ SetUInt32Value(UNIT_FIELD_FLAGS,GetCreatureInfo()->Flags);
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS,GetCreatureInfo()->dynamicflags);
+
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->armor));
+ SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->resistance1));
+ SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(GetCreatureInfo()->resistance2));
+ SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->resistance3));
+ SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(GetCreatureInfo()->resistance4));
+ SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->resistance5));
+ SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->resistance6));
+
+ SetCanModifyStats(true);
+ UpdateAllStats();
+
+ FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->faction_A);
+ if (factionTemplate) // check and error show at loading templates
+ {
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction);
+ if (factionEntry)
+ if( !(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN) &&
+ (factionEntry->team == ALLIANCE || factionEntry->team == HORDE) )
+ SetPvP(true);
+ }
+
+ m_spells[0] = GetCreatureInfo()->spell1;
+ m_spells[1] = GetCreatureInfo()->spell2;
+ m_spells[2] = GetCreatureInfo()->spell3;
+ m_spells[3] = GetCreatureInfo()->spell4;
+
+ return true;
+}
+
+void Creature::Update(uint32 diff)
+{
+ if(m_GlobalCooldown <= diff)
+ m_GlobalCooldown = 0;
+ else
+ m_GlobalCooldown -= diff;
+
+ switch( m_deathState )
+ {
+ case JUST_ALIVED:
+ // Dont must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting.
+ sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)",GetGUIDLow(),GetEntry());
+ break;
+ case JUST_DIED:
+ // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
+ sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry());
+ break;
+ case DEAD:
+ {
+ if( m_respawnTime <= time(NULL) )
+ {
+ DEBUG_LOG("Respawning...");
+ m_respawnTime = 0;
+ lootForPickPocketed = false;
+ lootForBody = false;
+
+ if(m_originalEntry != GetUInt32Value(OBJECT_FIELD_ENTRY))
+ UpdateEntry(m_originalEntry);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+
+ SelectLevel(cinfo);
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ if (m_isDeadByDefault)
+ {
+ setDeathState(JUST_DIED);
+ SetHealth(0);
+ i_motionMaster.Clear();
+ clearUnitState(UNIT_STAT_ALL_STATE);
+ LoadCreaturesAddon(true);
+ }
+ else
+ setDeathState( JUST_ALIVED );
+
+ //Call AI respawn virtual function
+ i_AI->JustRespawned();
+
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+ }
+ break;
+ }
+ case CORPSE:
+ {
+ if (m_isDeadByDefault)
+ break;
+
+ if( m_deathTimer <= diff )
+ {
+ RemoveCorpse();
+ DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
+ }
+ else
+ {
+ m_deathTimer -= diff;
+ if (m_groupLootTimer && lootingGroupLeaderGUID)
+ {
+ if(diff <= m_groupLootTimer)
+ {
+ m_groupLootTimer -= diff;
+ }
+ else
+ {
+ Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
+ if (group)
+ group->EndRoll();
+ m_groupLootTimer = 0;
+ lootingGroupLeaderGUID = 0;
+ }
+ }
+ }
+
+ break;
+ }
+ case ALIVE:
+ {
+ if (m_isDeadByDefault)
+ {
+ if( m_deathTimer <= diff )
+ {
+ RemoveCorpse();
+ DEBUG_LOG("Removing alive corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
+ }
+ else
+ {
+ m_deathTimer -= diff;
+ }
+ }
+
+ Unit::Update( diff );
+
+ // creature can be dead after Unit::Update call
+ // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
+ if(!isAlive())
+ break;
+
+ if(!IsInEvadeMode())
+ {
+ // do not allow the AI to be changed during update
+ m_AI_locked = true;
+ i_AI->UpdateAI(diff);
+ m_AI_locked = false;
+ }
+
+ // creature can be dead after UpdateAI call
+ // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
+ if(!isAlive())
+ break;
+ if(m_regenTimer > 0)
+ {
+ if(diff >= m_regenTimer)
+ m_regenTimer = 0;
+ else
+ m_regenTimer -= diff;
+ }
+ if (m_regenTimer != 0)
+ break;
+
+ if (!isInCombat() || IsPolymorphed())
+ RegenerateHealth();
+
+ RegenerateMana();
+
+ m_regenTimer = 2000;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Creature::RegenerateMana()
+{
+ uint32 curValue = GetPower(POWER_MANA);
+ uint32 maxValue = GetMaxPower(POWER_MANA);
+
+ if (curValue >= maxValue)
+ return;
+
+ uint32 addvalue = 0;
+
+ // Combat and any controlled creature
+ if (isInCombat() || GetCharmerOrOwnerGUID())
+ {
+ if(!IsUnderLastManaUseEffect())
+ {
+ float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
+ float Spirit = GetStat(STAT_SPIRIT);
+
+ addvalue = uint32((Spirit/5.0f + 17.0f) * ManaIncreaseRate);
+ }
+ }
+ else
+ addvalue = maxValue/3;
+
+ ModifyPower(POWER_MANA, addvalue);
+}
+
+void Creature::RegenerateHealth()
+{
+ if (!isRegeneratingHealth())
+ return;
+
+ uint32 curValue = GetHealth();
+ uint32 maxValue = GetMaxHealth();
+
+ if (curValue >= maxValue)
+ return;
+
+ uint32 addvalue = 0;
+
+ // Not only pet, but any controelled creature
+ if(GetCharmerOrOwnerGUID())
+ {
+ float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
+ float Spirit = GetStat(STAT_SPIRIT);
+
+ if( GetPower(POWER_MANA) > 0 )
+ addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
+ else
+ addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
+ }
+ else
+ addvalue = maxValue/3;
+
+ ModifyHealth(addvalue);
+}
+
+bool Creature::AIM_Initialize()
+{
+ // make sure nothing can change the AI during AI update
+ if(m_AI_locked)
+ {
+ sLog.outDebug("AIM_Initialize: failed to init, locked.");
+ return false;
+ }
+
+ CreatureAI * oldAI = i_AI;
+ i_motionMaster.Initialize();
+ i_AI = FactorySelector::selectAI(this);
+ if (oldAI)
+ delete oldAI;
+ return true;
+}
+
+bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data)
+{
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
+ const bool bResult = CreateFromProto(guidlow, Entry, team, data);
+
+ if (bResult)
+ {
+ switch (GetCreatureInfo()->rank)
+ {
+ case CREATURE_ELITE_RARE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RARE);
+ break;
+ case CREATURE_ELITE_ELITE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_ELITE);
+ break;
+ case CREATURE_ELITE_RAREELITE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RAREELITE);
+ break;
+ case CREATURE_ELITE_WORLDBOSS:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_WORLDBOSS);
+ break;
+ default:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
+ break;
+ }
+ }
+
+ return bResult;
+}
+
+bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
+{
+ if(!isTrainer())
+ return false;
+
+ if(m_trainer_spells.empty())
+ {
+ sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
+ GetGUIDLow(),GetEntry());
+ return false;
+ }
+
+ switch(GetCreatureInfo()->trainer_type)
+ {
+ case TRAINER_TYPE_CLASS:
+ if(pPlayer->getClass()!=GetCreatureInfo()->classNum)
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(GetCreatureInfo()->classNum)
+ {
+ case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
+ case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
+ case CLASS_MAGE: pPlayer->PlayerTalkClass->SendGossipMenu( 328,GetGUID()); break;
+ case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu( 1635,GetGUID()); break;
+ case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu( 4436,GetGUID()); break;
+ case CLASS_ROGUE: pPlayer->PlayerTalkClass->SendGossipMenu( 4797,GetGUID()); break;
+ case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu( 5003,GetGUID()); break;
+ case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu( 5836,GetGUID()); break;
+ case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu( 4985,GetGUID()); break;
+ }
+ }
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_PETS:
+ if(pPlayer->getClass()!=CLASS_HUNTER)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID());
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_MOUNTS:
+ if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race)
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(GetCreatureInfo()->classNum)
+ {
+ case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
+ case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
+ case RACE_HUMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break;
+ case RACE_NIGHTELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
+ case RACE_ORC: pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break;
+ case RACE_TAUREN: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
+ case RACE_TROLL: pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break;
+ case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu( 624,GetGUID()); break;
+ case RACE_BLOODELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
+ case RACE_DRAENEI: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
+ }
+ }
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_TRADESKILLS:
+ if(GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell))
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID());
+ }
+ return false;
+ }
+ break;
+ default:
+ return false; // checked and error output at creature_template loading
+ }
+ return true;
+}
+
+bool Creature::isCanIneractWithBattleMaster(Player* pPlayer, bool msg) const
+{
+ if(!isBattleMaster())
+ return false;
+
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
+ if(!msg)
+ return pPlayer->GetBGAccessByLevel(bgTypeId);
+
+ if(!pPlayer->GetBGAccessByLevel(bgTypeId))
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(bgTypeId)
+ {
+ case BATTLEGROUND_AV: pPlayer->PlayerTalkClass->SendGossipMenu(7616,GetGUID()); break;
+ case BATTLEGROUND_WS: pPlayer->PlayerTalkClass->SendGossipMenu(7599,GetGUID()); break;
+ case BATTLEGROUND_AB: pPlayer->PlayerTalkClass->SendGossipMenu(7642,GetGUID()); break;
+ case BATTLEGROUND_EY:
+ case BATTLEGROUND_NA:
+ case BATTLEGROUND_BE:
+ case BATTLEGROUND_AA:
+ case BATTLEGROUND_RL: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break;
+ break;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
+{
+ return pPlayer->getLevel() >= 10
+ && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
+ && pPlayer->getClass() == GetCreatureInfo()->classNum;
+}
+
+void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
+{
+ PlayerMenu* pm=pPlayer->PlayerTalkClass;
+ pm->ClearMenus();
+
+ // lazy loading single time at use
+ LoadGossipOptions();
+
+ GossipOption* gso;
+ GossipOption* ingso;
+
+ for( GossipOptionList::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
+ {
+ gso=&*i;
+ if(gso->GossipId == gossipid)
+ {
+ bool cantalking=true;
+ if(gso->Id==1)
+ {
+ uint32 textid=GetNpcTextId();
+ GossipText * gossiptext=objmgr.GetGossipText(textid);
+ if(!gossiptext)
+ cantalking=false;
+ }
+ else
+ {
+ switch (gso->Action)
+ {
+ case GOSSIP_OPTION_QUESTGIVER:
+ pPlayer->PrepareQuestMenu(GetGUID());
+ //if (pm->GetQuestMenu()->MenuItemCount() == 0)
+ cantalking=false;
+ //pm->GetQuestMenu()->ClearMenu();
+ break;
+ case GOSSIP_OPTION_ARMORER:
+ cantalking=false; // added in special mode
+ break;
+ case GOSSIP_OPTION_SPIRITHEALER:
+ if( !pPlayer->isDead() )
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_VENDOR:
+ // load vendor items if not yet
+ LoadGoods();
+
+ if(!GetItemCount())
+ {
+ sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
+ GetGUIDLow(),GetEntry());
+ cantalking=false;
+ }
+ break;
+ case GOSSIP_OPTION_TRAINER:
+ // Lazy loading at first access
+ LoadTrainerSpells();
+
+ if(!isCanTrainingOf(pPlayer,false))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_UNLEARNTALENTS:
+ if(!isCanTrainingAndResetTalentsOf(pPlayer))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_UNLEARNPETSKILLS:
+ if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->classNum != CLASS_HUNTER)
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_TAXIVENDOR:
+ if ( pPlayer->GetSession()->SendLearnNewTaxiNode(this) )
+ return;
+ break;
+ case GOSSIP_OPTION_BATTLEFIELD:
+ if(!isCanIneractWithBattleMaster(pPlayer,false))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_SPIRITGUIDE:
+ case GOSSIP_OPTION_INNKEEPER:
+ case GOSSIP_OPTION_BANKER:
+ case GOSSIP_OPTION_PETITIONER:
+ case GOSSIP_OPTION_STABLEPET:
+ case GOSSIP_OPTION_TABARDDESIGNER:
+ case GOSSIP_OPTION_AUCTIONEER:
+ break; // no checks
+ default:
+ sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u",GetGUIDLow(),GetEntry(),gso->Action);
+ break;
+ }
+ }
+
+ if(!gso->Option.empty() && cantalking )
+ { //note for future dev: should have database fields for BoxMessage & BoxMoney
+ pm->GetGossipMenu()->AddMenuItem((uint8)gso->Icon,gso->Option, gossipid,gso->Action,"",0,false);
+ ingso=gso;
+ }
+ }
+ }
+
+ ///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
+ if(pm->GetGossipMenu()->MenuItemCount()==0 && !pm->GetQuestMenu()->MenuItemCount())
+ {
+ if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
+ {
+ LoadTrainerSpells(); // Lazy loading at first access
+ isCanTrainingOf(pPlayer,true); // output error message if need
+ }
+ if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
+ {
+ isCanIneractWithBattleMaster(pPlayer,true); // output error message if need
+ }
+ }
+}
+
+void Creature::sendPreparedGossip(Player* player)
+{
+ if(!player)
+ return;
+
+ GossipMenu* gossipmenu = player->PlayerTalkClass->GetGossipMenu();
+
+ // in case empty gossip menu open quest menu if any
+ if (gossipmenu->MenuItemCount() == 0 && GetNpcTextId() == 0)
+ {
+ player->SendPreparedQuest(GetGUID());
+ return;
+ }
+
+ // in case non empty gossip menu (that not included quests list size) show it
+ // (quest entries from quest menu wiill be included in list)
+ player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
+}
+
+void Creature::OnGossipSelect(Player* player, uint32 option)
+{
+ GossipMenu* gossipmenu = player->PlayerTalkClass->GetGossipMenu();
+ uint32 action=gossipmenu->GetItem(option).m_gAction;
+ uint32 zoneid=GetZoneId();
+ uint64 guid=GetGUID();
+ GossipOption const *gossip=GetGossipOption( action );
+ uint32 textid;
+ if(!gossip)
+ {
+ zoneid=0;
+ gossip=GetGossipOption( action );
+ if(!gossip)
+ return;
+ }
+ textid=GetGossipTextId( action, zoneid);
+ if(textid==0)
+ textid=GetNpcTextId();
+
+ switch (gossip->Action)
+ {
+ case GOSSIP_OPTION_GOSSIP:
+ player->PlayerTalkClass->CloseGossip();
+ player->PlayerTalkClass->SendTalking( textid );
+ break;
+ case GOSSIP_OPTION_SPIRITHEALER:
+ if( player->isDead() )
+ CastSpell(this,17251,true,NULL,NULL,player->GetGUID());
+ break;
+ case GOSSIP_OPTION_QUESTGIVER:
+ player->PrepareQuestMenu( guid );
+ player->SendPreparedQuest( guid );
+ break;
+ case GOSSIP_OPTION_VENDOR:
+ case GOSSIP_OPTION_ARMORER:
+ player->GetSession()->SendListInventory(guid);
+ break;
+ case GOSSIP_OPTION_STABLEPET:
+ player->GetSession()->SendStablePet(guid);
+ break;
+ case GOSSIP_OPTION_TRAINER:
+ player->GetSession()->SendTrainerList(guid);
+ break;
+ case GOSSIP_OPTION_UNLEARNTALENTS:
+ player->PlayerTalkClass->CloseGossip();
+ player->SendTalentWipeConfirm(guid);
+ break;
+ case GOSSIP_OPTION_UNLEARNPETSKILLS:
+ player->PlayerTalkClass->CloseGossip();
+ player->SendPetSkillWipeConfirm();
+ break;
+ case GOSSIP_OPTION_TAXIVENDOR:
+ player->GetSession()->SendTaxiMenu(this);
+ break;
+ case GOSSIP_OPTION_INNKEEPER:
+ player->PlayerTalkClass->CloseGossip();
+ player->SetBindPoint( guid );
+ break;
+ case GOSSIP_OPTION_BANKER:
+ player->GetSession()->SendShowBank( guid );
+ break;
+ case GOSSIP_OPTION_PETITIONER:
+ player->PlayerTalkClass->CloseGossip();
+ player->GetSession()->SendPetitionShowList( guid );
+ break;
+ case GOSSIP_OPTION_TABARDDESIGNER:
+ player->PlayerTalkClass->CloseGossip();
+ player->GetSession()->SendTabardVendorActivate( guid );
+ break;
+ case GOSSIP_OPTION_AUCTIONEER:
+ player->GetSession()->SendAuctionHello( guid, this );
+ break;
+ case GOSSIP_OPTION_SPIRITGUIDE:
+ case GOSSIP_GUARD_SPELLTRAINER:
+ case GOSSIP_GUARD_SKILLTRAINER:
+ prepareGossipMenu( player,gossip->Id );
+ sendPreparedGossip( player );
+ break;
+ case GOSSIP_OPTION_BATTLEFIELD:
+ {
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
+ player->GetSession()->SendBattlegGroundList( GetGUID(), bgTypeId );
+ break;
+ }
+ default:
+ OnPoiSelect( player, gossip );
+ break;
+ }
+
+}
+
+void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
+{
+ if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
+ {
+ //float x,y;
+ //bool findnpc=false;
+ Poi_Icon icon = ICON_POI_0;
+ //QueryResult *result;
+ //Field *fields;
+ uint32 mapid=GetMapId();
+ Map const* map=MapManager::Instance().GetBaseMap( mapid );
+ uint16 areaflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
+ uint32 zoneid=Map::GetZoneId(areaflag,mapid);
+ std::string areaname= gossip->Option;
+ /*
+ uint16 pflag;
+
+ // use the action relate to creaturetemplate.trainer_type ?
+ result= WorldDatabase.PQuery("SELECT creature.position_x,creature.position_y FROM creature,creature_template WHERE creature.map = '%u' AND creature.id = creature_template.entry AND creature_template.trainer_type = '%u'", mapid, gossip->Action );
+ if(!result)
+ return;
+ do
+ {
+ fields = result->Fetch();
+ x=fields[0].GetFloat();
+ y=fields[1].GetFloat();
+ pflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
+ if(pflag==areaflag)
+ {
+ findnpc=true;
+ break;
+ }
+ }while(result->NextRow());
+
+ delete result;
+
+ if(!findnpc)
+ {
+ player->PlayerTalkClass->SendTalking( "$NSorry", "Here no this person.");
+ return;
+ }*/
+
+ //need add more case.
+ switch(gossip->Action)
+ {
+ case GOSSIP_GUARD_BANK:
+ icon=ICON_POI_HOUSE;
+ break;
+ case GOSSIP_GUARD_RIDE:
+ icon=ICON_POI_RWHORSE;
+ break;
+ case GOSSIP_GUARD_GUILD:
+ icon=ICON_POI_BLUETOWER;
+ break;
+ default:
+ icon=ICON_POI_TOWER;
+ break;
+ }
+ uint32 textid=GetGossipTextId( gossip->Action, zoneid );
+ player->PlayerTalkClass->SendTalking( textid );
+ // how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
+ }
+}
+
+uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid)
+{
+ QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
+
+ if(!result)
+ return 0;
+
+ Field *fields = result->Fetch();
+ uint32 id = fields[0].GetUInt32();
+
+ delete result;
+
+ return id;
+}
+
+uint32 Creature::GetNpcTextId()
+{
+ // already loaded and cached
+ if(m_NPCTextId)
+ return m_NPCTextId;
+
+ QueryResult* result = WorldDatabase.PQuery("SELECT textid FROM npc_gossip WHERE npc_guid= '%u'", m_DBTableGuid);
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ m_NPCTextId = fields[0].GetUInt32();
+ delete result;
+ }
+ else
+ m_NPCTextId = DEFAULT_GOSSIP_MESSAGE;
+
+ return m_NPCTextId;
+}
+
+GossipOption const* Creature::GetGossipOption( uint32 id ) const
+{
+ for( GossipOptionList::const_iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
+ {
+ if(i->Action==id )
+ return &*i;
+ }
+ return NULL;
+}
+
+void Creature::LoadGossipOptions()
+{
+ if(m_gossipOptionLoaded)
+ return;
+
+ uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id,gossip_id,npcflag,icon,action,option_text FROM npc_option WHERE (npcflag & %u)<>0", npcflags );
+
+ if(!result)
+ return;
+
+ GossipOption go;
+ do
+ {
+ Field *fields = result->Fetch();
+ go.Id= fields[0].GetUInt32();
+ go.GossipId = fields[1].GetUInt32();
+ go.NpcFlag=fields[2].GetUInt32();
+ go.Icon=fields[3].GetUInt32();
+ go.Action=fields[4].GetUInt32();
+ go.Option=fields[5].GetCppString();
+ addGossipOption(go);
+ }while( result->NextRow() );
+ delete result;
+
+ m_gossipOptionLoaded = true;
+}
+
+void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type)
+{
+ /* uint32 timeElap = getMSTime();
+ if ((timeElap - m_startMove) < m_moveTime)
+ {
+ oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
+ oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
+ }
+ else
+ {
+ oX = dX;
+ oY = dY;
+ }
+
+ dX = x;
+ dY = y;
+ m_orientation = atan2((oY - dY), (oX - dX));
+
+ m_startMove = getMSTime();
+ m_moveTime = time;*/
+ SendMonsterMove(x, y, z, type, MovementFlags, time);
+}
+
+Player *Creature::GetLootRecipient() const
+{
+ if (!m_lootRecipient) return NULL;
+ else return ObjectAccessor::FindPlayer(m_lootRecipient);
+}
+
+void Creature::SetLootRecipient(Unit *unit)
+{
+ // set the player whose group should receive the right
+ // to loot the creature after it dies
+ // should be set to NULL after the loot disappears
+
+ if (!unit)
+ {
+ m_lootRecipient = 0;
+ RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
+ return;
+ }
+
+ Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
+ if(!player) // normal creature, no player involved
+ return;
+
+ m_lootRecipient = player->GetGUID();
+ SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
+}
+
+void Creature::SaveToDB()
+{
+ // this should only be used when the creature has already been loaded
+ // perferably after adding to map, because mapid may not be valid otherwise
+ CreatureData const *data = objmgr.GetCreatureData(m_DBTableGuid);
+ if(!data)
+ {
+ sLog.outError("Creature::SaveToDB failed, cannot get creature data!");
+ return;
+ }
+
+ SaveToDB(GetMapId(), data->spawnMask);
+}
+
+void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
+{
+ // update in loaded data
+ CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
+
+ uint32 displayId = GetNativeDisplayId();
+
+ // check if it's a custom model and if not, use 0 for displayId
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(cinfo)
+ {
+ if(displayId != cinfo->DisplayID_A && displayId != cinfo->DisplayID_H)
+ {
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_A);
+ if(!minfo || displayId != minfo->modelid_other_gender)
+ {
+ minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_H);
+ if(minfo && displayId == minfo->modelid_other_gender)
+ displayId = 0;
+ }
+ else
+ displayId = 0;
+ }
+ else
+ displayId = 0;
+ }
+
+ // data->guid = guid don't must be update at save
+ data.id = GetEntry();
+ data.mapid = mapid;
+ data.displayid = displayId;
+ data.equipmentId = GetEquipmentId();
+ data.posX = GetPositionX();
+ data.posY = GetPositionY();
+ data.posZ = GetPositionZ();
+ data.orientation = GetOrientation();
+ data.spawntimesecs = m_respawnDelay;
+ // prevent add data integrity problems
+ data.spawndist = GetDefaultMovementType()==IDLE_MOTION_TYPE ? 0 : m_respawnradius;
+ data.currentwaypoint = 0;
+ data.curhealth = GetHealth();
+ data.curmana = GetPower(POWER_MANA);
+ data.is_dead = m_isDeadByDefault;
+ // prevent add data integrity problems
+ data.movementType = !m_respawnradius && GetDefaultMovementType()==RANDOM_MOTION_TYPE
+ ? IDLE_MOTION_TYPE : GetDefaultMovementType();
+ data.spawnMask = spawnMask;
+
+ // updated in DB
+ WorldDatabase.BeginTransaction();
+
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
+
+ std::ostringstream ss;
+ ss << "INSERT INTO creature VALUES ("
+ << m_DBTableGuid << ","
+ << GetEntry() << ","
+ << mapid <<","
+ << (uint32)spawnMask << ","
+ << displayId <<","
+ << GetEquipmentId() <<","
+ << GetPositionX() << ","
+ << GetPositionY() << ","
+ << GetPositionZ() << ","
+ << GetOrientation() << ","
+ << m_respawnDelay << "," //respawn time
+ << (float) m_respawnradius << "," //spawn distance (float)
+ << (uint32) (0) << "," //currentwaypoint
+ << GetHealth() << "," //curhealth
+ << GetPower(POWER_MANA) << "," //curmana
+ << (m_isDeadByDefault ? 1 : 0) << "," //is_dead
+ << GetDefaultMovementType() << ")"; //default movement generator type
+
+ WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
+
+ WorldDatabase.CommitTransaction();
+}
+
+void Creature::SelectLevel(const CreatureInfo *cinfo)
+{
+ uint32 rank = isPet()? 0 : cinfo->rank;
+
+ // level
+ uint32 minlevel = std::min(cinfo->maxlevel, cinfo->minlevel);
+ uint32 maxlevel = std::max(cinfo->maxlevel, cinfo->minlevel);
+ uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
+ SetLevel(level);
+
+ float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
+
+ // health
+ float healthmod = _GetHealthMod(rank);
+
+ uint32 minhealth = std::min(cinfo->maxhealth, cinfo->minhealth);
+ uint32 maxhealth = std::max(cinfo->maxhealth, cinfo->minhealth);
+ uint32 health = uint32(healthmod * (minhealth + uint32(rellevel*(maxhealth - minhealth))));
+
+ SetCreateHealth(health);
+ SetMaxHealth(health);
+ SetHealth(health);
+
+ // mana
+ uint32 minmana = std::min(cinfo->maxmana, cinfo->minmana);
+ uint32 maxmana = std::max(cinfo->maxmana, cinfo->minmana);
+ uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
+
+ SetCreateMana(mana);
+ SetMaxPower(POWER_MANA, mana); //MAX Mana
+ SetPower(POWER_MANA, mana);
+
+ SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);
+ SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);
+
+ // damage
+ float damagemod = _GetDamageMod(rank);
+
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod);
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod);
+
+ SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod);
+ SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod);
+
+ SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->attackpower * damagemod);
+}
+
+float Creature::_GetHealthMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_HP);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
+ }
+}
+
+float Creature::_GetDamageMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
+ }
+}
+
+float Creature::GetSpellDamageMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
+ }
+}
+
+bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const CreatureData *data)
+{
+ CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
+ if(!cinfo)
+ {
+ sLog.outErrorDb("Error: creature entry %u does not exist.", Entry);
+ return false;
+ }
+ m_originalEntry = Entry;
+
+ Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
+
+ m_DBTableGuid = guidlow;
+ if(!UpdateEntry(Entry, team, data))
+ return false;
+
+ //Notify the map's instance data.
+ //Only works if you create the object in it, not if it is moves to that map.
+ //Normally non-players do not teleport to other maps.
+ Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
+ if(map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
+ {
+ ((InstanceMap*)map)->GetInstanceData()->OnCreatureCreate(this, Entry);
+ }
+
+ return true;
+}
+
+bool Creature::LoadFromDB(uint32 guid, Map *map)
+{
+ CreatureData const* data = objmgr.GetCreatureData(guid);
+
+ if(!data)
+ {
+ sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
+ return false;
+ }
+
+ uint32 stored_guid = guid;
+ if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
+
+ uint16 team = 0;
+ if(!Create(guid,map,data->id,team,data))
+ return false;
+
+ Relocate(data->posX,data->posY,data->posZ,data->orientation);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY());
+ return false;
+ }
+
+ m_DBTableGuid = stored_guid;
+ LoadCreaturesAddon();
+
+ m_respawnradius = data->spawndist;
+
+ m_respawnDelay = data->spawntimesecs;
+ m_isDeadByDefault = data->is_dead;
+ m_deathState = m_isDeadByDefault ? DEAD : ALIVE;
+
+ m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId());
+ if(m_respawnTime > time(NULL)) // not ready to respawn
+ m_deathState = DEAD;
+ else if(m_respawnTime) // respawn time set but expired
+ {
+ m_respawnTime = 0;
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+
+ uint32 curhealth = data->curhealth;
+ if(curhealth)
+ {
+ curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank));
+ if(curhealth < 1)
+ curhealth = 1;
+ }
+
+ SetHealth(m_deathState == ALIVE ? curhealth : 0);
+ SetPower(POWER_MANA,data->curmana);
+
+ SetMeleeDamageSchool(SpellSchools(GetCreatureInfo()->dmgschool));
+
+ // checked at creature_template loading
+ m_defaultMovementType = MovementGeneratorType(data->movementType);
+
+ AIM_Initialize();
+ return true;
+}
+
+void Creature::LoadEquipment(uint32 equip_entry, bool force)
+{
+ if(equip_entry == 0)
+ {
+ if (force)
+ {
+ for (uint8 i=0;i<3;i++)
+ {
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, 0);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), 0);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, 0);
+ }
+ m_equipmentId = 0;
+ }
+ return;
+ }
+
+ EquipmentInfo const *einfo = objmgr.GetEquipmentInfo(equip_entry);
+ if (!einfo)
+ return;
+
+ m_equipmentId = equip_entry;
+ for (uint8 i=0;i<3;i++)
+ {
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, einfo->equipmodel[i]);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), einfo->equipinfo[i]);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, einfo->equipslot[i]);
+ }
+}
+
+void Creature::LoadGoods()
+{
+ // already loaded;
+ if(m_itemsLoaded)
+ return;
+
+ m_vendor_items.clear();
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT item, maxcount, incrtime, ExtendedCost FROM npc_vendor WHERE entry = '%u'", GetEntry());
+
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ if (GetItemCount() >= MAX_VENDOR_ITEMS)
+ {
+ sLog.outErrorDb( "Vendor %u has too many items (%u >= %i). Check the DB!", GetEntry(), GetItemCount(), MAX_VENDOR_ITEMS );
+ break;
+ }
+
+ uint32 item_id = fields[0].GetUInt32();
+ if(!sItemStorage.LookupEntry<ItemPrototype>(item_id))
+ {
+ sLog.outErrorDb("Vendor %u have in item list non-existed item %u",GetEntry(),item_id);
+ continue;
+ }
+
+ uint32 ExtendedCost = fields[3].GetUInt32();
+ if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
+ sLog.outErrorDb("Item (Entry: %u) has wrong ExtendedCost (%u) for vendor (%u)",item_id,ExtendedCost,GetEntry());
+
+ AddItem( item_id, fields[1].GetUInt32(), fields[2].GetUInt32(), ExtendedCost);
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ m_itemsLoaded = true;
+}
+
+bool Creature::hasQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mCreatureQuestRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool Creature::hasInvolvedQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+void Creature::DeleteFromDB()
+{
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ objmgr.DeleteCreatureData(m_DBTableGuid);
+
+ WorldDatabase.BeginTransaction();
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.CommitTransaction();
+}
+
+float Creature::GetAttackDistance(Unit const* pl) const
+{
+ float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO);
+ if(aggroRate==0)
+ return 0.0f;
+
+ int32 playerlevel = pl->getLevelForTarget(this);
+ int32 creaturelevel = getLevelForTarget(pl);
+
+ int32 leveldif = playerlevel - creaturelevel;
+
+ // "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
+ if ( leveldif < - 25)
+ leveldif = -25;
+
+ // "The aggro radius of a mob having the same level as the player is roughly 20 yards"
+ float RetDistance = 20;
+
+ // "Aggro Radius varries with level difference at a rate of roughly 1 yard/level"
+ // radius grow if playlevel < creaturelevel
+ RetDistance -= (float)leveldif;
+
+ if(creaturelevel+5 <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ // detect range auras
+ RetDistance += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);
+
+ // detected range auras
+ RetDistance += pl->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);
+ }
+
+ // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
+ if(RetDistance < 5)
+ RetDistance = 5;
+
+ return (RetDistance*aggroRate);
+}
+
+void Creature::setDeathState(DeathState s)
+{
+ if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault))
+ {
+ m_deathTimer = m_corpseDelay*1000;
+
+ // always save boss respawn time at death to prevent crash cheating
+ if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss())
+ SaveRespawnTime();
+
+ if(!IsStopped())
+ StopMoving();
+ }
+ Unit::setDeathState(s);
+
+ if(s == JUST_DIED)
+ {
+ SetUInt64Value (UNIT_FIELD_TARGET,0); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
+ SetUInt32Value(UNIT_NPC_FLAGS, 0);
+
+ if(!isPet() && GetCreatureInfo()->SkinLootId)
+ if ( LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId) )
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ Unit::setDeathState(CORPSE);
+ }
+ if(s == JUST_ALIVED)
+ {
+ SetHealth(GetMaxHealth());
+ SetLootRecipient(NULL);
+ Unit::setDeathState(ALIVE);
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+ AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
+ clearUnitState(UNIT_STAT_ALL_STATE);
+ i_motionMaster.Clear();
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+ LoadCreaturesAddon(true);
+ }
+}
+
+void Creature::Respawn()
+{
+ RemoveCorpse();
+
+ // forced recreate creature object at clients
+ UnitVisibility currentVis = GetVisibility();
+ SetVisibility(VISIBILITY_RESPAWN);
+ ObjectAccessor::UpdateObjectVisibility(this);
+ SetVisibility(currentVis); // restore visibility state
+ ObjectAccessor::UpdateObjectVisibility(this);
+
+ if(getDeathState()==DEAD)
+ {
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ m_respawnTime = time(NULL); // respawn at next tick
+ }
+}
+
+bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
+{
+ if (!spellInfo)
+ return false;
+
+ if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
+ return true;
+
+ return Unit::IsImmunedToSpell(spellInfo, useCharges);
+}
+
+bool Creature::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
+{
+ if (GetCreatureInfo()->MechanicImmuneMask & (1 << (mechanic-1)))
+ return true;
+
+ return Unit::IsImmunedToSpellEffect(effect, mechanic);
+}
+
+SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
+{
+ if(!pVictim)
+ return NULL;
+
+ for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
+ {
+ if(!m_spells[i])
+ continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
+ continue;
+ }
+
+ bool bcontinue = true;
+ for(uint32 j=0;j<3;j++)
+ {
+ if( (spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
+ )
+ {
+ bcontinue = false;
+ break;
+ }
+ }
+ if(bcontinue) continue;
+
+ if(spellInfo->manaCost > GetPower(POWER_MANA))
+ continue;
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+ float minrange = GetSpellMinRange(srange);
+ float dist = GetDistance(pVictim);
+ //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
+ // continue;
+ if( dist > range || dist < minrange )
+ continue;
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ continue;
+ return spellInfo;
+ }
+ return NULL;
+}
+
+SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
+{
+ if(!pVictim)
+ return NULL;
+
+ for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
+ {
+ if(!m_spells[i])
+ continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
+ continue;
+ }
+
+ bool bcontinue = true;
+ for(uint32 j=0;j<3;j++)
+ {
+ if( (spellInfo->Effect[j] == SPELL_EFFECT_HEAL ) )
+ {
+ bcontinue = false;
+ break;
+ }
+ }
+ if(bcontinue) continue;
+
+ if(spellInfo->manaCost > GetPower(POWER_MANA))
+ continue;
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+ float minrange = GetSpellMinRange(srange);
+ float dist = GetDistance(pVictim);
+ //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
+ // continue;
+ if( dist > range || dist < minrange )
+ continue;
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ continue;
+ return spellInfo;
+ }
+ return NULL;
+}
+
+bool Creature::IsVisibleInGridForPlayer(Player* pl) const
+{
+ // gamemaster in GM mode see all, including ghosts
+ if(pl->isGameMaster())
+ return true;
+
+ // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
+ if(pl->isAlive() || pl->GetDeathTimer() > 0)
+ {
+ if( GetEntry() == VISUAL_WAYPOINT && !pl->isGameMaster() )
+ return false;
+ return isAlive() || m_deathTimer > 0 || m_isDeadByDefault && m_deathState==CORPSE;
+ }
+
+ // Dead player see live creatures near own corpse
+ if(isAlive())
+ {
+ Corpse *corpse = pl->GetCorpse();
+ if(corpse)
+ {
+ // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
+ if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
+ return true;
+ }
+ }
+
+ // Dead player see Spirit Healer or Spirit Guide
+ if(isSpiritService())
+ return true;
+
+ // and not see any other
+ return false;
+}
+
+void Creature::CallAssistence()
+{
+ if( !m_AlreadyCallAssistence && getVictim() && !isPet() && !isCharmed())
+ {
+ SetNoCallAssistence(true);
+
+ float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS);
+ if(radius > 0)
+ {
+ std::list<Creature*> assistList;
+
+ {
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius);
+ MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(assistList, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::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));
+ }
+
+ for(std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
+ {
+ (*iter)->SetNoCallAssistence(true);
+ if((*iter)->AI())
+ (*iter)->AI()->AttackStart(getVictim());
+ }
+ }
+ }
+}
+
+void Creature::SaveRespawnTime()
+{
+ if(isPet())
+ return;
+
+ if(m_respawnTime > time(NULL)) // dead (no corpse)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
+ else if(m_deathTimer > 0) // dead (corpse)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000);
+}
+
+bool Creature::IsOutOfThreatArea(Unit* pVictim) const
+{
+ if(!pVictim)
+ return true;
+
+ if(!pVictim->IsInMap(this))
+ return true;
+
+ if(!pVictim->isTargetableForAttack())
+ return true;
+
+ if(!pVictim->isInAccessablePlaceFor(this))
+ return true;
+
+ if(sMapStore.LookupEntry(GetMapId())->Instanceable())
+ return false;
+
+ float length = pVictim->GetDistance(CombatStartX,CombatStartY,CombatStartZ);
+ float AttackDist = GetAttackDistance(pVictim);
+ uint32 ThreatRadius = sWorld.getConfig(CONFIG_THREAT_RADIUS);
+
+ //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and ouf of combat every update tick.
+ return ( length > (ThreatRadius > AttackDist ? ThreatRadius : AttackDist));
+}
+
+CreatureDataAddon const* Creature::GetCreatureAddon() const
+{
+ if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
+ return addon;
+
+ // dependent from heroic mode entry
+ return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
+}
+
+//creature_addon table
+bool Creature::LoadCreaturesAddon(bool reload)
+{
+ CreatureDataAddon const *cainfo = GetCreatureAddon();
+ if(!cainfo)
+ return false;
+
+ if (cainfo->mount != 0)
+ Mount(cainfo->mount);
+
+ if (cainfo->bytes0 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
+
+ if (cainfo->bytes1 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_1, cainfo->bytes1);
+
+ if (cainfo->bytes2 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_2, cainfo->bytes2);
+
+ if (cainfo->emote != 0)
+ SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
+
+ if (cainfo->move_flags != 0)
+ SetUnitMovementFlags(cainfo->move_flags);
+
+ if(cainfo->auras)
+ {
+ for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura)
+ {
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura->spell_id);
+ if (!AdditionalSpellInfo)
+ {
+ sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id);
+ continue;
+ }
+
+ // skip already applied aura
+ if(HasAura(cAura->spell_id,cAura->effect_idx))
+ {
+ if(!reload)
+ sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has duplicate aura (spell %u effect %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id,cAura->effect_idx);
+
+ continue;
+ }
+
+ Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, cAura->effect_idx, NULL, this, this, 0);
+ AddAura(AdditionalAura);
+ sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[0],GetGUIDLow(),GetEntry());
+ }
+ }
+ return true;
+}
+
+/// Send a message to LocalDefense channel for players oposition team in the zone
+void Creature::SendZoneUnderAttackMessage(Player* attacker)
+{
+ uint32 enemy_team = attacker->GetTeam();
+
+ WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4);
+ data << (uint32)GetZoneId();
+ sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE));
+}
+
+void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time)
+{
+ m_CreatureSpellCooldowns[spell_id] = end_time;
+}
+
+void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time)
+{
+ m_CreatureCategoryCooldowns[category] = apply_time;
+}
+
+void Creature::AddCreatureSpellCooldown(uint32 spellid)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo)
+ return;
+
+ uint32 cooldown = GetSpellRecoveryTime(spellInfo);
+ if(cooldown)
+ _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000);
+
+ if(spellInfo->Category)
+ _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL));
+
+ m_GlobalCooldown = spellInfo->StartRecoveryTime;
+}
+
+bool Creature::HasCategoryCooldown(uint32 spell_id) const
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(!spellInfo)
+ return false;
+
+ // check global cooldown if spell affected by it
+ if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0)
+ return true;
+
+ CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category);
+ return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL));
+}
+
+bool Creature::HasSpellCooldown(uint32 spell_id) const
+{
+ CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
+ return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id);
+}
+
+bool Creature::IsInEvadeMode() const
+{
+ return !i_motionMaster.empty() && i_motionMaster.GetCurrentMovementGeneratorType() == HOME_MOTION_TYPE;
+}
+
+bool Creature::HasSpell(uint32 spellID) const
+{
+ uint8 i;
+ for(i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ if(spellID == m_spells[i])
+ break;
+ return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells
+}
+
+time_t Creature::GetRespawnTimeEx() const
+{
+ time_t now = time(NULL);
+ if(m_respawnTime > now) // dead (no corpse)
+ return m_respawnTime;
+ else if(m_deathTimer > 0) // dead (corpse)
+ return now+m_respawnDelay+m_deathTimer/1000;
+ else
+ return now;
+}
+
+void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
+{
+ if(CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow()))
+ {
+ x = data->posX;
+ y = data->posY;
+ z = data->posZ;
+ if(ori)
+ *ori = data->orientation;
+ if(dist)
+ *dist = data->spawndist;
+ }
+ else
+ {
+ x = GetPositionX();
+ y = GetPositionY();
+ z = GetPositionZ();
+ if(ori)
+ *ori = GetOrientation();
+ if(dist)
+ *dist = 0;
+ }
+}
+
+void Creature::AllLootRemovedFromCorpse()
+{
+ if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
+ {
+ uint32 nDeathTimer;
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+
+ // corpse was not skinnable -> apply corpse looted timer
+ if (!cinfo || !cinfo->SkinLootId)
+ nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED));
+ // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
+ else
+ nDeathTimer = 0;
+
+ // update death timer only if looted timer is shorter
+ if (m_deathTimer > nDeathTimer)
+ m_deathTimer = nDeathTimer;
+ }
+}
+
+uint32 Creature::getLevelForTarget( Unit const* target ) const
+{
+ if(!isWorldBoss())
+ return Unit::getLevelForTarget(target);
+
+ uint32 level = target->getLevel()+sWorld.getConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
+ if(level < 1)
+ return 1;
+ if(level > 255)
+ return 255;
+ return level;
+}
+
+char const* Creature::GetScriptName() const
+{
+ return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
+}
diff --git a/src/game/Creature.h b/src/game/Creature.h
new file mode 100644
index 00000000000..d70954acefe
--- /dev/null
+++ b/src/game/Creature.h
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_CREATURE_H
+#define MANGOSSERVER_CREATURE_H
+
+#include "Common.h"
+#include "Unit.h"
+#include "UpdateMask.h"
+#include "ItemPrototype.h"
+#include "LootMgr.h"
+#include "Database/DatabaseEnv.h"
+#include "Cell.h"
+
+struct SpellEntry;
+
+class CreatureAI;
+class Quest;
+class Player;
+class WorldSession;
+
+enum Gossip_Option
+{
+ GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
+ GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP = 1,
+ GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER = 2,
+ GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR = 4,
+ GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR = 8,
+ GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER = 16,
+ GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER = 32,
+ GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE = 64,
+ GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER = 128,
+ GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER = 256,
+ GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER = 512,
+ GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER = 1024,
+ GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON = 2048,
+ GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER = 4096,
+ GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE = 8192,
+ GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER = 16384,
+ GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
+ GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
+};
+
+enum Gossip_Guard
+{
+ GOSSIP_GUARD_BANK = 32,
+ GOSSIP_GUARD_RIDE = 33,
+ GOSSIP_GUARD_GUILD = 34,
+ GOSSIP_GUARD_INN = 35,
+ GOSSIP_GUARD_MAIL = 36,
+ GOSSIP_GUARD_AUCTION = 37,
+ GOSSIP_GUARD_WEAPON = 38,
+ GOSSIP_GUARD_STABLE = 39,
+ GOSSIP_GUARD_BATTLE = 40,
+ GOSSIP_GUARD_SPELLTRAINER = 41,
+ GOSSIP_GUARD_SKILLTRAINER = 42
+};
+
+enum Gossip_Guard_Spell
+{
+ GOSSIP_GUARD_SPELL_WARRIOR = 64,
+ GOSSIP_GUARD_SPELL_PALADIN = 65,
+ GOSSIP_GUARD_SPELL_HUNTER = 66,
+ GOSSIP_GUARD_SPELL_ROGUE = 67,
+ GOSSIP_GUARD_SPELL_PRIEST = 68,
+ GOSSIP_GUARD_SPELL_UNKNOWN1 = 69,
+ GOSSIP_GUARD_SPELL_SHAMAN = 70,
+ GOSSIP_GUARD_SPELL_MAGE = 71,
+ GOSSIP_GUARD_SPELL_WARLOCK = 72,
+ GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
+ GOSSIP_GUARD_SPELL_DRUID = 74
+};
+
+enum Gossip_Guard_Skill
+{
+ GOSSIP_GUARD_SKILL_ALCHEMY = 80,
+ GOSSIP_GUARD_SKILL_BLACKSMITH = 81,
+ GOSSIP_GUARD_SKILL_COOKING = 82,
+ GOSSIP_GUARD_SKILL_ENCHANT = 83,
+ GOSSIP_GUARD_SKILL_FIRSTAID = 84,
+ GOSSIP_GUARD_SKILL_FISHING = 85,
+ GOSSIP_GUARD_SKILL_HERBALISM = 86,
+ GOSSIP_GUARD_SKILL_LEATHER = 87,
+ GOSSIP_GUARD_SKILL_MINING = 88,
+ GOSSIP_GUARD_SKILL_SKINNING = 89,
+ GOSSIP_GUARD_SKILL_TAILORING = 90,
+ GOSSIP_GUARD_SKILL_ENGINERING = 91
+};
+
+struct GossipOption
+{
+ uint32 Id;
+ uint32 GossipId;
+ uint32 NpcFlag;
+ uint32 Icon;
+ uint32 Action;
+ std::string Option;
+};
+
+struct CreatureItem
+{
+ CreatureItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
+ : id(_item), count(_maxcount), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost), lastincr((uint32)time(NULL)) {}
+
+ uint32 id;
+ uint32 count;
+ uint32 maxcount;
+ uint32 incrtime;
+ uint32 lastincr;
+ uint32 ExtendedCost;
+};
+
+struct TrainerSpell
+{
+ SpellEntry const* spell;
+ uint32 spellcost;
+ uint32 reqskill;
+ uint32 reqskillvalue;
+ uint32 reqlevel;
+};
+
+enum CreatureFlagsExtra
+{
+ CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
+ CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
+ CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
+ CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
+ CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
+ CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
+ CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
+};
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+// from `creature_template` table
+struct CreatureInfo
+{
+ uint32 Entry;
+ uint32 HeroicEntry;
+ uint32 DisplayID_A;
+ uint32 DisplayID_A2;
+ uint32 DisplayID_H;
+ uint32 DisplayID_H2;
+ char* Name;
+ char* SubName;
+ char* IconName;
+ uint32 minlevel;
+ uint32 maxlevel;
+ uint32 minhealth;
+ uint32 maxhealth;
+ uint32 minmana;
+ uint32 maxmana;
+ uint32 armor;
+ uint32 faction_A;
+ uint32 faction_H;
+ uint32 npcflag;
+ float speed;
+ float scale;
+ uint32 rank;
+ float mindmg;
+ float maxdmg;
+ uint32 dmgschool;
+ uint32 attackpower;
+ uint32 baseattacktime;
+ uint32 rangeattacktime;
+ uint32 Flags;
+ uint32 dynamicflags;
+ uint32 family;
+ uint32 trainer_type;
+ uint32 trainer_spell;
+ uint32 classNum;
+ uint32 race;
+ float minrangedmg;
+ float maxrangedmg;
+ uint32 rangedattackpower;
+ uint32 type;
+ uint32 flag1;
+ uint32 lootid;
+ uint32 pickpocketLootId;
+ uint32 SkinLootId;
+ int32 resistance1;
+ int32 resistance2;
+ int32 resistance3;
+ int32 resistance4;
+ int32 resistance5;
+ int32 resistance6;
+ uint32 spell1;
+ uint32 spell2;
+ uint32 spell3;
+ uint32 spell4;
+ uint32 PetSpellDataId;
+ uint32 mingold;
+ uint32 maxgold;
+ char const* AIName;
+ uint32 MovementType;
+ uint32 InhabitType;
+ bool RacialLeader;
+ bool RegenHealth;
+ uint32 equipmentId;
+ uint32 MechanicImmuneMask;
+ uint32 flags_extra;
+ char const* ScriptName;
+};
+
+struct CreatureLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> SubName;
+};
+
+struct EquipmentInfo
+{
+ uint32 entry;
+ uint32 equipmodel[3];
+ uint32 equipinfo[3];
+ uint32 equipslot[3];
+};
+
+// from `creature` table
+struct CreatureData
+{
+ uint32 id; // entry in creature_template
+ uint16 mapid;
+ uint32 displayid;
+ int32 equipmentId;
+ float posX;
+ float posY;
+ float posZ;
+ float orientation;
+ uint32 spawntimesecs;
+ float spawndist;
+ uint32 currentwaypoint;
+ uint32 curhealth;
+ uint32 curmana;
+ bool is_dead;
+ uint8 movementType;
+ uint8 spawnMask;
+};
+
+struct CreatureDataAddonAura
+{
+ uint16 spell_id;
+ uint8 effect_idx;
+};
+
+// from `creature_addon` table
+struct CreatureDataAddon
+{
+ uint32 guidOrEntry;
+ uint32 mount;
+ uint32 bytes0;
+ uint32 bytes1;
+ uint32 bytes2;
+ uint32 emote;
+ uint32 move_flags;
+ CreatureDataAddonAura const* auras; // loaded as char* "spell1 eff1 spell2 eff2 ... "
+};
+
+struct CreatureModelInfo
+{
+ uint32 modelid;
+ float bounding_radius;
+ float combat_reach;
+ uint8 gender;
+ uint32 modelid_other_gender;
+};
+
+enum InhabitTypeValues
+{
+ INHABIT_GROUND = 1,
+ INHABIT_WATER = 2,
+ INHABIT_AIR = 4,
+ INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+typedef std::list<GossipOption> GossipOptionList;
+
+typedef std::map<uint32,time_t> CreatureSpellCooldowns;
+
+// max different by z coordinate for creature aggro reaction
+#define CREATURE_Z_ATTACK_RANGE 3
+
+#define MAX_VENDOR_ITEMS 255 // Limitation in item count field size in SMSG_LIST_INVENTORY
+
+class MANGOS_DLL_SPEC Creature : public Unit
+{
+ CreatureAI *i_AI;
+
+ public:
+
+ explicit Creature();
+ virtual ~Creature();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data = NULL);
+ bool LoadCreaturesAddon(bool reload = false);
+ void SelectLevel(const CreatureInfo *cinfo);
+ void LoadEquipment(uint32 equip_entry, bool force=false);
+
+ uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
+ char const* GetSubName() const { return GetCreatureInfo()->SubName; }
+
+ void Update( uint32 time ); // overwrited Unit::Update
+ void GetRespawnCoord(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const;
+ uint32 GetEquipmentId() const { return m_equipmentId; }
+
+ bool isPet() const { return m_isPet; }
+ void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
+ bool isTotem() const { return m_isTotem; }
+ bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
+ bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
+ bool canWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
+ bool canSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
+ bool canFly() const { return GetCreatureInfo()->InhabitType & INHABIT_AIR; }
+ ///// TODO RENAME THIS!!!!!
+ bool isCanTrainingOf(Player* player, bool msg) const;
+ bool isCanIneractWithBattleMaster(Player* player, bool msg) const;
+ bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const;
+ bool IsOutOfThreatArea(Unit* pVictim) const;
+ bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
+ // redefine Unit::IsImmunedToSpell
+ bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
+ // redefine Unit::IsImmunedToSpellEffect
+ bool isElite() const
+ {
+ if(isPet())
+ return false;
+
+ uint32 rank = GetCreatureInfo()->rank;
+ return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE;
+ }
+
+ bool isWorldBoss() const
+ {
+ if(isPet())
+ return false;
+
+ return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS;
+ }
+
+ uint32 getLevelForTarget(Unit const* target) const; // overwrite Unit::getLevelForTarget for boss level support
+
+ bool IsInEvadeMode() const;
+
+ bool AIM_Initialize();
+
+ void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
+ CreatureAI* AI() { return i_AI; }
+
+ uint32 GetShieldBlockValue() const //dunno mob block value
+ {
+ return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
+ }
+
+ SpellSchoolMask GetMeleeDamageSchoolMask() const { return m_meleeDamageSchoolMask; }
+ void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
+
+ void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
+ void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
+ void AddCreatureSpellCooldown(uint32 spellid);
+ bool HasSpellCooldown(uint32 spell_id) const;
+ bool HasCategoryCooldown(uint32 spell_id) const;
+
+ bool HasSpell(uint32 spellID) const;
+
+ bool UpdateEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
+ bool UpdateStats(Stats stat);
+ bool UpdateAllStats();
+ void UpdateResistances(uint32 school);
+ void UpdateArmor();
+ void UpdateMaxHealth();
+ void UpdateMaxPower(Powers power);
+ void UpdateAttackPowerAndDamage(bool ranged = false);
+ void UpdateDamagePhysical(WeaponAttackType attType);
+ uint32 GetCurrentEquipmentId() { return m_equipmentId; }
+ float GetSpellDamageMod(int32 Rank);
+
+ /*********************************************************/
+ /*** VENDOR SYSTEM ***/
+ /*********************************************************/
+ void LoadGoods(); // must be called before access to vendor items, lazy loading at first call
+ void ReloadGoods() { m_itemsLoaded = false; LoadGoods(); }
+
+ CreatureItem* GetItem(uint32 slot)
+ {
+ if(slot>=m_vendor_items.size()) return NULL;
+ return &m_vendor_items[slot];
+ }
+ uint8 GetItemCount() const { return m_vendor_items.size(); }
+ void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
+ {
+ m_vendor_items.push_back(CreatureItem(item, maxcount, ptime, ExtendedCost));
+ }
+ bool RemoveItem( uint32 item_id )
+ {
+ for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i )
+ {
+ if(i->id==item_id)
+ {
+ m_vendor_items.erase(i);
+ return true;
+ }
+ }
+ return false;
+ }
+ CreatureItem* FindItem(uint32 item_id)
+ {
+ for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i )
+ if(i->id==item_id)
+ return &*i;
+ return NULL;
+ }
+
+ /*********************************************************/
+ /*** TRAINER SYSTEM ***/
+ /*********************************************************/
+ typedef std::list<TrainerSpell> SpellsList;
+ void LoadTrainerSpells(); // must be called before access to trainer spells, lazy loading at first call
+ void ReloadTrainerSpells() { m_trainerSpellsLoaded = false; LoadTrainerSpells(); }
+ SpellsList const& GetTrainerSpells() const { return m_trainer_spells; }
+ uint32 GetTrainerType() const { return m_trainer_type; }
+
+ CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; }
+ CreatureDataAddon const* GetCreatureAddon() const;
+ char const* GetScriptName() const;
+
+ void prepareGossipMenu( Player *pPlayer,uint32 gossipid );
+ void sendPreparedGossip( Player* player);
+ void OnGossipSelect(Player* player, uint32 option);
+ void OnPoiSelect(Player* player, GossipOption const *gossip);
+
+ uint32 GetGossipTextId(uint32 action, uint32 zoneid);
+ uint32 GetNpcTextId();
+ void LoadGossipOptions();
+ GossipOption const* GetGossipOption( uint32 id ) const;
+ void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
+
+ void setEmoteState(uint8 emote) { m_emoteState = emote; };
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(text,TargetGuid,IsBossEmote); }
+ void Whisper(const char* text, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(text,receiver,IsBossWhisper); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); }
+ void Whisper(int32 textId, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(textId,receiver,IsBossWhisper); }
+
+ void setDeathState(DeathState s); // overwrite virtual Unit::setDeathState
+
+ bool LoadFromDB(uint32 guid, Map *map);
+ void SaveToDB();
+ // overwrited in Pet
+ virtual void SaveToDB(uint32 mapid, uint8 spawnMask);
+ virtual void DeleteFromDB(); // overwrited in Pet
+
+ Loot loot;
+ bool lootForPickPocketed;
+ bool lootForBody;
+ Player *GetLootRecipient() const;
+ bool hasLootRecipient() const { return m_lootRecipient!=0; }
+
+ void SetLootRecipient (Unit* unit);
+ void AllLootRemovedFromCorpse();
+
+ SpellEntry const *reachWithSpellAttack(Unit *pVictim);
+ SpellEntry const *reachWithSpellCure(Unit *pVictim);
+
+ uint32 m_spells[CREATURE_MAX_SPELLS];
+ CreatureSpellCooldowns m_CreatureSpellCooldowns;
+ CreatureSpellCooldowns m_CreatureCategoryCooldowns;
+ uint32 m_GlobalCooldown;
+
+ float GetAttackDistance(Unit const* pl) const;
+
+ void CallAssistence();
+ void SetNoCallAssistence(bool val) { m_AlreadyCallAssistence = val; }
+
+ MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; }
+ void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
+
+ // for use only in LoadHelper, Map::Add Map::CreatureCellRelocation
+ Cell const& GetCurrentCell() const { return m_currentCell; }
+ void SetCurrentCell(Cell const& cell) { m_currentCell = cell; }
+
+ bool IsVisibleInGridForPlayer(Player* pl) const;
+
+ void RemoveCorpse();
+
+ time_t const& GetRespawnTime() const { return m_respawnTime; }
+ time_t GetRespawnTimeEx() const;
+ void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(NULL) + respawn : 0; }
+ void Respawn();
+ void SaveRespawnTime();
+
+ uint32 GetRespawnDelay() const { return m_respawnDelay; }
+ void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
+
+ float GetRespawnRadius() const { return m_respawnradius; }
+ void SetRespawnRadius(float dist) { m_respawnradius = dist; }
+
+ uint32 m_groupLootTimer; // (msecs)timer used for group loot
+ uint64 lootingGroupLeaderGUID; // used to find group which is looting corpse
+
+ void SendZoneUnderAttackMessage(Player* attacker);
+
+ bool hasQuest(uint32 quest_id) const;
+ bool hasInvolvedQuest(uint32 quest_id) const;
+
+ GridReference<Creature> &GetGridRef() { return m_gridRef; }
+ bool isRegeneratingHealth() { return m_regenHealth; }
+ 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)
+ return 0;
+ else
+ return m_charmInfo->GetCharmSpell(pos)->spellId;
+ }
+
+ void SetCombatStartPosition(float x, float y, float z) { CombatStartX = x; CombatStartY = y; CombatStartZ = z; }
+ void GetCombatStartPosition(float &x, float &y, float &z) { x = CombatStartX; y = CombatStartY; z = CombatStartZ; }
+
+ protected:
+ bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
+ bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
+
+ // vendor items
+ typedef std::vector<CreatureItem> CreatureItems;
+ CreatureItems m_vendor_items;
+ bool m_itemsLoaded; // vendor items loading state
+
+ // trainer spells
+ bool m_trainerSpellsLoaded; // trainer spells loading state
+ SpellsList m_trainer_spells;
+ uint32 m_trainer_type; // trainer type based at trainer spells, can be different from creature_template value.
+ // req. for correct show non-prof. trainers like weaponmaster.
+ void _RealtimeSetCreatureInfo();
+
+ static float _GetHealthMod(int32 Rank);
+ static float _GetDamageMod(int32 Rank);
+
+ uint32 m_lootMoney;
+ uint64 m_lootRecipient;
+
+ /// Timers
+ uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance
+ time_t m_respawnTime; // (secs) time of next respawn
+ uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
+ uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
+ float m_respawnradius;
+
+ bool m_gossipOptionLoaded;
+ GossipOptionList m_goptions;
+ uint32 m_NPCTextId; // cached value
+
+ uint8 m_emoteState;
+ bool m_isPet; // set only in Pet::Pet
+ bool m_isTotem; // set only in Totem::Totem
+ void RegenerateMana();
+ void RegenerateHealth();
+ uint32 m_regenTimer;
+ MovementGeneratorType m_defaultMovementType;
+ Cell m_currentCell; // store current cell where creature listed
+ uint32 m_DBTableGuid;
+ uint32 m_equipmentId;
+
+ bool m_AlreadyCallAssistence;
+ bool m_regenHealth;
+ bool m_AI_locked;
+ bool m_isDeadByDefault;
+
+ SpellSchoolMask m_meleeDamageSchoolMask;
+ uint32 m_originalEntry;
+
+ float CombatStartX;
+ float CombatStartY;
+ float CombatStartZ;
+ private:
+ GridReference<Creature> m_gridRef;
+ CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry())
+};
+#endif
diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp
new file mode 100644
index 00000000000..06b3d447327
--- /dev/null
+++ b/src/game/CreatureAI.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "CreatureAI.h"
+#include "HateMatrix.h"
+
+CreatureAI::~CreatureAI()
+{
+}
+
+uint32 HateBinder::si_noHateValue=0;
diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h
new file mode 100644
index 00000000000..185fb571bb2
--- /dev/null
+++ b/src/game/CreatureAI.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_CREATUREAI_H
+#define MANGOS_CREATUREAI_H
+
+#include "Common.h"
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "Dynamic/ObjectRegistry.h"
+#include "Dynamic/FactoryHolder.h"
+
+class Unit;
+class Creature;
+struct SpellEntry;
+
+#define TIME_INTERVAL_LOOK 5000
+#define VISIBILITY_RANGE 10000
+
+class MANGOS_DLL_SPEC CreatureAI
+{
+ public:
+
+ virtual ~CreatureAI();
+
+ // Called if IsVisible(Unit *who) is true at each *who move
+ virtual void MoveInLineOfSight(Unit *) = 0;
+
+ // Called at each attack of m_creature by any victim
+ virtual void AttackStart(Unit *) = 0;
+
+ // Called at stopping attack by any attacker
+ virtual void EnterEvadeMode() = 0;
+
+ // Called at any heal cast/item used (call non implemented)
+ virtual void HealBy(Unit * /*healer*/, uint32 /*amount_healed*/) {}
+
+ // Called at any Damage to any victim (before damage apply)
+ virtual void DamageDeal(Unit * /*done_to*/, uint32 & /*damage*/) {}
+
+ // Called at any Damage from any attacker (before damage apply)
+ virtual void DamageTaken(Unit *done_by, uint32 & /*damage*/) { AttackedBy(done_by); }
+
+ // Is unit visible for MoveInLineOfSight
+ virtual bool IsVisible(Unit *) const = 0;
+
+ // Called at World update tick
+ virtual void UpdateAI(const uint32 diff ) = 0;
+
+ // Called when the creature is killed
+ virtual void JustDied(Unit *) {}
+
+ // Called when the creature kills a unit
+ virtual void KilledUnit(Unit *) {}
+
+ // Called when the creature summon successfully other creature
+ virtual void JustSummoned(Creature* ) {}
+
+ virtual void SummonedCreatureDespawn(Creature* /*unit*/) {}
+
+ // Called when hit by a spell
+ virtual void SpellHit(Unit*, const SpellEntry*) {}
+
+ // Called when vitim entered water and creature can not enter water
+ virtual bool canReachByRangeAttack(Unit*) { return false; }
+
+ // Called when the creature is attacked
+ virtual void AttackedBy(Unit * /*attacker*/) {}
+
+ // Called when creature is spawned or respawned (for reseting variables)
+ virtual void JustRespawned() {}
+
+ // Called at waypoint reached or point movement finished
+ virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {}
+};
+
+struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
+{
+
+ SelectableAI(const char *id) : FactoryHolder<CreatureAI>(id) {}
+};
+
+template<class REAL_AI>
+struct CreatureAIFactory : public SelectableAI
+{
+ CreatureAIFactory(const char *name) : SelectableAI(name) {}
+
+ CreatureAI* Create(void *) const;
+
+ int Permit(const Creature *c) const { return REAL_AI::Permissible(c); }
+};
+
+enum Permitions
+{
+ PERMIT_BASE_NO = -1,
+ PERMIT_BASE_IDLE = 1,
+ PERMIT_BASE_REACTIVE = 100,
+ PERMIT_BASE_PROACTIVE = 200,
+ PERMIT_BASE_FACTION_SPECIFIC = 400,
+ PERMIT_BASE_SPECIAL = 800
+};
+
+typedef FactoryHolder<CreatureAI> CreatureAICreator;
+typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
+typedef FactoryHolder<CreatureAI>::FactoryHolderRepository CreatureAIRepository;
+#endif
diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h
new file mode 100644
index 00000000000..65ac4708238
--- /dev/null
+++ b/src/game/CreatureAIImpl.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef CREATUREAIIMPL_H
+#define CREATUREAIIMPL_H
+
+#include "CreatureAI.h"
+
+template<class REAL_AI>
+inline CreatureAI*
+CreatureAIFactory<REAL_AI>::Create(void *data) const
+{
+ Creature* creature = reinterpret_cast<Creature *>(data);
+ return (new REAL_AI(*creature));
+}
+#endif
diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp
new file mode 100644
index 00000000000..3a5e3dcfc91
--- /dev/null
+++ b/src/game/CreatureAIRegistry.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "CreatureAIRegistry.h"
+#include "NullCreatureAI.h"
+#include "ReactorAI.h"
+#include "AggressorAI.h"
+#include "GuardAI.h"
+#include "PetAI.h"
+#include "TotemAI.h"
+#include "RandomMovementGenerator.h"
+#include "CreatureAIImpl.h"
+#include "MovementGeneratorImpl.h"
+#include "MapManager.h"
+#include "CreatureAIRegistry.h"
+#include "WaypointMovementGenerator.h"
+
+namespace AIRegistry
+{
+ void Initialize()
+ {
+ (new CreatureAIFactory<NullCreatureAI>("NullAI"))->RegisterSelf();
+ (new CreatureAIFactory<AggressorAI>("AggressorAI"))->RegisterSelf();
+ (new CreatureAIFactory<ReactorAI>("ReactorAI"))->RegisterSelf();
+ (new CreatureAIFactory<GuardAI>("GuardAI"))->RegisterSelf();
+ (new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf();
+ (new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
+
+ (new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
+ (new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
+ }
+}
diff --git a/src/game/CreatureAIRegistry.h b/src/game/CreatureAIRegistry.h
new file mode 100644
index 00000000000..a254b6718b2
--- /dev/null
+++ b/src/game/CreatureAIRegistry.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_CREATUREAIREGISTRY_H
+#define MANGOS_CREATUREAIREGISTRY_H
+
+namespace AIRegistry
+{
+ void Initialize(void);
+}
+#endif
diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp
new file mode 100644
index 00000000000..2e8b6f1a230
--- /dev/null
+++ b/src/game/CreatureAISelector.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "CreatureAIImpl.h"
+#include "CreatureAISelector.h"
+#include "NullCreatureAI.h"
+#include "Policies/SingletonImp.h"
+#include "MovementGenerator.h"
+#include "ScriptCalls.h"
+#include "Pet.h"
+
+INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
+INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
+
+namespace FactorySelector
+{
+ CreatureAI* selectAI(Creature *creature)
+ {
+ // Allow scripting AI for normal creatures and not controlled pets (guardians and mini-pets)
+ if((!creature->isPet() || !((Pet*)creature)->isControlled()) && !creature->isCharmed())
+ if(CreatureAI* scriptedAI = Script->GetAI(creature))
+ return scriptedAI;
+
+ CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance());
+ assert( creature->GetCreatureInfo() != NULL );
+ CreatureInfo const *cinfo=creature->GetCreatureInfo();
+
+ const CreatureAICreator *ai_factory = NULL;
+
+ std::string ainame=cinfo->AIName;
+
+ // select by script name
+ if( !ainame.empty())
+ ai_factory = ai_registry.GetRegistryItem( ainame.c_str() );
+
+ // select by NPC flags
+ if(!ai_factory)
+ {
+ if( creature->isGuard() )
+ ainame="GuardAI";
+ else if(creature->isPet() || creature->isCharmed())
+ ainame="PetAI";
+ else if(creature->isTotem())
+ ainame="TotemAI";
+
+ ai_factory = ai_registry.GetRegistryItem( ainame.c_str() );
+ }
+
+ // select by permit check
+ if(!ai_factory)
+ {
+ int best_val = -1;
+ typedef CreatureAIRegistry::RegistryMapType RMT;
+ RMT const &l = ai_registry.GetRegisteredItems();
+ for( RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
+ {
+ const CreatureAICreator *factory = iter->second;
+ const SelectableAI *p = dynamic_cast<const SelectableAI *>(factory);
+ assert( p != NULL );
+ int val = p->Permit(creature);
+ if( val > best_val )
+ {
+ best_val = val;
+ ai_factory = p;
+ }
+ }
+ }
+
+ // select NullCreatureAI if not another cases
+ ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
+
+ DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str() );
+ return ( ai_factory == NULL ? new NullCreatureAI : ai_factory->Create(creature) );
+ }
+
+ MovementGenerator* selectMovementGenerator(Creature *creature)
+ {
+ MovementGeneratorRegistry &mv_registry(MovementGeneratorRepository::Instance());
+ assert( creature->GetCreatureInfo() != NULL );
+ const MovementGeneratorCreator *mv_factory = mv_registry.GetRegistryItem( creature->GetDefaultMovementType());
+
+ /* if( mv_factory == NULL )
+ {
+ int best_val = -1;
+ std::vector<std::string> l;
+ mv_registry.GetRegisteredItems(l);
+ for( std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
+ {
+ const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
+ const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
+ assert( p != NULL );
+ int val = p->Permit(creature);
+ if( val > best_val )
+ {
+ best_val = val;
+ mv_factory = p;
+ }
+ }
+ }*/
+
+ return ( mv_factory == NULL ? NULL : mv_factory->Create(creature) );
+
+ }
+}
diff --git a/src/game/CreatureAISelector.h b/src/game/CreatureAISelector.h
new file mode 100644
index 00000000000..328850dbc3d
--- /dev/null
+++ b/src/game/CreatureAISelector.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_CREATUREAISELECTOR_H
+#define MANGOS_CREATUREAISELECTOR_H
+
+class CreatureAI;
+class Creature;
+class MovementGenerator;
+
+namespace FactorySelector
+{
+ CreatureAI* selectAI(Creature *);
+ MovementGenerator* selectMovementGenerator(Creature *);
+}
+#endif
diff --git a/src/game/DestinationHolder.cpp b/src/game/DestinationHolder.cpp
new file mode 100644
index 00000000000..2c427ad7702
--- /dev/null
+++ b/src/game/DestinationHolder.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "DestinationHolder.h"
diff --git a/src/game/DestinationHolder.h b/src/game/DestinationHolder.h
new file mode 100644
index 00000000000..93705bb67cb
--- /dev/null
+++ b/src/game/DestinationHolder.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_DESTINATION_HOLDER_H
+#define MANGOS_DESTINATION_HOLDER_H
+
+#include "Platform/Define.h"
+#include "Timer.h"
+
+class WorldObject;
+
+#define TRAVELLER_UPDATE_INTERVAL 300
+
+template<typename TRAVELLER>
+class MANGOS_DLL_DECL DestinationHolder
+{
+ TimeTrackerSmall i_tracker;
+ uint32 i_totalTravelTime;
+ uint32 i_timeElapsed;
+ bool i_destSet;
+ float i_fromX, i_fromY, i_fromZ;
+ float i_destX, i_destY, i_destZ;
+
+ public:
+ DestinationHolder() : i_tracker(TRAVELLER_UPDATE_INTERVAL), i_totalTravelTime(0), i_timeElapsed(0),
+ i_destSet(false), i_fromX(0), i_fromY(0), i_fromZ(0), i_destX(0), i_destY(0), i_destZ(0) {}
+
+ uint32 SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove = true);
+ void GetDestination(float &x, float &y, float &z) const { x = i_destX; y = i_destY; z = i_destZ; }
+ bool UpdateExpired(void) const { return i_tracker.Passed(); }
+ void ResetUpdate(uint32 t = TRAVELLER_UPDATE_INTERVAL) { i_tracker.Reset(t); }
+ uint32 GetTotalTravelTime(void) const { return i_totalTravelTime; }
+ void IncreaseTravelTime(uint32 increment) { i_totalTravelTime += increment; }
+ bool HasDestination(void) const { return i_destSet; }
+ float GetDestinationDiff(float x, float y, float z) const;
+ bool HasArrived(void) const { return (i_totalTravelTime == 0 || i_timeElapsed >= i_totalTravelTime); }
+ bool UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update=false, bool micro_movement=false);
+ uint32 StartTravel(TRAVELLER &traveller, bool sendMove = true);
+ void GetLocationNow(uint32 mapid, float &x, float &y, float &z, bool is3D = false) const;
+ void GetLocationNowNoMicroMovement(float &x, float &y, float &z) const; // For use without micro movement
+ float GetDistance2dFromDestSq(const WorldObject &obj) const;
+
+ private:
+ void _findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y);
+
+};
+#endif
diff --git a/src/game/DestinationHolderImp.h b/src/game/DestinationHolderImp.h
new file mode 100644
index 00000000000..8f5efa54b0c
--- /dev/null
+++ b/src/game/DestinationHolderImp.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_DESTINATIONHOLDERIMP_H
+#define MANGOS_DESTINATIONHOLDERIMP_H
+
+#include "Creature.h"
+#include "MapManager.h"
+#include "DestinationHolder.h"
+
+#include <cmath>
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y)
+{
+ /* given the point (x1, y1) and (x2, y2).. need to find the point (x,y) on the same line
+ * such that the distance from (x, y) to (x2, y2) is offset.
+ * Let the distance of p1 to p2 = d.. then the ratio of offset/d = (x2-x)/(x2-x1)
+ * hence x = x2 - (offset/d)*(x2-x1)
+ * like wise offset/d = (y2-y)/(y2-y1);
+ */
+ if( offset == 0 )
+ {
+ x = x2;
+ y = y2;
+ }
+ else
+ {
+ double x_diff = double(x2 - x1);
+ double y_diff = double(y2 - y1);
+ double distance_d = (double)((x_diff*x_diff) + (y_diff * y_diff));
+ if(distance_d == 0)
+ {
+ x = x2;
+ y = y2;
+ }
+ else
+ {
+ distance_d = ::sqrt(distance_d); // starting distance
+ double distance_ratio = (double)(distance_d - offset)/(double)distance_d;
+ // line above has revised formula which is more correct, I think
+ x = (float)(x1 + (distance_ratio*x_diff));
+ y = (float)(y1 + (distance_ratio*y_diff));
+ }
+ }
+}
+
+template<typename TRAVELLER>
+uint32
+DestinationHolder<TRAVELLER>::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove)
+{
+ i_destSet = true;
+ i_destX = dest_x;
+ i_destY = dest_y;
+ i_destZ = dest_z;
+
+ return StartTravel(traveller, sendMove);
+}
+
+template<typename TRAVELLER>
+uint32
+DestinationHolder<TRAVELLER>::StartTravel(TRAVELLER &traveller, bool sendMove)
+{
+ if(!i_destSet) return 0;
+
+ i_fromX = traveller.GetPositionX();
+ i_fromY = traveller.GetPositionY();
+ i_fromZ = traveller.GetPositionZ();
+
+ float dx = i_destX - i_fromX;
+ float dy = i_destY - i_fromY;
+ float dz = i_destZ - i_fromZ;
+
+ float dist;
+ //Should be for Creature Flying and Swimming.
+ if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT))
+ dist = sqrt((dx*dx) + (dy*dy) + (dz*dz));
+ else //Walking on the ground
+ dist = sqrt((dx*dx) + (dy*dy));
+ float speed = traveller.Speed();
+
+ speed *= 0.001f; // speed is in seconds so convert from second to millisecond
+ i_totalTravelTime = static_cast<uint32>(dist/speed);
+ i_timeElapsed = 0;
+ if(sendMove)
+ traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime);
+ return i_totalTravelTime;
+}
+
+template<typename TRAVELLER>
+bool
+DestinationHolder<TRAVELLER>::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update, bool micro_movement)
+{
+ if(!micro_movement)
+ {
+ i_tracker.Update(diff);
+ i_timeElapsed += diff;
+ if( i_tracker.Passed() || force_update )
+ {
+ ResetUpdate();
+ if(!i_destSet) return true;
+ float x,y,z;
+ GetLocationNowNoMicroMovement(x, y, z);
+ if( x == -431602080 )
+ return false;
+ if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y )
+ {
+ float ori = traveller.GetTraveller().GetAngle(x, y);
+ traveller.Relocation(x, y, z, ori);
+ }
+ return true;
+ }
+ return false;
+ }
+ i_tracker.Update(diff);
+ i_timeElapsed += diff;
+ if( i_tracker.Passed() || force_update )
+ {
+ ResetUpdate();
+ if(!i_destSet) return true;
+ float x,y,z;
+
+ if(!traveller.GetTraveller().hasUnitState(UNIT_STAT_MOVING | UNIT_STAT_IN_FLIGHT))
+ return true;
+
+ if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT))
+ GetLocationNow(traveller.GetTraveller().GetMapId() ,x, y, z, true); // Should repositione Object with right Coord, so I can bypass some Grid Relocation
+ else
+ GetLocationNow(traveller.GetTraveller().GetMapId(), x, y, z, false);
+
+ if( x == -431602080 )
+ return false;
+
+ if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y )
+ {
+ float ori = traveller.GetTraveller().GetAngle(x, y);
+ traveller.Relocation(x, y, z, ori);
+ }
+ // Change movement computation to micro movement based on last tick coords, this makes system work
+ // even on multiple floors zones without hugh vmaps usage ;)
+
+ // Take care of underrun of uint32
+ if (i_totalTravelTime >= i_timeElapsed)
+ i_totalTravelTime -= i_timeElapsed; // Consider only the remaining part
+ else
+ i_totalTravelTime = 0;
+
+ i_timeElapsed = 0;
+ i_fromX = x; // and change origine
+ i_fromY = y; // then I take into account only micro movement
+ i_fromZ = z;
+ return true;
+ }
+ return false;
+}
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::GetLocationNow(uint32 mapid, float &x, float &y, float &z, bool is3D) const
+{
+ if( HasArrived() )
+ {
+ x = i_destX;
+ y = i_destY;
+ z = i_destZ;
+ }
+ else if(HasDestination())
+ {
+ double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime;
+ const float distanceX = ((i_destX - i_fromX) * percent_passed);
+ const float distanceY = ((i_destY - i_fromY) * percent_passed);
+ const float distanceZ = ((i_destZ - i_fromZ) * percent_passed);
+ x = i_fromX + distanceX;
+ y = i_fromY + distanceY;
+ float z2 = i_fromZ + distanceZ;
+ // All that is not finished but previous code neither... Traveller need be able to swim.
+ if(is3D)
+ z = z2;
+ else
+ {
+ //That part is good for mob Walking on the floor. But the floor is not allways what we thought.
+ z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y,i_fromZ,false); // Disable cave check
+ const float groundDist = sqrt(distanceX*distanceX + distanceY*distanceY);
+ const float zDist = fabs(i_fromZ - z) + 0.000001f;
+ const float slope = groundDist / zDist;
+ if(slope < 1.0f) // This prevents the ground returned by GetHeight to be used when in cave
+ z = z2; // a climb or jump of more than 45 is denied
+ }
+ }
+}
+
+template<typename TRAVELLER>
+float
+DestinationHolder<TRAVELLER>::GetDistance2dFromDestSq(const WorldObject &obj) const
+{
+ float x,y,z;
+ obj.GetPosition(x,y,z);
+ return (i_destX-x)*(i_destX-x)+(i_destY-y)*(i_destY-y);
+}
+
+template<typename TRAVELLER>
+float
+DestinationHolder<TRAVELLER>::GetDestinationDiff(float x, float y, float z) const
+{
+ return sqrt(((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) + ((z-i_destZ)*(z-i_destZ)));
+}
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::GetLocationNowNoMicroMovement(float &x, float &y, float &z) const
+{
+ if( HasArrived() )
+ {
+ x = i_destX;
+ y = i_destY;
+ z = i_destZ;
+ }
+ else
+ {
+ double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime;
+ x = i_fromX + ((i_destX - i_fromX) * percent_passed);
+ y = i_fromY + ((i_destY - i_fromY) * percent_passed);
+ z = i_fromZ + ((i_destZ - i_fromZ) * percent_passed);
+ }
+}
+
+#endif
diff --git a/src/game/DuelHandler.cpp b/src/game/DuelHandler.cpp
new file mode 100644
index 00000000000..1019c68d908
--- /dev/null
+++ b/src/game/DuelHandler.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "Player.h"
+
+void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ uint64 guid;
+ Player *pl;
+ Player *plTarget;
+
+ if(!GetPlayer()->duel) // ignore accept from duel-sender
+ return;
+
+ recvPacket >> guid;
+
+ pl = GetPlayer();
+ plTarget = pl->duel->opponent;
+
+ if(pl == pl->duel->initiator || !plTarget || pl == plTarget || pl->duel->startTime != 0 || plTarget->duel->startTime != 0)
+ return;
+
+ //sLog.outDebug( "WORLD: received CMSG_DUEL_ACCEPTED" );
+ DEBUG_LOG("Player 1 is: %u (%s)", pl->GetGUIDLow(),pl->GetName());
+ DEBUG_LOG("Player 2 is: %u (%s)", plTarget->GetGUIDLow(),plTarget->GetName());
+
+ time_t now = time(NULL);
+ pl->duel->startTimer = now;
+ plTarget->duel->startTimer = now;
+
+ WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
+ data << (uint32)3000; // 3 seconds
+ pl->GetSession()->SendPacket(&data);
+ plTarget->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ //sLog.outDebug( "WORLD: received CMSG_DUEL_CANCELLED" );
+
+ // no duel requested
+ if(!GetPlayer()->duel)
+ return;
+
+ // player surrendered in a duel using /forfeit
+ if(GetPlayer()->duel->startTime != 0)
+ {
+ GetPlayer()->CombatStopWithPets(true);
+ if(GetPlayer()->duel->opponent)
+ GetPlayer()->duel->opponent->CombatStopWithPets(true);
+
+ GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg
+ GetPlayer()->DuelComplete(DUEL_WON);
+ return;
+ }
+
+ // player either discarded the duel using the "discard button"
+ // or used "/forfeit" before countdown reached 0
+ uint64 guid;
+ recvPacket >> guid;
+
+ GetPlayer()->DuelComplete(DUEL_INTERUPTED);
+}
diff --git a/src/game/DynamicObject.cpp b/src/game/DynamicObject.cpp
new file mode 100644
index 00000000000..d01a6f84a4d
--- /dev/null
+++ b/src/game/DynamicObject.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "GameObject.h"
+#include "UpdateMask.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectAccessor.h"
+#include "Database/DatabaseEnv.h"
+#include "SpellAuras.h"
+#include "MapManager.h"
+#include "GridNotifiers.h"
+#include "CellImpl.h"
+#include "GridNotifiersImpl.h"
+
+DynamicObject::DynamicObject() : WorldObject()
+{
+ m_objectType |= TYPEMASK_DYNAMICOBJECT;
+ m_objectTypeId = TYPEID_DYNAMICOBJECT;
+ // 2.3.2 - 0x58
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+
+ m_valuesCount = DYNAMICOBJECT_END;
+}
+
+void DynamicObject::AddToWorld()
+{
+ ///- Register the dynamicObject for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Object::AddToWorld();
+}
+
+void DynamicObject::RemoveFromWorld()
+{
+ ///- Remove the dynamicObject from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Object::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());
+
+ WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetMapId());
+ Relocate(x,y,z,0);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",spellId,effIndex,GetPositionX(),GetPositionY());
+ return false;
+ }
+
+ SetUInt32Value( OBJECT_FIELD_ENTRY, spellId );
+ SetFloatValue( OBJECT_FIELD_SCALE_X, 1 );
+ SetUInt64Value( DYNAMICOBJECT_CASTER, caster->GetGUID() );
+ SetUInt32Value( DYNAMICOBJECT_BYTES, 0x00000001 );
+ SetUInt32Value( DYNAMICOBJECT_SPELLID, spellId );
+ SetFloatValue( DYNAMICOBJECT_RADIUS, radius);
+ SetFloatValue( DYNAMICOBJECT_POS_X, x );
+ SetFloatValue( DYNAMICOBJECT_POS_Y, y );
+ SetFloatValue( DYNAMICOBJECT_POS_Z, z );
+ SetUInt32Value( DYNAMICOBJECT_CASTTIME, getMSTime() ); // new 2.4.0
+
+ m_aliveDuration = duration;
+ m_radius = radius;
+ m_effIndex = effIndex;
+ m_spellId = spellId;
+ m_casterGuid = caster->GetGUID();
+ return true;
+}
+
+Unit* DynamicObject::GetCaster() const
+{
+ // can be not found in some cases
+ return ObjectAccessor::GetUnit(*this,m_casterGuid);
+}
+
+void DynamicObject::Update(uint32 p_time)
+{
+ // caster can be not in world at time dynamic object update, but dynamic object not yet deleted in Unit destructor
+ Unit* caster = GetCaster();
+ if(!caster)
+ {
+ Delete();
+ return;
+ }
+
+ bool deleteThis = false;
+
+ if(m_aliveDuration > int32(p_time))
+ m_aliveDuration -= p_time;
+ else
+ deleteThis = true;
+
+ // TODO: make a timer and update this in larger intervals
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::DynamicObjectUpdater notifier(*this,caster);
+
+ TypeContainerVisitor<MaNGOS::DynamicObjectUpdater, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::DynamicObjectUpdater, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ if(deleteThis)
+ {
+ caster->RemoveDynObjectWithGUID(GetGUID());
+ Delete();
+ }
+}
+
+void DynamicObject::Delete()
+{
+ SendObjectDeSpawnAnim(GetGUID());
+ AddObjectToRemoveList();
+}
+
+void DynamicObject::Delay(int32 delaytime)
+{
+ m_aliveDuration -= delaytime;
+ for(AffectedSet::iterator iunit= m_affected.begin();iunit != m_affected.end();++iunit)
+ if (*iunit)
+ (*iunit)->DelayAura(m_spellId, m_effIndex, delaytime);
+}
+
+bool DynamicObject::isVisibleForInState(Player const* u, bool inVisibleList) const
+{
+ return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f));
+}
diff --git a/src/game/DynamicObject.h b/src/game/DynamicObject.h
new file mode 100644
index 00000000000..279a763d4ce
--- /dev/null
+++ b/src/game/DynamicObject.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_DYNAMICOBJECT_H
+#define MANGOSSERVER_DYNAMICOBJECT_H
+
+#include "Object.h"
+
+class Unit;
+struct SpellEntry;
+
+class DynamicObject : public WorldObject
+{
+ public:
+ typedef std::set<Unit*> AffectedSet;
+ explicit DynamicObject();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32 effIndex, float x, float y, float z, int32 duration, float radius);
+ void Update(uint32 p_time);
+ void Delete();
+ uint32 GetSpellId() const { return m_spellId; }
+ uint32 GetEffIndex() const { return m_effIndex; }
+ uint32 GetDuration() const { return m_aliveDuration; }
+ uint64 GetCasterGUID() const { return m_casterGuid; }
+ Unit* GetCaster() const;
+ float GetRadius() const { return m_radius; }
+ bool IsAffecting(Unit *unit) const { return m_affected.find(unit) != m_affected.end(); }
+ void AddAffected(Unit *unit) { m_affected.insert(unit); }
+ void RemoveAffected(Unit *unit) { m_affected.erase(unit); }
+ void Delay(int32 delaytime);
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
+ void Whisper(const char* text, uint64 receiver) { MonsterWhisper(text,receiver); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
+ void Whisper(int32 textId,uint64 receiver) { MonsterWhisper(textId,receiver); }
+
+ GridReference<DynamicObject> &GetGridRef() { return m_gridRef; }
+ protected:
+ uint64 m_casterGuid;
+ uint32 m_spellId;
+ uint32 m_effIndex;
+ int32 m_aliveDuration;
+ time_t m_nextThinkTime;
+ float m_radius;
+ AffectedSet m_affected;
+ private:
+ GridReference<DynamicObject> m_gridRef;
+};
+#endif
diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp
new file mode 100644
index 00000000000..80ecf13922f
--- /dev/null
+++ b/src/game/FleeingMovementGenerator.cpp
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "MapManager.h"
+#include "FleeingMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "ObjectAccessor.h"
+
+#define MIN_QUIET_DISTANCE 28.0f
+#define MAX_QUIET_DISTANCE 43.0f
+
+template<class T>
+void
+FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if( !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) )
+ return;
+
+ if(!_setMoveData(owner))
+ return;
+
+ float x, y, z;
+ if(!_getPoint(owner, x, y, z))
+ return;
+
+ owner.addUnitState(UNIT_STAT_FLEEING);
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+{
+ if(!&owner)
+ return false;
+
+ x = owner.GetPositionX();
+ y = owner.GetPositionY();
+ z = owner.GetPositionZ();
+
+ float temp_x, temp_y, angle;
+ const Map * _map = MapManager::Instance().GetBaseMap(owner.GetMapId());
+ //primitive path-finding
+ for(uint8 i = 0; i < 18; i++)
+ {
+ if(i_only_forward && i > 2)
+ break;
+
+ float distance = 5.0f;
+
+ switch(i)
+ {
+ case 0:
+ angle = i_cur_angle;
+ break;
+ case 1:
+ angle = i_cur_angle;
+ distance /= 2;
+ break;
+ case 2:
+ angle = i_cur_angle;
+ distance /= 4;
+ break;
+ case 3:
+ angle = i_cur_angle + M_PI/4.0f;
+ break;
+ case 4:
+ angle = i_cur_angle - M_PI/4.0f;
+ break;
+ case 5:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 6:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 7:
+ angle = i_cur_angle + M_PI/2.0f;
+ break;
+ case 8:
+ angle = i_cur_angle - M_PI/2.0f;
+ break;
+ case 9:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 10:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 11:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 12:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 13:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 14:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 15:
+ angle = i_cur_angle + M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 16:
+ angle = i_cur_angle - M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 17:
+ angle = i_cur_angle + M_PI;
+ distance /= 2;
+ break;
+ }
+ temp_x = x + distance * cos(angle);
+ temp_y = y + distance * sin(angle);
+ MaNGOS::NormalizeMapCoord(temp_x);
+ MaNGOS::NormalizeMapCoord(temp_y);
+ if( owner.IsWithinLOS(temp_x,temp_y,z))
+ {
+ bool is_water_now = _map->IsInWater(x,y,z);
+
+ if(is_water_now && _map->IsInWater(temp_x,temp_y,z))
+ {
+ x = temp_x;
+ y = temp_y;
+ return true;
+ }
+ float new_z = _map->GetHeight(temp_x,temp_y,z,true);
+
+ if(new_z <= INVALID_HEIGHT)
+ continue;
+
+ bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z);
+
+ if((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
+ continue;
+
+ if( !(new_z - z) || distance / fabs(new_z - z) > 1.0f)
+ {
+ float new_z_left = _map->GetHeight(temp_x + 1.0f*cos(angle+M_PI/2),temp_y + 1.0f*sin(angle+M_PI/2),z,true);
+ float new_z_right = _map->GetHeight(temp_x + 1.0f*cos(angle-M_PI/2),temp_y + 1.0f*sin(angle-M_PI/2),z,true);
+ if(fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
+ {
+ x = temp_x;
+ y = temp_y;
+ z = new_z;
+ return true;
+ }
+ }
+ }
+ }
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset( urand(500,1000) );
+ return false;
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_setMoveData(T &owner)
+{
+ float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
+
+ if(i_to_distance_from_caster > 0.0f)
+ {
+ if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
+ // if we reach lower distance
+ (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
+ // if we can't be close
+ (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
+ // if we reach bigger distance
+ (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
+ (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE) )
+ // if we leave 'quiet zone'
+ {
+ // we are very far or too close, stopping
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset( urand(500,1000) );
+ return false;
+ }
+ else
+ {
+ // now we are running, continue
+ i_last_distance_from_caster = cur_dist_xyz;
+ return true;
+ }
+ }
+
+ float cur_dist;
+ float angle_to_caster;
+
+ Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+
+ if(fright)
+ {
+ cur_dist = fright->GetDistance(&owner);
+ if(cur_dist < cur_dist_xyz)
+ {
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ angle_to_caster = fright->GetAngle(&owner);
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+
+ // if we too close may use 'path-finding' else just stop
+ i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
+
+ //get angle and 'distance from caster' to run
+ float angle;
+
+ if(i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ {
+ angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE;
+ i_only_forward = true;
+ }
+ else if(cur_dist < MIN_QUIET_DISTANCE)
+ {
+ angle = M_PI/6 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ }
+ else if(cur_dist > MAX_QUIET_DISTANCE)
+ {
+ angle = rand_norm()*M_PI/3 + M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+ else
+ {
+ angle = rand_norm()*M_PI;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+
+ int8 sign = rand_norm() > 0.5f ? 1 : -1;
+ i_cur_angle = sign*angle + angle_to_caster;
+
+ // current distance
+ i_last_distance_from_caster = cur_dist;
+
+ return true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+
+ Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+ if(!fright)
+ return;
+
+ _Init(owner);
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ i_only_forward = true;
+ i_cur_angle = 0.0f;
+ i_last_distance_from_caster = 0.0f;
+ i_to_distance_from_caster = 0.0f;
+ _setTargetLocation(owner);
+}
+
+template<>
+void
+FleeingMovementGenerator<Creature>::_Init(Creature &owner)
+{
+ if(!&owner)
+ return;
+ owner.SetUInt64Value(UNIT_FIELD_TARGET, 0);
+ is_water_ok = owner.canSwim();
+ is_land_ok = owner.canWalk();
+}
+
+template<>
+void
+FleeingMovementGenerator<Player>::_Init(Player &)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_FLEEING);
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if( !&owner || !owner.isAlive() )
+ return false;
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) )
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ i_nextCheckTime.Update(time_diff);
+
+ if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() )
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
+ {
+ i_destinationHolder.ResetUpdate(50);
+ if(i_nextCheckTime.Passed() && i_destinationHolder.HasArrived())
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+ }
+ return true;
+}
+
+template void FleeingMovementGenerator<Player>::Initialize(Player &);
+template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
+template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
+template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
+template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
+template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void FleeingMovementGenerator<Player>::Finalize(Player &);
+template void FleeingMovementGenerator<Creature>::Finalize(Creature &);
+template void FleeingMovementGenerator<Player>::Reset(Player &);
+template void FleeingMovementGenerator<Creature>::Reset(Creature &);
+template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
diff --git a/src/game/FleeingMovementGenerator.h b/src/game/FleeingMovementGenerator.h
new file mode 100644
index 00000000000..f0b48e27b23
--- /dev/null
+++ b/src/game/FleeingMovementGenerator.h
@@ -0,0 +1,62 @@
+/*
+* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+*
+* 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
+*/
+
+#ifndef MANGOS_FLEEINGMOVEMENTGENERATOR_H
+#define MANGOS_FLEEINGMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+#include "MapManager.h"
+
+template<class T>
+class MANGOS_DLL_SPEC FleeingMovementGenerator
+: public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
+{
+ public:
+ FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+
+ MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
+
+ private:
+ void _setTargetLocation(T &owner);
+ bool _getPoint(T &owner, float &x, float &y, float &z);
+ bool _setMoveData(T &owner);
+ void _Init(T &);
+
+ bool is_water_ok :1;
+ bool is_land_ok :1;
+ bool i_only_forward:1;
+
+ float i_caster_x;
+ float i_caster_y;
+ float i_caster_z;
+ float i_last_distance_from_caster;
+ float i_to_distance_from_caster;
+ float i_cur_angle;
+ TimeTracker i_nextCheckTime;
+ uint64 i_frightGUID;
+
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+};
+#endif
diff --git a/src/game/FollowerRefManager.h b/src/game/FollowerRefManager.h
new file mode 100644
index 00000000000..460a3612ded
--- /dev/null
+++ b/src/game/FollowerRefManager.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _FOLLOWERREFMANAGER
+#define _FOLLOWERREFMANAGER
+
+#include "Utilities/LinkedReference/RefManager.h"
+
+class Unit;
+class TargetedMovementGeneratorBase;
+
+class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase>
+{
+
+};
+#endif
diff --git a/src/game/FollowerReference.cpp b/src/game/FollowerReference.cpp
new file mode 100644
index 00000000000..2ebea8187f5
--- /dev/null
+++ b/src/game/FollowerReference.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Unit.h"
+#include "TargetedMovementGenerator.h"
+#include "FollowerReference.h"
+
+void FollowerReference::targetObjectBuildLink()
+{
+ getTarget()->addFollower(this);
+}
+
+void FollowerReference::targetObjectDestroyLink()
+{
+ getTarget()->removeFollower(this);
+}
+
+void FollowerReference::sourceObjectDestroyLink()
+{
+ getSource()->stopFollowing();
+}
diff --git a/src/game/FollowerReference.h b/src/game/FollowerReference.h
new file mode 100644
index 00000000000..3a79d8cedb0
--- /dev/null
+++ b/src/game/FollowerReference.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _FOLLOWERREFERENCE_H
+#define _FOLLOWERREFERENCE_H
+
+#include "Utilities/LinkedReference/Reference.h"
+
+class TargetedMovementGeneratorBase;
+class Unit;
+
+class MANGOS_DLL_SPEC FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
+{
+ protected:
+ void targetObjectBuildLink();
+ void targetObjectDestroyLink();
+ void sourceObjectDestroyLink();
+};
+#endif
diff --git a/src/game/Formulas.h b/src/game/Formulas.h
new file mode 100644
index 00000000000..a7ef3200e73
--- /dev/null
+++ b/src/game/Formulas.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_FORMULAS_H
+#define MANGOS_FORMULAS_H
+
+#include "World.h"
+
+namespace MaNGOS
+{
+ namespace XP
+ {
+ typedef enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY };
+
+ inline uint32 GetGrayLevel(uint32 pl_level)
+ {
+ if( pl_level <= 5 )
+ return 0;
+ else if( pl_level <= 39 )
+ return pl_level - 5 - pl_level/10;
+ else if( pl_level <= 59 )
+ return pl_level - 1 - pl_level/5;
+ else
+ return pl_level - 9;
+ }
+
+ inline XPColorChar GetColorCode(uint32 pl_level, uint32 mob_level)
+ {
+ if( mob_level >= pl_level + 5 )
+ return RED;
+ else if( mob_level >= pl_level + 3 )
+ return ORANGE;
+ else if( mob_level >= pl_level - 2 )
+ return YELLOW;
+ else if( mob_level > GetGrayLevel(pl_level) )
+ return GREEN;
+ else
+ return GRAY;
+ }
+
+ inline uint32 GetZeroDifference(uint32 pl_level)
+ {
+ if( pl_level < 8 ) return 5;
+ if( pl_level < 10 ) return 6;
+ if( pl_level < 12 ) return 7;
+ if( pl_level < 16 ) return 8;
+ if( pl_level < 20 ) return 9;
+ if( pl_level < 30 ) return 11;
+ if( pl_level < 40 ) return 12;
+ if( pl_level < 45 ) return 13;
+ if( pl_level < 50 ) return 14;
+ if( pl_level < 55 ) return 15;
+ if( pl_level < 60 ) return 16;
+ return 17;
+ }
+
+ inline uint32 BaseGain(uint32 pl_level, uint32 mob_level, ContentLevels content)
+ {
+ const uint32 nBaseExp = content == CONTENT_1_60 ? 45 : 235;
+ if( mob_level >= pl_level )
+ {
+ uint32 nLevelDiff = mob_level - pl_level;
+ if (nLevelDiff > 4)
+ nLevelDiff = 4;
+ return ((pl_level*5 + nBaseExp) * (20 + nLevelDiff)/10 + 1)/2;
+ }
+ else
+ {
+ uint32 gray_level = GetGrayLevel(pl_level);
+ if( mob_level > gray_level )
+ {
+ uint32 ZD = GetZeroDifference(pl_level);
+ return (pl_level*5 + nBaseExp) * (ZD + mob_level - pl_level)/ZD;
+ }
+ return 0;
+ }
+ }
+
+ inline uint32 Gain(Player *pl, Unit *u)
+ {
+ if(u->GetTypeId()==TYPEID_UNIT && (
+ ((Creature*)u)->isTotem() || ((Creature*)u)->isPet() ||
+ (((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()));
+ if( xp_gain == 0 )
+ return 0;
+
+ if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isElite())
+ xp_gain *= 2;
+
+ return (uint32)(xp_gain*sWorld.getRate(RATE_XP_KILL));
+ }
+
+ inline uint32 xp_Diff(uint32 lvl)
+ {
+ if( lvl < 29 )
+ return 0;
+ if( lvl == 29 )
+ return 1;
+ if( lvl == 30 )
+ return 3;
+ if( lvl == 31 )
+ return 6;
+ else
+ return (5*(lvl-30));
+ }
+
+ inline uint32 mxp(uint32 lvl)
+ {
+ if (lvl < 60)
+ {
+ return (45 + (5*lvl));
+ }
+ else
+ {
+ return (235 + (5*lvl));
+ }
+ }
+
+ inline uint32 xp_to_level(uint32 lvl)
+ {
+ uint32 xp = 0;
+ if (lvl < 60)
+ {
+ xp = (8*lvl + xp_Diff(lvl)) * mxp(lvl);
+ }
+ else if (lvl == 60)
+ {
+ xp = (155 + mxp(lvl) * (1344 - 70 - ((69 - lvl) * (7 + (69 - lvl) * 8 - 1)/2)));
+ }
+ else if (lvl < 70)
+ {
+ xp = (155 + mxp(lvl) * (1344 - ((69-lvl) * (7 + (69 - lvl) * 8 - 1)/2)));
+ }else
+ {
+ // level higher than 70 is not supported
+ xp = (uint32)(779700 * (pow(sWorld.getRate(RATE_XP_PAST_70), (int32)lvl - 69)));
+ return ((xp < 0x7fffffff) ? xp : 0x7fffffff);
+ }
+
+ // The XP to Level is always rounded to the nearest 100 points (50 rounded to high).
+ xp = ((xp + 50) / 100) * 100; // use additional () for prevent free association operations in C++
+
+ if ((lvl > 10) && (lvl < 60)) // compute discount added in 2.3.x
+ {
+ uint32 discount = (lvl < 28) ? (lvl - 10) : 18;
+ xp = (xp * (100 - discount)) / 100; // apply discount
+ xp = (xp / 100) * 100; // floor to hundreds
+ }
+
+ return xp;
+ }
+
+ inline float xp_in_group_rate(uint32 count, bool isRaid)
+ {
+ if(isRaid)
+ {
+ // FIX ME: must apply decrease modifiers dependent from raid size
+ return 1.0f;
+ }
+ else
+ {
+ switch(count)
+ {
+ case 0:
+ case 1:
+ case 2:
+ return 1.0f;
+ case 3:
+ return 1.166f;
+ case 4:
+ return 1.3f;
+ case 5:
+ default:
+ return 1.4f;
+ }
+ }
+ }
+ }
+}
+#endif
diff --git a/src/game/GameEvent.cpp b/src/game/GameEvent.cpp
new file mode 100644
index 00000000000..e36cb6caae8
--- /dev/null
+++ b/src/game/GameEvent.cpp
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "GameEvent.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "Language.h"
+#include "Log.h"
+#include "MapManager.h"
+#include "Policies/SingletonImp.h"
+
+INSTANTIATE_SINGLETON_1(GameEvent);
+
+bool GameEvent::CheckOneGameEvent(uint16 entry) const
+{
+ // Get the event information
+ time_t currenttime = time(NULL);
+ if( mGameEvent[entry].start < currenttime && currenttime < mGameEvent[entry].end &&
+ ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)) < (mGameEvent[entry].length * MINUTE) )
+ return true;
+ else
+ return false;
+}
+
+uint32 GameEvent::NextCheck(uint16 entry) const
+{
+ time_t currenttime = time(NULL);
+
+ // outdated event: we return max
+ if (currenttime > mGameEvent[entry].end)
+ return max_ge_check_delay;
+
+ // never started event, we return delay before start
+ if (mGameEvent[entry].start > currenttime)
+ return (mGameEvent[entry].start - currenttime);
+
+ uint32 delay;
+ // in event, we return the end of it
+ if ((((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * 60)) < (mGameEvent[entry].length * 60)))
+ // we return the delay before it ends
+ delay = (mGameEvent[entry].length * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
+ else // not in window, we return the delay before next start
+ delay = (mGameEvent[entry].occurence * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE));
+ // In case the end is before next check
+ if (mGameEvent[entry].end < time_t(currenttime + delay))
+ return (mGameEvent[entry].end - currenttime);
+ else
+ return delay;
+}
+
+void GameEvent::StartEvent( uint16 event_id, bool overwrite )
+{
+ AddActiveEvent(event_id);
+ ApplyNewEvent(event_id);
+ if(overwrite)
+ {
+ mGameEvent[event_id].start = time(NULL);
+ if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
+ mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
+ }
+}
+
+void GameEvent::StopEvent( uint16 event_id, bool overwrite )
+{
+ RemoveActiveEvent(event_id);
+ UnApplyEvent(event_id);
+ if(overwrite)
+ {
+ mGameEvent[event_id].start = time(NULL) - mGameEvent[event_id].length * MINUTE;
+ if(mGameEvent[event_id].end <= mGameEvent[event_id].start)
+ mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length;
+ }
+}
+
+void GameEvent::LoadFromDB()
+{
+ {
+ QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event");
+ if( !result )
+ {
+ sLog.outString(">> Table game_event is empty.");
+ sLog.outString();
+ return;
+ }
+
+ Field *fields = result->Fetch();
+
+ uint32 max_event_id = fields[0].GetUInt16();
+ delete result;
+
+ mGameEvent.resize(max_event_id+1);
+ }
+
+ QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,description FROM game_event");
+ if( !result )
+ {
+ mGameEvent.clear();
+ sLog.outString(">> Table game_event is empty:");
+ sLog.outString();
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ ++count;
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint16 event_id = fields[0].GetUInt16();
+ if(event_id==0)
+ {
+ sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id);
+ continue;
+ }
+
+ GameEventData& pGameEvent = mGameEvent[event_id];
+ uint64 starttime = fields[1].GetUInt64();
+ pGameEvent.start = time_t(starttime);
+ uint64 endtime = fields[2].GetUInt64();
+ pGameEvent.end = time_t(endtime);
+ pGameEvent.occurence = fields[3].GetUInt32();
+ pGameEvent.length = fields[4].GetUInt32();
+
+ if(pGameEvent.length==0) // length>0 is validity check
+ {
+ sLog.outErrorDb("`game_event` game event id (%i) have length 0 and can't be used.",event_id);
+ continue;
+ }
+
+ pGameEvent.description = fields[5].GetCppString();
+
+ } while( result->NextRow() );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u game events", count );
+ delete result;
+
+ mGameEventCreatureGuids.resize(mGameEvent.size()*2-1);
+ // 1 2
+ result = WorldDatabase.Query("SELECT creature.guid, game_event_creature.event "
+ "FROM creature JOIN game_event_creature ON creature.guid = game_event_creature.guid");
+
+ count = 0;
+ if( !result )
+ {
+ barGoLink bar2(1);
+ bar2.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u creatures in game events", count );
+ }
+ else
+ {
+
+ barGoLink bar2( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar2.step();
+
+ uint32 guid = fields[0].GetUInt32();
+ int16 event_id = fields[1].GetInt16();
+
+ int32 internal_event_id = mGameEvent.size() + event_id - 1;
+
+ if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
+ {
+ sLog.outErrorDb("`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
+ continue;
+ }
+
+ ++count;
+ GuidList& crelist = mGameEventCreatureGuids[internal_event_id];
+ crelist.push_back(guid);
+
+ } while( result->NextRow() );
+ sLog.outString();
+ sLog.outString( ">> Loaded %u creatures in game events", count );
+ delete result;
+ }
+
+ mGameEventGameobjectGuids.resize(mGameEvent.size()*2-1);
+ // 1 2
+ result = WorldDatabase.Query("SELECT gameobject.guid, game_event_gameobject.event "
+ "FROM gameobject JOIN game_event_gameobject ON gameobject.guid=game_event_gameobject.guid");
+
+ count = 0;
+ if( !result )
+ {
+ barGoLink bar3(1);
+ bar3.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u gameobjects in game events", count );
+ }
+ else
+ {
+
+ barGoLink bar3( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar3.step();
+
+ uint32 guid = fields[0].GetUInt32();
+ int16 event_id = fields[1].GetInt16();
+
+ int32 internal_event_id = mGameEvent.size() + event_id - 1;
+
+ if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
+ {
+ sLog.outErrorDb("`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
+ continue;
+ }
+
+ ++count;
+ GuidList& golist = mGameEventGameobjectGuids[internal_event_id];
+ golist.push_back(guid);
+
+ } while( result->NextRow() );
+ sLog.outString();
+ sLog.outString( ">> Loaded %u gameobjects in game events", count );
+
+ delete result;
+ }
+
+ mGameEventModelEquip.resize(mGameEvent.size());
+ // 0 1 2
+ result = WorldDatabase.Query("SELECT creature.guid, game_event_model_equip.event, game_event_model_equip.modelid,"
+ // 3
+ "game_event_model_equip.equipment_id "
+ "FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid");
+
+ count = 0;
+ if( !result )
+ {
+ barGoLink bar3(1);
+ bar3.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u model/equipment changes in game events", count );
+ }
+ else
+ {
+
+ barGoLink bar3( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar3.step();
+ uint32 guid = fields[0].GetUInt32();
+ uint16 event_id = fields[1].GetUInt16();
+
+ if(event_id >= mGameEventModelEquip.size())
+ {
+ sLog.outErrorDb("`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
+ continue;
+ }
+
+ ++count;
+ ModelEquipList& equiplist = mGameEventModelEquip[event_id];
+ ModelEquip newModelEquipSet;
+ newModelEquipSet.modelid = fields[2].GetUInt32();
+ newModelEquipSet.equipment_id = fields[3].GetUInt32();
+ newModelEquipSet.equipement_id_prev = 0;
+ newModelEquipSet.modelid_prev = 0;
+
+ if(newModelEquipSet.equipment_id > 0)
+ {
+ if(!objmgr.GetEquipmentInfo(newModelEquipSet.equipment_id))
+ {
+ sLog.outErrorDb("Table `game_event_model_equip` have creature (Guid: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", guid, newModelEquipSet.equipment_id);
+ continue;
+ }
+ }
+
+ equiplist.push_back(std::pair<uint32, ModelEquip>(guid, newModelEquipSet));
+
+ } while( result->NextRow() );
+ sLog.outString();
+ sLog.outString( ">> Loaded %u model/equipment changes in game events", count );
+
+ delete result;
+ }
+
+ mGameEventQuests.resize(mGameEvent.size());
+ // 0 1 2
+ result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_creature_quest");
+
+ count = 0;
+ if( !result )
+ {
+ barGoLink bar3(1);
+ bar3.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u quests additions in game events", count );
+ }
+ else
+ {
+
+ barGoLink bar3( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar3.step();
+ uint32 id = fields[0].GetUInt32();
+ uint32 quest = fields[1].GetUInt32();
+ uint16 event_id = fields[2].GetUInt16();
+
+ if(event_id >= mGameEventQuests.size())
+ {
+ sLog.outErrorDb("`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
+ continue;
+ }
+
+ ++count;
+ QuestRelList& questlist = mGameEventQuests[event_id];
+ questlist.push_back(QuestRelation(id, quest));
+
+ } while( result->NextRow() );
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quests additions in game events", count );
+
+ delete result;
+ }
+}
+
+uint32 GameEvent::Initialize() // return the next event delay in ms
+{
+ m_ActiveEvents.clear();
+ uint32 delay = Update();
+ sLog.outBasic("Game Event system initialized." );
+ isSystemInit = true;
+ return delay;
+}
+
+uint32 GameEvent::Update() // return the next event delay in ms
+{
+ uint32 nextEventDelay = max_ge_check_delay; // 1 day
+ uint32 calcDelay;
+ for (uint16 itr = 1; itr < mGameEvent.size(); itr++)
+ {
+ //sLog.outErrorDb("Checking event %u",itr);
+ if (CheckOneGameEvent(itr))
+ {
+ //sLog.outDebug("GameEvent %u is active",itr->first);
+ if (!IsActiveEvent(itr))
+ StartEvent(itr);
+ }
+ else
+ {
+ //sLog.outDebug("GameEvent %u is not active",itr->first);
+ if (IsActiveEvent(itr))
+ StopEvent(itr);
+ else
+ {
+ if (!isSystemInit)
+ {
+ int16 event_nid = (-1) * (itr);
+ // spawn all negative ones for this event
+ GameEventSpawn(event_nid);
+ }
+ }
+ }
+ calcDelay = NextCheck(itr);
+ if (calcDelay < nextEventDelay)
+ nextEventDelay = calcDelay;
+ }
+ sLog.outBasic("Next game event check in %u seconds.", nextEventDelay + 1);
+ return (nextEventDelay + 1) * 1000; // Add 1 second to be sure event has started/stopped at next call
+}
+
+void GameEvent::UnApplyEvent(uint16 event_id)
+{
+ sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str());
+ // un-spawn positive event tagged objects
+ GameEventUnspawn(event_id);
+ // spawn negative event tagget objects
+ int16 event_nid = (-1) * event_id;
+ GameEventSpawn(event_nid);
+ // restore equipment or model
+ ChangeEquipOrModel(event_id, false);
+ // Remove quests that are events only to non event npc
+ UpdateEventQuests(event_id, false);
+}
+
+void GameEvent::ApplyNewEvent(uint16 event_id)
+{
+ switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE))
+ {
+ case 0: // disable
+ break;
+ case 1: // announce events
+ sWorld.SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str());
+ break;
+ }
+
+ sLog.outString("GameEvent %u \"%s\" started.", event_id, mGameEvent[event_id].description.c_str());
+ // spawn positive event tagget objects
+ GameEventSpawn(event_id);
+ // un-spawn negative event tagged objects
+ int16 event_nid = (-1) * event_id;
+ GameEventUnspawn(event_nid);
+ // Change equipement or model
+ ChangeEquipOrModel(event_id, true);
+ // Add quests that are events only to non event npc
+ UpdateEventQuests(event_id, true);
+}
+
+void GameEvent::GameEventSpawn(int16 event_id)
+{
+ int32 internal_event_id = mGameEvent.size() + event_id - 1;
+
+ if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
+ {
+ sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
+ return;
+ }
+
+ for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
+ {
+ // Add to correct cell
+ CreatureData const* data = objmgr.GetCreatureData(*itr);
+ if (data)
+ {
+ objmgr.AddCreatureToGrid(*itr, data);
+
+ // Spawn if necessary (loaded grids only)
+ Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
+ // We use spawn coords to spawn
+ if(!map->Instanceable() && !map->IsRemovalGrid(data->posX,data->posY))
+ {
+ Creature* pCreature = new Creature;
+ //sLog.outDebug("Spawning creature %u",*itr);
+ if (!pCreature->LoadFromDB(*itr, map))
+ {
+ delete pCreature;
+ }
+ else
+ {
+ map->Add(pCreature);
+ }
+ }
+ }
+ }
+
+ if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
+ {
+ sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
+ return;
+ }
+
+ for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
+ {
+ // Add to correct cell
+ GameObjectData const* data = objmgr.GetGOData(*itr);
+ if (data)
+ {
+ objmgr.AddGameobjectToGrid(*itr, data);
+ // Spawn if necessary (loaded grids only)
+ // this base map checked as non-instanced and then only existed
+ Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid));
+ // We use current coords to unspawn, not spawn coords since creature can have changed grid
+ if(!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY))
+ {
+ GameObject* pGameobject = new GameObject;
+ //sLog.outDebug("Spawning gameobject %u", *itr);
+ if (!pGameobject->LoadFromDB(*itr, map))
+ {
+ delete pGameobject;
+ }
+ else
+ {
+ if(pGameobject->isSpawnedByDefault())
+ map->Add(pGameobject);
+ }
+ }
+ }
+ }
+}
+
+void GameEvent::GameEventUnspawn(int16 event_id)
+{
+ int32 internal_event_id = mGameEvent.size() + event_id - 1;
+
+ if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size())
+ {
+ sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size());
+ return;
+ }
+
+ for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
+ {
+ // Remove the creature from grid
+ if( CreatureData const* data = objmgr.GetCreatureData(*itr) )
+ {
+ 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("GameEvent::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size());
+ return;
+ }
+
+ for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
+ {
+ // Remove the gameobject from grid
+ if(GameObjectData const* data = objmgr.GetGOData(*itr))
+ {
+ objmgr.RemoveGameobjectFromGrid(*itr, data);
+
+ if( GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL) )
+ pGameobject->AddObjectToRemoveList();
+ }
+ }
+}
+
+void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate)
+{
+ for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr)
+ {
+ // Remove the creature from grid
+ CreatureData const* data = objmgr.GetCreatureData(itr->first);
+ if(!data)
+ continue;
+
+ // Update if spawned
+ Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(itr->first, data->id,HIGHGUID_UNIT), (Creature*)NULL);
+ if (pCreature)
+ {
+ if (activate)
+ {
+ itr->second.equipement_id_prev = pCreature->GetCurrentEquipmentId();
+ itr->second.modelid_prev = pCreature->GetDisplayId();
+ pCreature->LoadEquipment(itr->second.equipment_id, true);
+ if (itr->second.modelid >0 && itr->second.modelid_prev != itr->second.modelid)
+ {
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid);
+ if (minfo)
+ {
+ pCreature->SetDisplayId(itr->second.modelid);
+ pCreature->SetNativeDisplayId(itr->second.modelid);
+ pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
+ pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
+ }
+ }
+ }
+ else
+ {
+ pCreature->LoadEquipment(itr->second.equipement_id_prev, true);
+ if (itr->second.modelid_prev >0 && itr->second.modelid_prev != itr->second.modelid)
+ {
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid_prev);
+ if (minfo)
+ {
+ pCreature->SetDisplayId(itr->second.modelid_prev);
+ pCreature->SetNativeDisplayId(itr->second.modelid_prev);
+ pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
+ pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
+ }
+ }
+ }
+ }
+ else // If not spawned
+ {
+ CreatureData const* data = objmgr.GetCreatureData(itr->first);
+ if (data && activate)
+ {
+ CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(data->id);
+ uint32 display_id = objmgr.ChooseDisplayId(0,cinfo,data);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (minfo)
+ display_id = minfo->modelid;
+ if (data->equipmentId == 0)
+ itr->second.equipement_id_prev = cinfo->equipmentId;
+ else if (data->equipmentId != -1)
+ itr->second.equipement_id_prev = data->equipmentId;
+ itr->second.modelid_prev = display_id;
+ }
+ }
+ // now last step: put in data
+ // just to have write access to it
+ CreatureData& data2 = objmgr.NewOrExistCreatureData(itr->first);
+ if (activate)
+ {
+ data2.displayid = itr->second.modelid;
+ data2.equipmentId = itr->second.equipment_id;
+ }
+ else
+ {
+ data2.displayid = itr->second.modelid_prev;
+ data2.equipmentId = itr->second.equipement_id_prev;
+ }
+ }
+}
+
+void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate)
+{
+ QuestRelList::iterator itr;
+ for (itr = mGameEventQuests[event_id].begin();itr != mGameEventQuests[event_id].end();++itr)
+ {
+ QuestRelations &CreatureQuestMap = objmgr.mCreatureQuestRelations;
+ if (Activate) // Add the pair(id,quest) to the multimap
+ CreatureQuestMap.insert(QuestRelations::value_type(itr->first, itr->second));
+ else
+ { // Remove the pair(id,quest) from the multimap
+ QuestRelations::iterator qitr = CreatureQuestMap.find(itr->first);
+ if (qitr == CreatureQuestMap.end())
+ continue;
+ QuestRelations::iterator lastElement = CreatureQuestMap.upper_bound(itr->first);
+ for ( ;qitr != lastElement;++qitr)
+ {
+ if (qitr->second == itr->second)
+ {
+ CreatureQuestMap.erase(qitr); // iterator is now no more valid
+ break; // but we can exit loop since the element is found
+ }
+ }
+ }
+ }
+}
+
+GameEvent::GameEvent()
+{
+ isSystemInit = false;
+}
diff --git a/src/game/GameEvent.h b/src/game/GameEvent.h
new file mode 100644
index 00000000000..535422eb407
--- /dev/null
+++ b/src/game/GameEvent.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GAMEEVENT_H
+#define MANGOS_GAMEEVENT_H
+
+#include "Platform/Define.h"
+#include "Creature.h"
+#include "GameObject.h"
+
+#define max_ge_check_delay 86400 // 1 day in seconds
+
+struct GameEventData
+{
+ GameEventData() : start(1),end(0),occurence(0),length(0) {}
+ time_t start;
+ time_t end;
+ uint32 occurence;
+ uint32 length;
+ std::string description;
+
+ bool isValid() const { return length > 0; }
+};
+
+struct ModelEquip
+{
+ uint32 modelid;
+ uint32 equipment_id;
+ uint32 modelid_prev;
+ uint32 equipement_id_prev;
+};
+
+class GameEvent
+{
+ public:
+ GameEvent();
+ ~GameEvent() {};
+ typedef std::set<uint16> ActiveEvents;
+ typedef std::vector<GameEventData> GameEventDataMap;
+ ActiveEvents const& GetActiveEventList() const { return m_ActiveEvents; }
+ GameEventDataMap const& GetEventMap() const { return mGameEvent; }
+ bool CheckOneGameEvent(uint16 entry) const;
+ uint32 NextCheck(uint16 entry) const;
+ void LoadFromDB();
+ uint32 Update();
+ bool IsActiveEvent(uint16 event_id) { return ( m_ActiveEvents.find(event_id)!=m_ActiveEvents.end()); }
+ uint32 Initialize();
+ void StartEvent(uint16 event_id, bool overwrite = false);
+ void StopEvent(uint16 event_id, bool overwrite = false);
+ private:
+ void AddActiveEvent(uint16 event_id) { m_ActiveEvents.insert(event_id); }
+ void RemoveActiveEvent(uint16 event_id) { m_ActiveEvents.erase(event_id); }
+ void ApplyNewEvent(uint16 event_id);
+ void UnApplyEvent(uint16 event_id);
+ void GameEventSpawn(int16 event_id);
+ void GameEventUnspawn(int16 event_id);
+ void ChangeEquipOrModel(int16 event_id, bool activate);
+ void UpdateEventQuests(uint16 event_id, bool Activate);
+ protected:
+ typedef std::list<uint32> GuidList;
+ typedef std::vector<GuidList> GameEventGuidMap;
+ typedef std::pair<uint32, ModelEquip> ModelEquipPair;
+ typedef std::list<ModelEquipPair> ModelEquipList;
+ typedef std::vector<ModelEquipList> GameEventModelEquipMap;
+ typedef std::pair<uint32, uint32> QuestRelation;
+ typedef std::list<QuestRelation> QuestRelList;
+ typedef std::vector<QuestRelList> GameEventQuestMap;
+ GameEventQuestMap mGameEventQuests;
+ GameEventModelEquipMap mGameEventModelEquip;
+ GameEventGuidMap mGameEventCreatureGuids;
+ GameEventGuidMap mGameEventGameobjectGuids;
+ GameEventDataMap mGameEvent;
+ ActiveEvents m_ActiveEvents;
+ bool isSystemInit;
+};
+
+#define gameeventmgr MaNGOS::Singleton<GameEvent>::Instance()
+#endif
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp
new file mode 100644
index 00000000000..c3b8cf3d6e4
--- /dev/null
+++ b/src/game/GameObject.cpp
@@ -0,0 +1,1253 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "QuestDef.h"
+#include "GameObject.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Spell.h"
+#include "UpdateMask.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Database/DatabaseEnv.h"
+#include "MapManager.h"
+#include "LootMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "InstanceData.h"
+#include "BattleGround.h"
+#include "Util.h"
+
+GameObject::GameObject() : WorldObject()
+{
+ m_objectType |= TYPEMASK_GAMEOBJECT;
+ m_objectTypeId = TYPEID_GAMEOBJECT;
+ // 2.3.2 - 0x58
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+
+ m_valuesCount = GAMEOBJECT_END;
+ m_respawnTime = 0;
+ m_respawnDelayTime = 25;
+ m_lootState = GO_NOT_READY;
+ m_spawnedByDefault = true;
+ m_usetimes = 0;
+ m_spellId = 0;
+ m_charges = 5;
+ m_cooldownTime = 0;
+ m_goInfo = NULL;
+}
+
+GameObject::~GameObject()
+{
+ if(m_uint32Values) // field array can be not exist if GameOBject not loaded
+ {
+ // crash possable at access to deleted GO in Unit::m_gameobj
+ uint64 owner_guid = GetOwnerGUID();
+ if(owner_guid)
+ {
+ Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid);
+ if(owner)
+ 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 possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid));
+ }
+ }
+}
+
+void GameObject::AddToWorld()
+{
+ ///- Register the gameobject for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Object::AddToWorld();
+}
+
+void GameObject::RemoveFromWorld()
+{
+ ///- Remove the gameobject from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Object::RemoveFromWorld();
+}
+
+bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state)
+{
+ Relocate(x,y,z,ang);
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
+ return false;
+ }
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
+ return false;
+ }
+
+ Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
+
+ m_DBTableGuid = guidlow;
+ m_goInfo = goinfo;
+
+ if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type);
+ return false;
+ }
+
+ SetFloatValue(GAMEOBJECT_POS_X, x);
+ SetFloatValue(GAMEOBJECT_POS_Y, y);
+ SetFloatValue(GAMEOBJECT_POS_Z, z);
+ SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle
+
+ SetFloatValue (GAMEOBJECT_ROTATION, rotation0);
+ SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1);
+ SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2);
+ SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
+ SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
+ SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
+
+ SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
+ SetGoState(go_state);
+ SetGoType(GameobjectTypes(goinfo->type));
+
+ SetGoAnimProgress(animprogress);
+
+ // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
+ if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER)
+ m_charges = goinfo->spellcaster.charges;
+
+ //Notify the map's instance data.
+ //Only works if you create the object in it, not if it is moves to that map.
+ //Normally non-players do not teleport to other maps.
+ if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
+ {
+ ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
+ }
+
+ return true;
+}
+
+void GameObject::Update(uint32 /*p_time*/)
+{
+ if (IS_MO_TRANSPORT(GetGUID()))
+ {
+ //((Transport*)this)->Update(p_time);
+ return;
+ }
+
+ switch (m_lootState)
+ {
+ case GO_NOT_READY:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_TRAP:
+ {
+ // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
+ Unit* owner = GetOwner();
+ if (owner && ((Player*)owner)->isInCombat())
+ m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
+ m_lootState = GO_READY;
+ break;
+ }
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ {
+ // fishing code (bobber ready)
+ if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
+ {
+ // splash bobber (bobber ready now)
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ SetGoState(0);
+ SetUInt32Value(GAMEOBJECT_FLAGS, 32);
+
+ UpdateData udata;
+ WorldPacket packet;
+ BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
+ udata.BuildPacket(&packet);
+ ((Player*)caster)->GetSession()->SendPacket(&packet);
+
+ WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
+ data << GetGUID();
+ data << (uint32)(0);
+ ((Player*)caster)->SendMessageToSet(&data,true);
+ }
+
+ m_lootState = GO_READY; // can be succesfully open with some chance
+ }
+ return;
+ }
+ default:
+ m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
+ break;
+ }
+ // NO BREAK for switch (m_lootState)
+ }
+ case GO_READY:
+ {
+ if (m_respawnTime > 0) // timer on
+ {
+ if (m_respawnTime <= time(NULL)) // timer expired
+ {
+ m_respawnTime = 0;
+ m_SkillupList.clear();
+ m_usetimes = 0;
+
+ switch (GetGoType())
+ {
+ case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
+ {
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false);
+ }
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ // can be delete
+ m_lootState = GO_JUST_DEACTIVATED;
+ return;
+ }
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
+ if( !GetGoState() )
+ SwitchDoorOrButton(false);
+ //flags in AB are type_button and we need to add them here so no break!
+ default:
+ if(!m_spawnedByDefault) // despawn timer
+ {
+ // can be despawned or destroyed
+ SetLootState(GO_JUST_DEACTIVATED);
+ return;
+ }
+ // respawn timer
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+ break;
+ }
+ }
+ }
+
+ // traps can have time and can not have
+ GameObjectInfo const* goInfo = GetGOInfo();
+ if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
+ {
+ // traps
+ Unit* owner = GetOwner();
+ Unit* ok = NULL; // pointer to appropriate target if found any
+
+ if(m_cooldownTime >= time(NULL))
+ return;
+
+ 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(goInfo->trap.cooldown != 3) // cast in other case (at some triggring/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;
+ }
+ }
+
+ bool NeedDespawn = (goInfo->trap.charges != 0);
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ // Note: this hack with search required until GO casting not implemented
+ // search unfriendly creature
+ if(owner && NeedDespawn) // hunter trap
+ {
+ MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
+ MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ // or unfriendly player/pet
+ if(!ok)
+ {
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+ }
+ 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* p_ok = NULL;
+ MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius);
+ MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ ok = p_ok;
+ }
+
+ if (ok)
+ {
+ Unit *caster = owner ? owner : ok;
+
+ caster->CastSpell(ok, goInfo->trap.spellId, true);
+ m_cooldownTime = time(NULL) + 4; // 4 seconds
+
+ if(NeedDespawn)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
+ {
+ //BattleGround gameobjects case
+ if(((Player*)ok)->InBattleGround())
+ if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
+ bg->HandleTriggerBuff(GetGUID());
+ }
+ }
+ }
+
+ if (m_charges && m_usetimes >= m_charges)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ break;
+ }
+ case GO_ACTIVATED:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ if(GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
+ {
+ SwitchDoorOrButton(false);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ break;
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED:
+ {
+ //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
+ if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
+ {
+ uint32 spellId = GetGOInfo()->goober.spellId;
+
+ if(spellId)
+ {
+ std::set<uint32>::iterator it = m_unique_users.begin();
+ std::set<uint32>::iterator end = m_unique_users.end();
+ for (; it != end; it++)
+ {
+ Unit* owner = Unit::GetUnit(*this, uint64(*it));
+ if (owner) owner->CastSpell(owner, spellId, false);
+ }
+
+ m_unique_users.clear();
+ m_usetimes = 0;
+ }
+ //any return here in case battleground traps
+ }
+
+ if(GetOwnerGUID())
+ {
+ m_respawnTime = 0;
+ Delete();
+ return;
+ }
+
+ //burning flags in some battlegrounds, if you find better condition, just add it
+ if (GetGoAnimProgress() > 0)
+ {
+ SendObjectDeSpawnAnim(this->GetGUID());
+ //reset flags
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+ }
+
+ loot.clear();
+ SetLootState(GO_READY);
+
+ if(!m_respawnDelayTime)
+ return;
+
+ if(!m_spawnedByDefault)
+ {
+ m_respawnTime = 0;
+ return;
+ }
+
+ m_respawnTime = time(NULL) + m_respawnDelayTime;
+
+ // if option not set then object will be saved at grid unload
+ if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ SaveRespawnTime();
+
+ ObjectAccessor::UpdateObjectVisibility(this);
+
+ break;
+ }
+ }
+}
+
+void GameObject::Refresh()
+{
+ // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
+ if(m_respawnTime > 0 && m_spawnedByDefault)
+ return;
+
+ if(isSpawned())
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+}
+
+void GameObject::AddUniqueUse(Player* player)
+{
+ AddUse();
+ m_unique_users.insert(player->GetGUIDLow());
+}
+
+void GameObject::Delete()
+{
+ SendObjectDeSpawnAnim(GetGUID());
+
+ SetGoState(1);
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+
+ AddObjectToRemoveList();
+}
+
+void GameObject::getFishLoot(Loot *fishloot)
+{
+ fishloot->clear();
+
+ uint32 subzone = GetAreaId();
+
+ // if subzone loot exist use it
+ if(LootTemplates_Fishing.HaveLootFor(subzone))
+ fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL);
+ // else use zone loot
+ else
+ fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL);
+}
+
+void GameObject::SaveToDB()
+{
+ // this should only be used when the creature has already been loaded
+ // perferably after adding to map, because mapid may not be valid otherwise
+ GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
+ if(!data)
+ {
+ sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
+ return;
+ }
+
+ SaveToDB(GetMapId(), data->spawnMask);
+}
+
+void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask)
+{
+ const GameObjectInfo *goI = GetGOInfo();
+
+ if (!goI)
+ return;
+
+ // update in loaded data (changing data only in this place)
+ GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
+
+ // data->guid = guid don't must be update at save
+ data.id = GetEntry();
+ data.mapid = mapid;
+ data.posX = GetFloatValue(GAMEOBJECT_POS_X);
+ data.posY = GetFloatValue(GAMEOBJECT_POS_Y);
+ data.posZ = GetFloatValue(GAMEOBJECT_POS_Z);
+ data.orientation = GetFloatValue(GAMEOBJECT_FACING);
+ data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0);
+ data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1);
+ data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2);
+ data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3);
+ data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
+ data.animprogress = GetGoAnimProgress();
+ data.go_state = GetGoState();
+ data.spawnMask = spawnMask;
+
+ // updated in DB
+ std::ostringstream ss;
+ ss << "INSERT INTO gameobject VALUES ( "
+ << m_DBTableGuid << ", "
+ << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", "
+ << mapid << ", "
+ << (uint32)spawnMask << ", "
+ << GetFloatValue(GAMEOBJECT_POS_X) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Y) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Z) << ", "
+ << GetFloatValue(GAMEOBJECT_FACING) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", "
+ << m_respawnDelayTime << ", "
+ << GetGoAnimProgress() << ", "
+ << GetGoState() << ")";
+
+ WorldDatabase.BeginTransaction();
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
+ WorldDatabase.CommitTransaction();
+}
+
+bool GameObject::LoadFromDB(uint32 guid, Map *map)
+{
+ GameObjectData const* data = objmgr.GetGOData(guid);
+
+ if( !data )
+ {
+ sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
+ return false;
+ }
+
+ uint32 entry = data->id;
+ uint32 map_id = data->mapid;
+ float x = data->posX;
+ float y = data->posY;
+ float z = data->posZ;
+ float ang = data->orientation;
+
+ float rotation0 = data->rotation0;
+ float rotation1 = data->rotation1;
+ float rotation2 = data->rotation2;
+ float rotation3 = data->rotation3;
+
+ uint32 animprogress = data->animprogress;
+ uint32 go_state = data->go_state;
+
+ uint32 stored_guid = guid;
+ if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) )
+ return false;
+
+ m_DBTableGuid = stored_guid;
+
+ switch(GetGOInfo()->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = 0;
+ m_respawnTime = 0;
+ break;*/
+ default:
+ if(data->spawntimesecs >= 0)
+ {
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = data->spawntimesecs;
+ m_respawnTime = objmgr.GetGORespawnTime(stored_guid, map->GetInstanceId());
+
+ // ready to respawn
+ if(m_respawnTime && m_respawnTime <= time(NULL))
+ {
+ m_respawnTime = 0;
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+ }
+ else
+ {
+ m_spawnedByDefault = false;
+ m_respawnDelayTime = -data->spawntimesecs;
+ m_respawnTime = 0;
+ }
+ break;
+ }
+
+ return true;
+}
+
+void GameObject::DeleteFromDB()
+{
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ objmgr.DeleteGOData(m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
+}
+
+GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid)
+{
+ return ObjectAccessor::GetGameObject(object,guid);
+}
+
+GameObjectInfo const *GameObject::GetGOInfo() const
+{
+ return m_goInfo;
+}
+
+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;
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ return ginfo->fishnode.lootId;
+ default:
+ return 0;
+ }
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+bool GameObject::hasQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::hasInvolvedQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::IsTransport() const
+{
+ // If something is marked as a transport, don't transmit an out of range packet for it.
+ GameObjectInfo const * gInfo = GetGOInfo();
+ if(!gInfo) return false;
+ return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
+}
+
+Unit* GameObject::GetOwner() const
+{
+ return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
+}
+
+void GameObject::SaveRespawnTime()
+{
+ if(m_respawnTime > time(NULL) && m_spawnedByDefault)
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
+}
+
+bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
+{
+ // Not in world
+ if(!IsInWorld() || !u->IsInWorld())
+ return false;
+
+ // Transport always visible at this step implementation
+ if(IsTransport() && IsInMap(u))
+ return true;
+
+ // quick check visibility false cases for non-GM-mode
+ if(!u->isGameMaster())
+ {
+ // despawned and then not visible for non-GM in GM-mode
+ if(!isSpawned())
+ return false;
+
+ // special invisibility cases
+ /* TODO: implement trap stealth, take look at spell 2836
+ if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
+ {
+ if(check stuff here)
+ return false;
+ }*/
+
+ // Smuggled Mana Cell required 10 invisibility type detection/state
+ if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0)
+ return false;
+ }
+
+ // check distance
+ return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() +
+ (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) );
+}
+
+void GameObject::Respawn()
+{
+ if(m_spawnedByDefault && m_respawnTime > 0)
+ {
+ m_respawnTime = time(NULL);
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+}
+
+bool GameObject::ActivateToQuest( Player *pTarget)const
+{
+ if(!objmgr.IsGameObjectForQuests(GetEntry()))
+ return false;
+
+ switch(GetGoType())
+ {
+ // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget))
+ return true;
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER:
+ {
+ if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
+{
+ GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
+ if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ return;
+
+ SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
+ if(!trapSpell) // checked at load already
+ return;
+
+ float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
+
+ // search nearest linked GO
+ GameObject* trapGO = NULL;
+ {
+ // using original GO distance
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // found correct GO
+ // FIXME: when GO casting will be implemented trap must cast spell to target
+ if(trapGO)
+ target->CastSpell(target,trapSpell,true);
+}
+
+GameObject* GameObject::LookupFishingHoleAround(float range)
+{
+ GameObject* ok = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
+ MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ return ok;
+}
+
+void GameObject::UseDoorOrButton(uint32 time_to_restore)
+{
+ if(m_lootState != GO_READY)
+ return;
+
+ if(!time_to_restore)
+ time_to_restore = GetAutoCloseTime();
+
+ SwitchDoorOrButton(true);
+ SetLootState(GO_ACTIVATED);
+
+ m_cooldownTime = time(NULL) + time_to_restore;
+
+}
+
+void GameObject::SwitchDoorOrButton(bool activate)
+{
+ if(activate)
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+ else
+ RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+
+ if(GetGoState()) //if closed -> open
+ SetGoState(0);
+ else //if open -> close
+ SetGoState(1);
+}
+
+void GameObject::Use(Unit* user)
+{
+ // by default spell caster is user
+ Unit* spellCaster = user;
+ uint32 spellId = 0;
+
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: //0
+ case GAMEOBJECT_TYPE_BUTTON: //1
+ //doors/buttons never really despawn, only reset to default state/flags
+ UseDoorOrButton();
+
+ // activate script
+ sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
+ return;
+
+ case GAMEOBJECT_TYPE_QUESTGIVER: //2
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ player->PrepareQuestMenu( GetGUID() );
+ player->SendPreparedQuest( GetGUID() );
+ return;
+ }
+ //Sitting: Wooden bench, chairs enzz
+ case GAMEOBJECT_TYPE_CHAIR: //7
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
+
+ // check if the db is sane
+ if(info->chair.slots > 0)
+ {
+ float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
+
+ float x_lowest = GetPositionX();
+ float y_lowest = GetPositionY();
+
+ // the object orientation + 1/2 pi
+ // every slot will be on that straight line
+ float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
+ // find nearest slot
+ for(uint32 i=0; i<info->chair.slots; i++)
+ {
+ // the distance between this slot and the center of the go - imagine a 1D space
+ float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
+
+ float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
+ float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
+
+ // calculate the distance between the player and this slot
+ float thisDistance = player->GetDistance2d(x_i, y_i);
+
+ /* debug code. It will spawn a npc on each slot to visualize them.
+ Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
+ std::ostringstream output;
+ output << i << ": thisDist: " << thisDistance;
+ helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
+ */
+
+ if(thisDistance <= lowestDist)
+ {
+ lowestDist = thisDistance;
+ x_lowest = x_i;
+ y_lowest = y_i;
+ }
+ }
+ player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ else
+ {
+ // fallback, will always work
+ player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height);
+ return;
+ }
+ //big gun, its a spell/aura
+ case GAMEOBJECT_TYPE_GOOBER: //10
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player* player = (Player*)user;
+
+ // show page
+ if(info->goober.pageId)
+ {
+ WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
+ data << GetGUID();
+ player->GetSession()->SendPacket(&data);
+ }
+
+ // possible quest objective for active quests
+ player->CastedCreatureOrGO(info->id, GetGUID(), 0);
+ }
+
+ // cast this spell later if provided
+ spellId = info->goober.spellId;
+
+ break;
+ }
+ case GAMEOBJECT_TYPE_CAMERA: //13
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(info->camera.cinematicId)
+ {
+ WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
+ data << info->camera.cinematicId;
+ player->GetSession()->SendPacket(&data);
+ }
+ return;
+ }
+ //fishing bobber
+ case GAMEOBJECT_TYPE_FISHINGNODE: //17
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(player->GetGUID() != GetOwnerGUID())
+ return;
+
+ switch(getLootState())
+ {
+ case GO_READY: // ready for loot
+ {
+ // 1) skill must be >= base_zone_skill
+ // 2) if skill == base_zone_skill => 5% chance
+ // 3) chance is linear dependence from (base_zone_skill-skill)
+
+ uint32 subzone = GetAreaId();
+
+ int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
+ if(!zone_skill)
+ zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() );
+
+ //provide error, no fishable zone or area should be 0
+ if(!zone_skill)
+ sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
+
+ int32 skill = player->GetSkillValue(SKILL_FISHING);
+ int32 chance = skill - zone_skill + 5;
+ int32 roll = irand(1,100);
+
+ DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
+
+ if(skill >= zone_skill && chance >= roll)
+ {
+ // prevent removing GO at spell cancel
+ player->RemoveGameObject(this,false);
+ SetOwnerGUID(player->GetGUID());
+
+ //fish catched
+ player->UpdateFishingSkill();
+
+ GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
+ if (ok)
+ {
+ player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else
+ player->SendLoot(GetGUID(),LOOT_FISHING);
+ }
+ else
+ {
+ // fish escaped, can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_ESCAPED, 0);
+ player->GetSession()->SendPacket(&data);
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
+ break;
+ default:
+ {
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
+ player->GetSession()->SendPacket(&data);
+ break;
+ }
+ }
+
+ if(player->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+ }
+ return;
+ }
+
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Unit* caster = GetOwner();
+
+ GameObjectInfo const* info = GetGOInfo();
+
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ // accept only use by player from same group for caster except caster itself
+ if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
+ return;
+
+ AddUniqueUse(player);
+
+ // full amount unique participants including original summoner
+ if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
+ return;
+
+ // in case summoning ritual caster is GO creator
+ spellCaster = caster;
+
+ if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ return;
+
+ spellId = info->summoningRitual.spellId;
+
+ // finish spell
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+
+ // can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ // go to end function to spell casting
+ break;
+ }
+ case GAMEOBJECT_TYPE_SPELLCASTER: //22
+ {
+ SetUInt32Value(GAMEOBJECT_FLAGS,2);
+
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(info->spellcaster.partyOnly)
+ {
+ Unit* caster = GetOwner();
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
+ return;
+ }
+
+ spellId = info->spellcaster.spellId;
+
+ AddUse();
+ break;
+ }
+ case GAMEOBJECT_TYPE_MEETINGSTONE: //23
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
+
+ // accept only use by player from same group for caster except caster itself
+ if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
+ return;
+
+ //required lvl checks!
+ uint8 level = player->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+ level = targetPlayer->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+
+ spellId = 23598;
+
+ break;
+ }
+
+ case GAMEOBJECT_TYPE_FLAGSTAND: // 24
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->InBattleGround() && // in battleground
+ !player->IsMounted() && // not mounted
+ !player->HasStealthAura() && // not stealthed
+ !player->HasInvisibilityAura() && // not invisible
+ player->isAlive()) // live player
+ {
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag click
+ // AB:
+ // 15001
+ // 15002
+ // 15003
+ // 15004
+ // 15005
+ bg->EventPlayerClickedOnFlag(player, this);
+ return; //we don;t need to delete flag ... it is despawned!
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_FLAGDROP: // 26
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->InBattleGround() && // in battleground
+ !player->IsMounted() && // not mounted
+ !player->HasStealthAura() && // not stealthed
+ !player->HasInvisibilityAura() && // not invisible
+ !player->HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup
+ player->isAlive()) // live player
+ {
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag dropped
+ // WS:
+ // 179785 - Silverwing Flag
+ // 179786 - Warsong Flag
+ // EotS:
+ // 184142 - Netherstorm Flag
+ GameObjectInfo const* info = GetGOInfo();
+ if(info)
+ {
+ switch(info->id)
+ {
+ case 179785: // Silverwing Flag
+ // check if it's correct bg
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 179786: // Warsong Flag
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 184142: // Netherstorm Flag
+ if(bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ }
+ }
+ //this cause to call return, all flags must be deleted here!!
+ spellId = 0;
+ Delete();
+ }
+ break;
+ }
+ default:
+ sLog.outDebug("Unknown Object Type %u", GetGoType());
+ break;
+ }
+
+ if(!spellId)
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
+ return;
+ }
+
+ Spell *spell = new Spell(spellCaster, spellInfo, false);
+
+ // spell target is user of GO
+ SpellCastTargets targets;
+ targets.setUnitTarget( user );
+
+ spell->prepare(&targets);
+}
diff --git a/src/game/GameObject.h b/src/game/GameObject.h
new file mode 100644
index 00000000000..30f3a815397
--- /dev/null
+++ b/src/game/GameObject.h
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_GAMEOBJECT_H
+#define MANGOSSERVER_GAMEOBJECT_H
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Object.h"
+#include "LootMgr.h"
+#include "Database/DatabaseEnv.h"
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+// from `gameobject_template`
+struct GameObjectInfo
+{
+ uint32 id;
+ uint32 type;
+ uint32 displayId;
+ char *name;
+ char *castBarCaption;
+ uint32 faction;
+ uint32 flags;
+ float size;
+ union // different GO types have different data field
+ {
+ //0 GAMEOBJECT_TYPE_DOOR
+ struct
+ {
+ uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 noDamageImmune; //3 break opening whenever you recieve damage?
+ uint32 openTextID; //4 can be used to replace castBarCaption?
+ uint32 closeTextID; //5
+ } door;
+ //1 GAMEOBJECT_TYPE_BUTTON
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 linkedTrap; //3
+ uint32 noDamageImmune; //4 isBattlegroundObject
+ uint32 large; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 closeTextID; //7
+ uint32 losOK; //8
+ } button;
+ //2 GAMEOBJECT_TYPE_QUESTGIVER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questList; //1
+ uint32 pageMaterial; //2
+ uint32 gossipID; //3
+ uint32 customAnim; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 losOK; //7
+ uint32 allowMounted; //8
+ uint32 large; //9
+ } questgiver;
+ //3 GAMEOBJECT_TYPE_CHEST
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 lootId; //1
+ uint32 chestRestockTime; //2
+ uint32 consumable; //3
+ uint32 minSuccessOpens; //4
+ uint32 maxSuccessOpens; //5
+ uint32 eventId; //6 lootedEvent
+ uint32 linkedTrapId; //7
+ uint32 questId; //8 not used currently but store quest required for GO activation for player
+ uint32 level; //9
+ uint32 losOK; //10
+ uint32 leaveLoot; //11
+ uint32 notInCombat; //12
+ uint32 logLoot; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 groupLootRules; //15
+ } chest;
+ //5 GAMEOBJECT_TYPE_GENERIC
+ struct
+ {
+ uint32 floatingTooltip; //0
+ uint32 highlight; //1
+ uint32 serverOnly; //2
+ uint32 large; //3
+ uint32 floatOnWater; //4
+ uint32 questID; //5
+ } _generic;
+ //6 GAMEOBJECT_TYPE_TRAP
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 level; //1
+ uint32 radius; //2 radius for trap activation
+ uint32 spellId; //3
+ uint32 charges; //4 need respawn (if > 0)
+ uint32 cooldown; //5 time in secs
+ uint32 autoCloseTime; //6
+ uint32 startDelay; //7
+ uint32 serverOnly; //8
+ uint32 stealthed; //9
+ uint32 large; //10
+ uint32 stealthAffected; //11
+ uint32 openTextID; //12 can be used to replace castBarCaption?
+ uint32 closeTextID; //13
+ } trap;
+ //7 GAMEOBJECT_TYPE_CHAIR
+ struct
+ {
+ uint32 slots; //0
+ uint32 height; //1
+ uint32 onlyCreatorUse; //2
+ } chair;
+ //8 GAMEOBJECT_TYPE_SPELL_FOCUS
+ struct
+ {
+ uint32 focusId; //0
+ uint32 dist; //1
+ uint32 linkedTrapId; //2
+ uint32 serverOnly; //3
+ uint32 questID; //4
+ uint32 large; //5
+ } spellFocus;
+ //9 GAMEOBJECT_TYPE_TEXT
+ struct
+ {
+ uint32 pageID; //0
+ uint32 language; //1
+ uint32 pageMaterial; //2
+ uint32 allowMounted; //3
+ } text;
+ //10 GAMEOBJECT_TYPE_GOOBER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questId; //1
+ uint32 eventId; //2
+ uint32 autoCloseTime; //3
+ uint32 customAnim; //4
+ uint32 consumable; //5
+ uint32 cooldown; //6
+ uint32 pageId; //7
+ uint32 language; //8
+ uint32 pageMaterial; //9
+ uint32 spellId; //10
+ uint32 noDamageImmune; //11
+ uint32 linkedTrapId; //12
+ uint32 large; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 closeTextID; //15
+ uint32 losOK; //16 isBattlegroundObject
+ uint32 allowMounted; //17
+ } goober;
+ //11 GAMEOBJECT_TYPE_TRANSPORT
+ struct
+ {
+ uint32 pause; //0
+ uint32 startOpen; //1
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ } transport;
+ //12 GAMEOBJECT_TYPE_AREADAMAGE
+ struct
+ {
+ uint32 lockId; //0
+ uint32 radius; //1
+ uint32 damageMin; //2
+ uint32 damageMax; //3
+ uint32 damageSchool; //4
+ uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000
+ uint32 openTextID; //6
+ uint32 closeTextID; //7
+ } areadamage;
+ //13 GAMEOBJECT_TYPE_CAMERA
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 cinematicId; //1
+ uint32 eventID; //2
+ uint32 openTextID; //3 can be used to replace castBarCaption?
+ } camera;
+ //15 GAMEOBJECT_TYPE_MO_TRANSPORT
+ struct
+ {
+ uint32 taxiPathId; //0
+ uint32 moveSpeed; //1
+ uint32 accelRate; //2
+ uint32 startEventID; //3
+ uint32 stopEventID; //4
+ uint32 transportPhysics; //5
+ uint32 mapID; //6
+ } moTransport;
+ //17 GAMEOBJECT_TYPE_FISHINGNODE
+ struct
+ {
+ uint32 _data0; //0
+ uint32 lootId; //1
+ } fishnode;
+ //18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
+ struct
+ {
+ uint32 reqParticipants; //0
+ uint32 spellId; //1
+ uint32 animSpell; //2
+ uint32 ritualPersistent; //3
+ uint32 casterTargetSpell; //4
+ uint32 casterTargetSpellTargets; //5
+ uint32 castersGrouped; //6
+ uint32 ritualNoTargetCheck; //7
+ } summoningRitual;
+ //20 GAMEOBJECT_TYPE_AUCTIONHOUSE
+ struct
+ {
+ uint32 actionHouseID; //0
+ } auctionhouse;
+ //21 GAMEOBJECT_TYPE_GUARDPOST
+ struct
+ {
+ uint32 creatureID; //0
+ uint32 charges; //1
+ } guardpost;
+ //22 GAMEOBJECT_TYPE_SPELLCASTER
+ struct
+ {
+ uint32 spellId; //0
+ uint32 charges; //1
+ uint32 partyOnly; //2
+ } spellcaster;
+ //23 GAMEOBJECT_TYPE_MEETINGSTONE
+ struct
+ {
+ uint32 minLevel; //0
+ uint32 maxLevel; //1
+ uint32 areaID; //2
+ } meetingstone;
+ //24 GAMEOBJECT_TYPE_FLAGSTAND
+ struct
+ {
+ uint32 lockId; //0
+ uint32 pickupSpell; //1
+ uint32 radius; //2
+ uint32 returnAura; //3
+ uint32 returnSpell; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6
+ uint32 losOK; //7
+ } flagstand;
+ //25 GAMEOBJECT_TYPE_FISHINGHOLE // not implemented yet
+ struct
+ {
+ uint32 radius; //0 how close bobber must land for sending loot
+ uint32 lootId; //1
+ uint32 minSuccessOpens; //2
+ uint32 maxSuccessOpens; //3
+ uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all?
+ } fishinghole;
+ //26 GAMEOBJECT_TYPE_FLAGDROP
+ struct
+ {
+ uint32 lockId; //0
+ uint32 eventID; //1
+ uint32 pickupSpell; //2
+ uint32 noDamageImmune; //3
+ uint32 openTextID; //4
+ } flagdrop;
+ //27 GAMEOBJECT_TYPE_MINI_GAME
+ struct
+ {
+ uint32 gameType; //0
+ } miniGame;
+ //29 GAMEOBJECT_TYPE_CAPTURE_POINT
+ struct
+ {
+ uint32 radius; //0
+ uint32 spell; //1
+ uint32 worldState1; //2
+ uint32 worldstate2; //3
+ uint32 winEventID1; //4
+ uint32 winEventID2; //5
+ uint32 contestedEventID1; //6
+ uint32 contestedEventID2; //7
+ uint32 progressEventID1; //8
+ uint32 progressEventID2; //9
+ uint32 neutralEventID1; //10
+ uint32 neutralEventID2; //11
+ uint32 neutralPercent; //12
+ uint32 worldstate3; //13
+ uint32 minSuperiority; //14
+ uint32 maxSuperiority; //15
+ uint32 minTime; //16
+ uint32 maxTime; //17
+ uint32 large; //18
+ uint32 highlight; //19
+ } capturePoint;
+ //30 GAMEOBJECT_TYPE_AURA_GENERATOR
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 radius; //1
+ uint32 auraID1; //2
+ uint32 conditionID1; //3
+ uint32 auraID2; //4
+ uint32 conditionID2; //5
+ uint32 serverOnly; //6
+ } auraGenerator;
+ //31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } dungeonDifficulty;
+ //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } doNotUseYet;
+ //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
+ struct
+ {
+ uint32 dmgPctState1; //0
+ uint32 dmgPctState2; //1
+ uint32 state1Name; //2
+ uint32 state2Name; //3
+ } destructibleBuilding;
+
+ // not use for specific field access (only for output with loop by all filed), also this determinate max union size
+ struct // GAMEOBJECT_TYPE_SPELLCASTER
+ {
+ uint32 data[24];
+ } raw;
+ };
+ char *ScriptName;
+};
+
+struct GameObjectLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> CastBarCaption;
+};
+
+// from `gameobject`
+struct GameObjectData
+{
+ uint32 id; // entry in gamobject_template
+ uint32 mapid;
+ float posX;
+ float posY;
+ float posZ;
+ float orientation;
+ float rotation0;
+ float rotation1;
+ float rotation2;
+ float rotation3;
+ int32 spawntimesecs;
+ uint32 animprogress;
+ uint32 go_state;
+ uint8 spawnMask;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
+// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
+// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
+// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
+enum LootState
+{
+ GO_NOT_READY = 0,
+ GO_READY, // can be ready but despawned, and then not possible activate until spawn
+ GO_ACTIVATED,
+ GO_JUST_DEACTIVATED
+};
+
+class Unit;
+
+// 5 sec for bobber catch
+#define FISHING_BOBBER_READY_TIME 5
+
+class MANGOS_DLL_SPEC GameObject : public WorldObject
+{
+ public:
+ explicit GameObject();
+ ~GameObject();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state);
+ void Update(uint32 p_time);
+ static GameObject* GetGameObject(WorldObject& object, uint64 guid);
+ GameObjectInfo const* GetGOInfo() const;
+
+ 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 Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
+ void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
+ void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); }
+
+ void SaveToDB();
+ void SaveToDB(uint32 mapid, uint8 spawnMask);
+ bool LoadFromDB(uint32 guid, Map *map);
+ void DeleteFromDB();
+ void SetLootState(LootState s) { m_lootState = s; }
+ static uint32 GetLootId(GameObjectInfo const* info);
+ uint32 GetLootId() const { return GetLootId(GetGOInfo()); }
+ uint32 GetLockId() const
+ {
+ switch(GetGoType())
+ {
+ 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;
+ }
+ }
+
+ time_t GetRespawnTime() const { return m_respawnTime; }
+ time_t GetRespawnTimeEx() const
+ {
+ time_t now = time(NULL);
+ if(m_respawnTime > now)
+ return m_respawnTime;
+ else
+ return now;
+ }
+
+ void SetRespawnTime(int32 respawn)
+ {
+ m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
+ m_respawnDelayTime = respawn > 0 ? respawn : 0;
+ }
+ void Respawn();
+ bool isSpawned() const
+ {
+ return m_respawnDelayTime == 0 ||
+ (m_respawnTime > 0 && !m_spawnedByDefault) ||
+ (m_respawnTime == 0 && m_spawnedByDefault);
+ }
+ bool isSpawnedByDefault() const { return m_spawnedByDefault; }
+ uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
+ void Refresh();
+ void Delete();
+ void SetSpellId(uint32 id) { m_spellId = id;}
+ uint32 GetSpellId() const { return m_spellId;}
+ void getFishLoot(Loot *loot);
+ GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); }
+ void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); }
+ uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); }
+ void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); }
+ uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); }
+ void SetGoArtKit(uint32 artkit) { SetUInt32Value(GAMEOBJECT_ARTKIT, artkit); }
+ uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); }
+ void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); }
+
+ void Use(Unit* user);
+
+ LootState getLootState() const { return m_lootState; }
+
+ void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); }
+ bool IsInSkillupList(uint32 PlayerGuidLow) const
+ {
+ for (std::list<uint32>::const_iterator i = m_SkillupList.begin(); i != m_SkillupList.end(); ++i)
+ if (*i == PlayerGuidLow) return true;
+ return false;
+ }
+ void ClearSkillupList() { m_SkillupList.clear(); }
+
+ void AddUniqueUse(Player* player);
+ void AddUse() { ++m_usetimes; }
+
+ uint32 GetUseCount() const { return m_usetimes; }
+ uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
+
+ void SaveRespawnTime();
+
+ Loot loot;
+
+ bool hasQuest(uint32 quest_id) const;
+ bool hasInvolvedQuest(uint32 quest_id) const;
+ bool ActivateToQuest(Player *pTarget) const;
+ void UseDoorOrButton(uint32 time_to_restore = 0); // 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);
+
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
+ GameObject* LookupFishingHoleAround(float range);
+
+ GridReference<GameObject> &GetGridRef() { return m_gridRef; }
+ 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
+ LootState m_lootState;
+ bool m_spawnedByDefault;
+ time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
+ // For traps this: spell casting cooldown, for doors/buttons: reset time.
+ std::list<uint32> m_SkillupList;
+
+ std::set<uint32> m_unique_users;
+ uint32 m_usetimes;
+
+ uint32 m_DBTableGuid;
+ GameObjectInfo const* m_goInfo;
+ private:
+ void SwitchDoorOrButton(bool activate);
+
+ GridReference<GameObject> m_gridRef;
+};
+#endif
diff --git a/src/game/GlobalEvents.cpp b/src/game/GlobalEvents.cpp
new file mode 100644
index 00000000000..5bac79ad102
--- /dev/null
+++ b/src/game/GlobalEvents.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup world
+*/
+
+#include "Log.h"
+#include "Database/DatabaseEnv.h"
+#include "Platform/Define.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "GlobalEvents.h"
+#include "ObjectDefines.h"
+#include "Corpse.h"
+
+/// Handle periodic erase of corpses and bones
+static void CorpsesErase(bool bones,uint32 delay)
+{
+ ///- Get the list of eligible corpses/bones to be removed
+ //No SQL injection (uint32 and enum)
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid,position_x,position_y,map,player FROM corpse WHERE UNIX_TIMESTAMP()-time > '%u' AND corpse_type %s '0'", delay, (bones ? "=" : "<>") );
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guidlow = fields[0].GetUInt32();
+ float positionX = fields[1].GetFloat();
+ float positionY = fields[2].GetFloat();
+ uint32 mapid = fields[3].GetUInt32();
+ uint64 player_guid = MAKE_NEW_GUID(fields[4].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ uint64 guid = MAKE_NEW_GUID(guidlow, 0, HIGHGUID_CORPSE);
+
+ sLog.outDebug("[Global event] Removing %s %u (X:%f Y:%f Map:%u).",(bones?"bones":"corpse"),guidlow,positionX,positionY,mapid);
+
+ /// Resurrectable - convert corpses to bones
+ if(!bones)
+ {
+ if(!ObjectAccessor::Instance().ConvertCorpseForPlayer(player_guid))
+ {
+ sLog.outDebug("Corpse %u not found in world. Delete from DB.",guidlow);
+ CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow);
+ }
+ }
+ else
+ ///- or delete bones
+ {
+ MapManager::Instance().RemoveBonesFromMap(mapid, guid, positionX, positionY);
+
+ ///- remove bones from the database
+ CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow);
+ }
+ } while (result->NextRow());
+
+ delete result;
+ }
+}
+
+/// not thread guarded variant for call from other thread
+void CorpsesErase()
+{
+ CorpsesErase(true, 20*MINUTE);
+ CorpsesErase(false,3*DAY);
+}
diff --git a/src/game/GlobalEvents.h b/src/game/GlobalEvents.h
new file mode 100644
index 00000000000..aacf72d730a
--- /dev/null
+++ b/src/game/GlobalEvents.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup world
+/// @{
+/// \file
+
+#ifndef __GLOBALEVENTS_H
+#define __GLOBALEVENTS_H
+
+void CorpsesErase();
+void HandleCorpsesErase(void*);
+#endif
+/// @}
diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp
new file mode 100644
index 00000000000..57c3597a7b8
--- /dev/null
+++ b/src/game/GossipDef.cpp
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "QuestDef.h"
+#include "GossipDef.h"
+#include "ObjectMgr.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+
+GossipMenu::GossipMenu()
+{
+ m_gItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
+}
+
+GossipMenu::~GossipMenu()
+{
+ ClearMenu();
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded)
+{
+ ASSERT( m_gItems.size() <= GOSSIP_MAX_MENU_ITEMS );
+
+ GossipMenuItem gItem;
+
+ gItem.m_gIcon = Icon;
+ gItem.m_gMessage = Message;
+ gItem.m_gCoded = Coded;
+ gItem.m_gSender = dtSender;
+ gItem.m_gAction = dtAction;
+ gItem.m_gBoxMessage = BoxMessage;
+ gItem.m_gBoxMoney = BoxMoney;
+
+ m_gItems.push_back(gItem);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, bool Coded)
+{
+ AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, bool Coded)
+{
+ AddMenuItem(Icon, std::string(Message ? Message : ""),Coded);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded)
+{
+ AddMenuItem(Icon, std::string(Message ? Message : ""), dtSender, dtAction, std::string(BoxMessage ? BoxMessage : ""), BoxMoney, Coded);
+}
+
+uint32 GossipMenu::MenuItemSender( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gSender;
+}
+
+uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gAction;
+}
+
+bool GossipMenu::MenuItemCoded( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gCoded;
+}
+
+void GossipMenu::ClearMenu()
+{
+ m_gItems.clear();
+}
+
+PlayerMenu::PlayerMenu( WorldSession *Session )
+{
+ pGossipMenu = new GossipMenu();
+ pQuestMenu = new QuestMenu();
+ pSession = Session;
+}
+
+PlayerMenu::~PlayerMenu()
+{
+ delete pGossipMenu;
+ delete pQuestMenu;
+}
+
+void PlayerMenu::ClearMenus()
+{
+ pGossipMenu->ClearMenu();
+ pQuestMenu->ClearMenu();
+}
+
+uint32 PlayerMenu::GossipOptionSender( unsigned int Selection )
+{
+ return pGossipMenu->MenuItemSender( Selection );
+}
+
+uint32 PlayerMenu::GossipOptionAction( unsigned int Selection )
+{
+ return pGossipMenu->MenuItemAction( Selection );
+}
+
+bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
+{
+ return pGossipMenu->MenuItemCoded( Selection );
+}
+
+void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
+ data << npcGUID;
+ data << uint32(0); // new 2.4.0
+ data << uint32( TitleTextId );
+ data << uint32( pGossipMenu->MenuItemCount() ); // max count 0x0F
+
+ for ( unsigned int iI = 0; iI < pGossipMenu->MenuItemCount(); iI++ )
+ {
+ GossipMenuItem const& gItem = pGossipMenu->GetItem(iI);
+ data << uint32( iI );
+ data << uint8( gItem.m_gIcon );
+ // icons:
+ // 0 unlearn talents/misc
+ // 1 trader
+ // 2 taxi
+ // 3 trainer
+ // 9 BG/arena
+ data << uint8( gItem.m_gCoded ); // makes pop up box password
+ data << uint32(gItem.m_gBoxMoney); // money required to open menu, 2.0.3
+ data << gItem.m_gMessage; // text for gossip item
+ data << gItem.m_gBoxMessage; // accept text (related to money) pop up box, 2.0.3
+ }
+
+ data << uint32( pQuestMenu->MenuItemCount() ); // max count 0x20
+
+ for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ )
+ {
+ QuestMenuItem const& qItem = pQuestMenu->GetItem(iI);
+ uint32 questID = qItem.m_qId;
+ Quest const* pQuest = objmgr.GetQuestTemplate(questID);
+
+ data << questID;
+ data << uint32( qItem.m_qIcon );
+ data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 );
+ std::string Title = pQuest->GetTitle();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(questID);
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ }
+ }
+ data << Title;
+ }
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_MESSAGE NPCGuid=%u",GUID_LOPART(npcGUID) );
+}
+
+void PlayerMenu::CloseGossip()
+{
+ WorldPacket data( SMSG_GOSSIP_COMPLETE, 0 );
+ pSession->SendPacket( &data );
+
+ //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_COMPLETE" );
+}
+
+void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, char const * locName )
+{
+ WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size
+ data << Flags;
+ data << X << Y;
+ data << uint32(Icon);
+ data << uint32(Data);
+ data << locName;
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI");
+}
+
+void PlayerMenu::SendTalking( uint32 textID )
+{
+ GossipText *pGossip;
+ std::string GossipStr;
+
+ pGossip = objmgr.GetGossipText(textID);
+
+ WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
+ data << textID; // can be < 0
+
+ if (!pGossip)
+ {
+ for(uint32 i = 0; i < 8; ++i)
+ {
+ data << float(0);
+ data << "Greetings $N";
+ data << "Greetings $N";
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ }
+ }
+ else
+ {
+ std::string Text_0[8],Text_1[8];
+ for (int i=0;i<8;i++)
+ {
+ Text_0[i]=pGossip->Options[i].Text_0;
+ Text_1[i]=pGossip->Options[i].Text_1;
+ }
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID);
+ if (nl)
+ {
+ for (int i=0;i<8;i++)
+ {
+ if (nl->Text_0[i].size() > loc_idx && !nl->Text_0[i][loc_idx].empty())
+ Text_0[i]=nl->Text_0[i][loc_idx];
+ if (nl->Text_1[i].size() > loc_idx && !nl->Text_1[i][loc_idx].empty())
+ Text_1[i]=nl->Text_1[i][loc_idx];
+ }
+ }
+ }
+ for (int i=0; i<8; i++)
+ {
+ data << pGossip->Options[i].Probability;
+
+ if ( Text_0[i].empty() )
+ data << Text_1[i];
+ else
+ data << Text_0[i];
+
+ if ( Text_1[i].empty() )
+ data << Text_0[i];
+ else
+ data << Text_1[i];
+
+ data << pGossip->Options[i].Language;
+
+ data << pGossip->Options[i].Emotes[0]._Delay;
+ data << pGossip->Options[i].Emotes[0]._Emote;
+
+ data << pGossip->Options[i].Emotes[1]._Delay;
+ data << pGossip->Options[i].Emotes[1]._Emote;
+
+ data << pGossip->Options[i].Emotes[2]._Delay;
+ data << pGossip->Options[i].Emotes[2]._Emote;
+ }
+ }
+ pSession->SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
+}
+
+void PlayerMenu::SendTalking( char const * title, char const * text )
+{
+ WorldPacket data( SMSG_NPC_TEXT_UPDATE, 50 ); // guess size
+ data << uint32(0);
+ for(uint32 i = 0; i < 8; ++i)
+ {
+ data << float(0);
+ data << title;
+ data << text;
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ }
+
+ pSession->SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+
+QuestMenu::QuestMenu()
+{
+ m_qItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
+}
+
+QuestMenu::~QuestMenu()
+{
+ ClearMenu();
+}
+
+void QuestMenu::AddMenuItem( uint32 QuestId, uint8 Icon)
+{
+ Quest const* qinfo = objmgr.GetQuestTemplate(QuestId);
+ if (!qinfo) return;
+
+ ASSERT( m_qItems.size() <= GOSSIP_MAX_MENU_ITEMS );
+
+ QuestMenuItem qItem;
+
+ qItem.m_qId = QuestId;
+ qItem.m_qIcon = Icon;
+
+ m_qItems.push_back(qItem);
+}
+
+bool QuestMenu::HasItem( uint32 questid )
+{
+ for (QuestMenuItemList::iterator i = m_qItems.begin(); i != m_qItems.end(); i++)
+ {
+ if(i->m_qId==questid)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuestMenu::ClearMenu()
+{
+ m_qItems.clear();
+}
+
+void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_LIST, 100 ); // guess size
+ data << uint64(npcGUID);
+ data << Title;
+ data << uint32(eEmote._Delay ); // player emote
+ data << uint32(eEmote._Emote ); // NPC emote
+ data << uint8 ( pQuestMenu->MenuItemCount() );
+
+ for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ )
+ {
+ QuestMenuItem qmi=pQuestMenu->GetItem(iI);
+ uint32 questID = qmi.m_qId;
+ Quest const *pQuest = objmgr.GetQuestTemplate(questID);
+
+ std::string title = pQuest ? pQuest->GetTitle() : "";
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ if(QuestLocale const *ql = objmgr.GetQuestLocale(questID))
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ title=ql->Title[loc_idx];
+ }
+ }
+
+ data << uint32(questID);
+ data << uint32(qmi.m_qIcon);
+ data << uint32(pQuest ? pQuest->GetQuestLevel() : 0);
+ data << title;
+ }
+ pSession->SendPacket( &data );
+ //uint32 fqid=pQuestMenu->GetItem(0).m_qId;
+ //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC Guid=%u, questid-0=%u",npcGUID,fqid);
+}
+
+void PlayerMenu::SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_QUESTGIVER_STATUS, 9 );
+ data << uint64(npcGUID);
+ data << uint8(questStatus);
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
+}
+
+void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept )
+{
+ WorldPacket data(SMSG_QUESTGIVER_QUEST_DETAILS, 100); // guess size
+
+ std::string Title = pQuest->GetTitle();
+ std::string Details = pQuest->GetDetails();
+ std::string Objectives = pQuest->GetObjectives();
+ std::string EndText = pQuest->GetEndText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
+ Details=ql->Details[loc_idx];
+ if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
+ Objectives=ql->Objectives[loc_idx];
+ if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
+ EndText=ql->EndText[loc_idx];
+ }
+ }
+
+ data << uint64(npcGUID);
+ data << uint32(pQuest->GetQuestId());
+ data << Title << Details << Objectives;
+ data << uint32(ActivateAccept);
+ data << uint32(pQuest->GetSuggestedPlayers());
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ {
+ data << uint32(0); // Rewarded chosen items hidden
+ data << uint32(0); // Rewarded items hidden
+ data << uint32(0); // Rewarded money hidden
+ }
+ else
+ {
+ ItemPrototype const* IProto;
+
+ data << uint32(pQuest->GetRewChoiceItemsCount());
+ for (uint32 i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
+ {
+ if ( !pQuest->RewChoiceItemId[i] ) continue;
+ data << uint32(pQuest->RewChoiceItemId[i]);
+ data << uint32(pQuest->RewChoiceItemCount[i]);
+ IProto = objmgr.GetItemPrototype(pQuest->RewChoiceItemId[i]);
+ if ( IProto )
+ data << uint32(IProto->DisplayInfoID);
+ else
+ data << uint32( 0x00 );
+ }
+
+ data << uint32(pQuest->GetRewItemsCount());
+ for (uint32 i=0; i < QUEST_REWARDS_COUNT; i++)
+ {
+ if ( !pQuest->RewItemId[i] ) continue;
+ data << uint32(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemCount[i]);
+ IProto = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
+ if ( IProto )
+ data << uint32(IProto->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+ data << uint32(pQuest->GetRewOrReqMoney());
+ }
+
+ data << uint32(0); // Honor points reward, not implemented
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+ data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
+
+ data << uint32(QUEST_EMOTE_COUNT);
+ for (uint32 i=0; i < QUEST_EMOTE_COUNT; i++)
+ {
+ data << uint32(pQuest->DetailsEmote[i]);
+ data << uint32(0); // DetailsEmoteDelay
+ }
+ pSession->SendPacket( &data );
+
+ //sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
+}
+
+void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
+{
+ std::string Title,Details,Objectives,EndText;
+ std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
+ Title = pQuest->GetTitle();
+ Details = pQuest->GetDetails();
+ Objectives = pQuest->GetObjectives();
+ EndText = pQuest->GetEndText();
+ for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
+ ObjectiveText[i]=pQuest->ObjectiveText[i];
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
+ Details=ql->Details[loc_idx];
+ if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
+ Objectives=ql->Objectives[loc_idx];
+ if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
+ EndText=ql->EndText[loc_idx];
+
+ for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
+ if (ql->ObjectiveText[i].size() > loc_idx && !ql->ObjectiveText[i][loc_idx].empty())
+ ObjectiveText[i]=ql->ObjectiveText[i][loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size
+
+ data << uint32(pQuest->GetQuestId());
+ data << uint32(pQuest->GetMinLevel()); // not MinLevel. Accepted values: 0, 1 or 2 Possible theory for future dev: 0==cannot in quest log, 1==can in quest log session only(removed on log out), 2==can in quest log always (save to db)
+ data << uint32(pQuest->GetQuestLevel()); // may be 0
+ data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log
+
+ data << uint32(pQuest->GetType());
+ data << uint32(pQuest->GetSuggestedPlayers());
+
+ data << uint32(pQuest->GetRepObjectiveFaction()); // shown in quest log as part of quest objective
+ data << uint32(pQuest->GetRepObjectiveValue()); // shown in quest log as part of quest objective
+
+ data << uint32(0); // RequiredOpositeRepFaction
+ data << uint32(0); // RequiredOpositeRepValue, required faction value with another (oposite) faction (objective)
+
+ data << uint32(pQuest->GetNextQuestInChain()); // client will request this quest from NPC, if not 0
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ data << uint32(0); // Hide money rewarded
+ else
+ data << uint32(pQuest->GetRewOrReqMoney());
+
+ data << uint32(pQuest->GetRewMoneyMaxLevel()); // used in XP calculation at client
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+
+ data << uint32(0); // Honor points reward, not implemented
+ data << uint32(pQuest->GetSrcItemId());
+ data << uint32(pQuest->GetFlags() & 0xFFFF);
+ data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
+
+ int iI;
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ {
+ for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
+ data << uint32(0) << uint32(0);
+ for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
+ data << uint32(0) << uint32(0);
+ }
+ else
+ {
+ for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
+ {
+ data << uint32(pQuest->RewItemId[iI]);
+ data << uint32(pQuest->RewItemCount[iI]);
+ }
+ for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
+ {
+ data << uint32(pQuest->RewChoiceItemId[iI]);
+ data << uint32(pQuest->RewChoiceItemCount[iI]);
+ }
+ }
+
+ data << pQuest->GetPointMapId();
+ data << pQuest->GetPointX();
+ data << pQuest->GetPointY();
+ data << pQuest->GetPointOpt();
+
+ data << Title;
+ data << Objectives;
+ data << Details;
+ data << EndText;
+
+ for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
+ {
+ if (pQuest->ReqCreatureOrGOId[iI] < 0)
+ {
+ // client expected gameobject template id in form (id|0x80000000)
+ data << uint32((pQuest->ReqCreatureOrGOId[iI]*(-1))|0x80000000);
+ }
+ else
+ {
+ data << uint32(pQuest->ReqCreatureOrGOId[iI]);
+ }
+ data << uint32(pQuest->ReqCreatureOrGOCount[iI]);
+ data << uint32(pQuest->ReqItemId[iI]);
+ data << uint32(pQuest->ReqItemCount[iI]);
+ }
+
+ for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
+ data << ObjectiveText[iI];
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
+}
+
+void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext )
+{
+ std::string Title = pQuest->GetTitle();
+ std::string OfferRewardText = pQuest->GetOfferRewardText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->OfferRewardText.size() > loc_idx && !ql->OfferRewardText[loc_idx].empty())
+ OfferRewardText=ql->OfferRewardText[loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUESTGIVER_OFFER_REWARD, 50 ); // guess size
+
+ data << npcGUID;
+ data << pQuest->GetQuestId();
+ data << Title;
+ data << OfferRewardText;
+
+ data << uint32( EnbleNext );
+ data << uint32(0); // unk
+
+ uint32 EmoteCount = 0;
+ for (uint32 i = 0; i < QUEST_EMOTE_COUNT; i++)
+ {
+ if(pQuest->OfferRewardEmote[i] <= 0)
+ break;
+ ++EmoteCount;
+ }
+
+ data << EmoteCount; // Emote Count
+ for (uint32 i = 0; i < EmoteCount; i++)
+ {
+ data << uint32(0); // Delay Emote
+ data << pQuest->OfferRewardEmote[i];
+ }
+
+ ItemPrototype const *pItem;
+
+ data << uint32(pQuest->GetRewChoiceItemsCount());
+ for (uint32 i=0; i < pQuest->GetRewChoiceItemsCount(); i++)
+ {
+ pItem = objmgr.GetItemPrototype( pQuest->RewChoiceItemId[i] );
+
+ data << uint32(pQuest->RewChoiceItemId[i]);
+ data << uint32(pQuest->RewChoiceItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ data << uint32(pQuest->GetRewItemsCount());
+ for (uint16 i=0; i < pQuest->GetRewItemsCount(); i++)
+ {
+ pItem = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ data << uint32(pQuest->GetRewOrReqMoney());
+ data << uint32(0x00); // new 2.3.0. Honor points
+ data << uint32(0x08); // unused by client?
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+ data << uint32(0); // Honor points reward, not implemented
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+}
+
+void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel )
+{
+ // We can always call to RequestItems, but this packet only goes out if there are actually
+ // items. Otherwise, we'll skip straight to the OfferReward
+
+ // We may wish a better check, perhaps checking the real quest requirements
+ if (pQuest->GetRequestItemsText().empty())
+ {
+ SendQuestGiverOfferReward(pQuest, npcGUID, true);
+ return;
+ }
+
+ std::string Title,RequestItemsText;
+ Title = pQuest->GetTitle();
+ RequestItemsText = pQuest->GetRequestItemsText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->RequestItemsText.size() > loc_idx && !ql->RequestItemsText[loc_idx].empty())
+ RequestItemsText=ql->RequestItemsText[loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUESTGIVER_REQUEST_ITEMS, 50 ); // guess size
+ data << npcGUID;
+ data << pQuest->GetQuestId();
+ data << Title;
+ data << RequestItemsText;
+
+ data << uint32(0x00); // unknown
+
+ if(Completable)
+ data << pQuest->GetCompleteEmote();
+ else
+ data << pQuest->GetIncompleteEmote();
+
+ // Close Window after cancel
+ if (CloseOnCancel)
+ data << uint32(0x01);
+ else
+ data << uint32(0x00);
+
+ data << uint32(0x00); // unknown
+
+ // Required Money
+ data << uint32(pQuest->GetRewOrReqMoney() < 0 ? -pQuest->GetRewOrReqMoney() : 0);
+
+ data << uint32( pQuest->GetReqItemsCount() );
+ ItemPrototype const *pItem;
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ( !pQuest->ReqItemId[i] ) continue;
+ pItem = objmgr.GetItemPrototype(pQuest->ReqItemId[i]);
+ data << uint32(pQuest->ReqItemId[i]);
+ data << uint32(pQuest->ReqItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ if ( !Completable )
+ data << uint32(0x00);
+ else
+ data << uint32(0x03);
+
+ data << uint32(0x04) << uint32(0x08) << uint32(0x10);
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+}
diff --git a/src/game/GossipDef.h b/src/game/GossipDef.h
new file mode 100644
index 00000000000..aacf7a8fcb8
--- /dev/null
+++ b/src/game/GossipDef.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_GOSSIP_H
+#define MANGOSSERVER_GOSSIP_H
+
+#include "Common.h"
+#include "QuestDef.h"
+#include "NPCHandler.h"
+
+class WorldSession;
+
+#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
+#define DEFAULT_GOSSIP_MESSAGE 0xffffff
+
+//POI defines
+enum Poi_Icon
+{
+ ICON_POI_0 = 0, // Grey ?
+ ICON_POI_1 = 1, // Red ?
+ ICON_POI_2 = 2, // Blue ?
+ ICON_POI_BWTOMB = 3, // Blue and White Tomb Stone
+ ICON_POI_HOUSE = 4, // House
+ ICON_POI_TOWER = 5, // Tower
+ ICON_POI_REDFLAG = 6, // Red Flag with Yellow !
+ ICON_POI_TOMB = 7, // Tomb Stone
+ ICON_POI_BWTOWER = 8, // Blue and White Tower
+ ICON_POI_REDTOWER = 9, // Red Tower
+ ICON_POI_BLUETOWER = 10, // Blue Tower
+ ICON_POI_RWTOWER = 11, // Red and White Tower
+ ICON_POI_REDTOMB = 12, // Red Tomb Stone
+ ICON_POI_RWTOMB = 13, // Red and White Tomb Stone
+ ICON_POI_BLUETOMB = 14, // Blue Tomb Stone
+ ICON_POI_NOTHING = 15, // NOTHING
+ ICON_POI_16 = 16, // Red ?
+ ICON_POI_17 = 17, // Grey ?
+ ICON_POI_18 = 18, // Blue ?
+ ICON_POI_19 = 19, // Red and White ?
+ ICON_POI_20 = 20, // Red ?
+ ICON_POI_GREYLOGS = 21, // Grey Wood Logs
+ ICON_POI_BWLOGS = 22, // Blue and White Wood Logs
+ ICON_POI_BLUELOGS = 23, // Blue Wood Logs
+ ICON_POI_RWLOGS = 24, // Red and White Wood Logs
+ ICON_POI_REDLOGS = 25, // Red Wood Logs
+ ICON_POI_26 = 26, // Grey ?
+ ICON_POI_27 = 27, // Blue and White ?
+ ICON_POI_28 = 28, // Blue ?
+ ICON_POI_29 = 29, // Red and White ?
+ ICON_POI_30 = 30, // Red ?
+ ICON_POI_GREYHOUSE = 31, // Grey House
+ ICON_POI_BWHOUSE = 32, // Blue and White House
+ ICON_POI_BLUEHOUSE = 33, // Blue House
+ ICON_POI_RWHOUSE = 34, // Red and White House
+ ICON_POI_REDHOUSE = 35, // Red House
+ ICON_POI_GREYHORSE = 36, // Grey Horse
+ ICON_POI_BWHORSE = 37, // Blue and White Horse
+ ICON_POI_BLUEHORSE = 38, // Blue Horse
+ ICON_POI_RWHORSE = 39, // Red and White Horse
+ ICON_POI_REDHORSE = 40 // Red Horse
+};
+
+struct GossipMenuItem
+{
+ uint8 m_gIcon;
+ bool m_gCoded;
+ std::string m_gMessage;
+ uint32 m_gSender;
+ uint32 m_gAction;
+ std::string m_gBoxMessage;
+ uint32 m_gBoxMoney;
+};
+
+typedef std::vector<GossipMenuItem> GossipMenuItemList;
+
+struct QuestMenuItem
+{
+ uint32 m_qId;
+ uint8 m_qIcon;
+};
+
+typedef std::vector<QuestMenuItem> QuestMenuItemList;
+
+class MANGOS_DLL_SPEC GossipMenu
+{
+ public:
+ GossipMenu();
+ ~GossipMenu();
+
+ void AddMenuItem(uint8 Icon, std::string Message, bool Coded = false);
+ void AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded = false);
+
+ // for using from scripts, don't must be inlined
+ void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
+ void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
+
+ unsigned int MenuItemCount()
+ {
+ return m_gItems.size();
+ }
+
+ GossipMenuItem const& GetItem( unsigned int Id )
+ {
+ return m_gItems[ Id ];
+ }
+
+ uint32 MenuItemSender( unsigned int ItemId );
+ uint32 MenuItemAction( unsigned int ItemId );
+ bool MenuItemCoded( unsigned int ItemId );
+
+ void ClearMenu();
+
+ protected:
+ GossipMenuItemList m_gItems;
+};
+
+class QuestMenu
+{
+ public:
+ QuestMenu();
+ ~QuestMenu();
+
+ void AddMenuItem( uint32 QuestId, uint8 Icon);
+ void ClearMenu();
+
+ uint8 MenuItemCount()
+ {
+ return m_qItems.size();
+ }
+ bool HasItem( uint32 questid );
+
+ QuestMenuItem const& GetItem( uint16 Id )
+ {
+ return m_qItems[ Id ];
+ }
+
+ protected:
+ QuestMenuItemList m_qItems;
+};
+
+class MANGOS_DLL_SPEC PlayerMenu
+{
+ private:
+ GossipMenu* pGossipMenu;
+ QuestMenu* pQuestMenu;
+ WorldSession* pSession;
+
+ public:
+ PlayerMenu( WorldSession *Session );
+ ~PlayerMenu();
+
+ GossipMenu* GetGossipMenu() { return pGossipMenu; }
+ QuestMenu* GetQuestMenu() { return pQuestMenu; }
+
+ void ClearMenus();
+ uint32 GossipOptionSender( unsigned int Selection );
+ uint32 GossipOptionAction( unsigned int Selection );
+ bool GossipOptionCoded( unsigned int Selection );
+
+ void SendGossipMenu( uint32 TitleTextId, uint64 npcGUID );
+ void CloseGossip();
+ void SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char * locName );
+ void SendTalking( uint32 textID );
+ void SendTalking( char const * title, char const * text );
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+ void SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID );
+
+ void SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID );
+
+ void SendQuestQueryResponse ( Quest const *pQuest );
+ void SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept);
+
+ void SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext );
+ void SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel );
+};
+#endif
diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h
new file mode 100644
index 00000000000..d6287fa495c
--- /dev/null
+++ b/src/game/GridDefines.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GRIDDEFINES_H
+#define MANGOS_GRIDDEFINES_H
+
+#include "Common.h"
+#include "GameSystem/NGrid.h"
+#include <cmath>
+
+// Forward class definitions
+class Corpse;
+class Creature;
+class DynamicObject;
+class GameObject;
+class Pet;
+class Player;
+
+#define MAX_NUMBER_OF_GRIDS 64
+
+#define SIZE_OF_GRIDS 533.33333f
+#define CENTER_GRID_ID (MAX_NUMBER_OF_GRIDS/2)
+
+#define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2)
+
+#define MIN_GRID_DELAY MINUTE*1000
+#define MIN_MAP_UPDATE_DELAY 50
+
+#define MAX_NUMBER_OF_CELLS 8
+#define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS)
+
+#define CENTER_GRID_CELL_ID 256
+#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)
+
+#define MAP_RESOLUTION 256
+
+#define MAP_SIZE (SIZE_OF_GRIDS*MAX_NUMBER_OF_GRIDS)
+#define MAP_HALFSIZE (MAP_SIZE/2)
+
+// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case)
+typedef TYPELIST_3(Player, Creature/*pets*/, Corpse/*resurrectable*/) AllWorldObjectTypes;
+typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes;
+
+typedef GridRefManager<Corpse> CorpseMapType;
+typedef GridRefManager<Creature> CreatureMapType;
+typedef GridRefManager<DynamicObject> DynamicObjectMapType;
+typedef GridRefManager<GameObject> GameObjectMapType;
+typedef GridRefManager<Player> PlayerMapType;
+
+typedef Grid<Player, AllWorldObjectTypes,AllGridObjectTypes> GridType;
+typedef NGrid<8, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType;
+
+typedef TypeMapContainer<AllGridObjectTypes> GridTypeMapContainer;
+typedef TypeMapContainer<AllWorldObjectTypes> WorldTypeMapContainer;
+
+template<const unsigned int LIMIT>
+struct MANGOS_DLL_DECL CoordPair
+{
+ CoordPair(uint32 x=0, uint32 y=0) : x_coord(x), y_coord(y) {}
+ CoordPair(const CoordPair<LIMIT> &obj) : x_coord(obj.x_coord), y_coord(obj.y_coord) {}
+ bool operator==(const CoordPair<LIMIT> &obj) const { return (obj.x_coord == x_coord && obj.y_coord == y_coord); }
+ bool operator!=(const CoordPair<LIMIT> &obj) const { return !operator==(obj); }
+ CoordPair<LIMIT>& operator=(const CoordPair<LIMIT> &obj)
+ {
+ x_coord = obj.x_coord;
+ y_coord = obj.y_coord;
+ return *this;
+ }
+
+ void operator<<(const uint32 val)
+ {
+ if( x_coord >= val )
+ x_coord -= val;
+ }
+
+ void operator>>(const uint32 val)
+ {
+ if( x_coord+val < LIMIT )
+ x_coord += val;
+ }
+
+ void operator-=(const uint32 val)
+ {
+ if( y_coord >= val )
+ y_coord -= val;
+ }
+
+ void operator+=(const uint32 val)
+ {
+ if( y_coord+val < LIMIT )
+ y_coord += val;
+ }
+
+ uint32 x_coord;
+ uint32 y_coord;
+};
+
+typedef CoordPair<MAX_NUMBER_OF_GRIDS> GridPair;
+typedef CoordPair<TOTAL_NUMBER_OF_CELLS_PER_MAP> CellPair;
+
+namespace MaNGOS
+{
+ template<class RET_TYPE, int CENTER_VAL>
+ inline RET_TYPE Compute(float x, float y, float center_offset, float size)
+ {
+ // calculate and store temporary values in double format for having same result as same mySQL calculations
+ double x_offset = (double(x) - center_offset)/size;
+ double y_offset = (double(y) - center_offset)/size;
+
+ int x_val = int(x_offset+CENTER_VAL + 0.5);
+ int y_val = int(y_offset+CENTER_VAL + 0.5);
+ return RET_TYPE(x_val, y_val);
+ }
+
+ inline GridPair ComputeGridPair(float x, float y)
+ {
+ return Compute<GridPair, CENTER_GRID_ID>(x, y, CENTER_GRID_OFFSET, SIZE_OF_GRIDS);
+ }
+
+ inline CellPair ComputeCellPair(float x, float y)
+ {
+ return Compute<CellPair, CENTER_GRID_CELL_ID>(x, y, CENTER_GRID_CELL_OFFSET, SIZE_OF_GRID_CELL);
+ }
+
+ inline void NormalizeMapCoord(float &c)
+ {
+ if(c > MAP_HALFSIZE - 0.5)
+ c = MAP_HALFSIZE - 0.5;
+ else if(c < -(MAP_HALFSIZE - 0.5))
+ c = -(MAP_HALFSIZE - 0.5);
+ }
+
+ inline bool IsValidMapCoord(float c)
+ {
+ return finite(c) && (std::fabs(c) <= MAP_HALFSIZE - 0.5);
+ }
+
+ inline bool IsValidMapCoord(float x, float y)
+ {
+ return IsValidMapCoord(x) && IsValidMapCoord(y);
+ }
+
+ inline bool IsValidMapCoord(float x, float y, float z)
+ {
+ return IsValidMapCoord(x,y) && finite(z);
+ }
+
+ inline bool IsValidMapCoord(float x, float y, float z, float o)
+ {
+ return IsValidMapCoord(x,y,z) && finite(o);
+ }
+}
+#endif
diff --git a/src/game/GridNotifiers.cpp b/src/game/GridNotifiers.cpp
new file mode 100644
index 00000000000..5348b7f61b0
--- /dev/null
+++ b/src/game/GridNotifiers.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "GridNotifiers.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "UpdateData.h"
+#include "Item.h"
+#include "Map.h"
+#include "MapManager.h"
+#include "Transports.h"
+#include "ObjectAccessor.h"
+
+using namespace MaNGOS;
+
+void
+MaNGOS::PlayerNotifier::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( iter->getSource() == &i_player )
+ continue;
+
+ iter->getSource()->UpdateVisibilityOf(&i_player);
+ i_player.UpdateVisibilityOf(iter->getSource());
+ }
+}
+
+void
+VisibleChangesNotifier::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if(iter->getSource() == &i_object)
+ continue;
+
+ iter->getSource()->UpdateVisibilityOf(&i_object);
+ }
+}
+
+void
+VisibleNotifier::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( iter->getSource() == &i_player )
+ continue;
+
+ iter->getSource()->UpdateVisibilityOf(&i_player);
+ i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_data_updates,i_visibleNow);
+ i_clientGUIDs.erase(iter->getSource()->GetGUID());
+ }
+}
+
+void
+VisibleNotifier::Notify()
+{
+ // at this moment i_clientGUIDs have guids that not iterate at grid level checks
+ // but exist one case when this possible and object not out of range: transports
+ if(Transport* transport = i_player.GetTransport())
+ {
+ for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
+ {
+ if(i_clientGUIDs.find((*itr)->GetGUID())!=i_clientGUIDs.end())
+ {
+ (*itr)->UpdateVisibilityOf(&i_player);
+ i_player.UpdateVisibilityOf((*itr),i_data,i_data_updates,i_visibleNow);
+ i_clientGUIDs.erase((*itr)->GetGUID());
+ }
+ }
+ }
+
+ // generate outOfRange for not iterate objects
+ i_data.AddOutOfRangeGUID(i_clientGUIDs);
+ for(Player::ClientGUIDs::iterator itr = i_clientGUIDs.begin();itr!=i_clientGUIDs.end();++itr)
+ {
+ i_player.m_clientGUIDs.erase(*itr);
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) is out of range (no in active cells set) now for player %u",GUID_LOPART(*itr),GuidHigh2TypeId(GUID_HIPART(*itr)),i_player.GetGUIDLow());
+ #endif
+ }
+
+ // send update to other players (except player updates that already sent using SendUpdateToPlayer)
+ for(UpdateDataMapType::iterator iter = i_data_updates.begin(); iter != i_data_updates.end(); ++iter)
+ {
+ if(iter->first==&i_player)
+ continue;
+
+ WorldPacket packet;
+ iter->second.BuildPacket(&packet);
+ iter->first->GetSession()->SendPacket(&packet);
+ }
+
+ if( i_data.HasData() )
+ {
+ // send create/outofrange packet to player (except player create updates that already sent using SendUpdateToPlayer)
+ WorldPacket packet;
+ i_data.BuildPacket(&packet);
+ i_player.GetSession()->SendPacket(&packet);
+
+ // send out of range to other players if need
+ std::set<uint64> const& oor = i_data.GetOutOfRangeGUIDs();
+ for(std::set<uint64>::const_iterator iter = oor.begin(); iter != oor.end(); ++iter)
+ {
+ if(!IS_PLAYER_GUID(*iter))
+ continue;
+
+ Player* plr = ObjectAccessor::GetPlayer(i_player,*iter);
+ if(plr)
+ plr->UpdateVisibilityOf(&i_player);
+ }
+ }
+
+ // Now do operations that required done at object visibility change to visible
+
+ // target aura duration for caster show only if target exist at caster client
+ // send data at target visibility change (adding to client)
+ for(std::set<WorldObject*>::const_iterator vItr = i_visibleNow.begin(); vItr != i_visibleNow.end(); ++vItr)
+ if((*vItr)!=&i_player && (*vItr)->isType(TYPEMASK_UNIT))
+ i_player.SendAuraDurationsForTarget((Unit*)(*vItr));
+}
+
+void
+MessageDeliverer::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( i_toSelf || iter->getSource() != &i_player)
+ {
+ if(WorldSession* session = iter->getSource()->GetSession())
+ session->SendPacket(i_message);
+ }
+ }
+}
+
+void
+ObjectMessageDeliverer::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if(WorldSession* session = iter->getSource()->GetSession())
+ session->SendPacket(i_message);
+ }
+}
+
+void
+MessageDistDeliverer::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( (i_toSelf || iter->getSource() != &i_player ) &&
+ (!i_ownTeamOnly || iter->getSource()->GetTeam() == i_player.GetTeam() ) &&
+ (!i_dist || iter->getSource()->GetDistance(&i_player) <= i_dist) )
+ {
+ if(WorldSession* session = iter->getSource()->GetSession())
+ session->SendPacket(i_message);
+ }
+ }
+}
+
+void
+ObjectMessageDistDeliverer::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( !i_dist || iter->getSource()->GetDistance(&i_object) <= i_dist )
+ {
+ if(WorldSession* session = iter->getSource()->GetSession())
+ session->SendPacket(i_message);
+ }
+ }
+}
+
+template<class T> void
+ObjectUpdater::Visit(GridRefManager<T> &m)
+{
+ for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
+ {
+ iter->getSource()->Update(i_timeDiff);
+ }
+}
+
+template void ObjectUpdater::Visit<GameObject>(GameObjectMapType &);
+template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType &);
+
+bool CannibalizeObjectCheck::operator()(Corpse* u)
+{
+ // ignore bones
+ if(u->GetType()==CORPSE_BONES)
+ return false;
+
+ Player* owner = ObjectAccessor::FindPlayer(u->GetOwnerGUID());
+
+ if( !owner || i_funit->IsFriendlyTo(owner))
+ return false;
+
+ if(i_funit->IsWithinDistInMap(u, i_range) )
+ return true;
+
+ return false;
+}
diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h
new file mode 100644
index 00000000000..215cf9152e6
--- /dev/null
+++ b/src/game/GridNotifiers.h
@@ -0,0 +1,812 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GRIDNOTIFIERS_H
+#define MANGOS_GRIDNOTIFIERS_H
+
+#include "ObjectGridLoader.h"
+#include "ByteBuffer.h"
+#include "UpdateData.h"
+#include <iostream>
+
+#include "Corpse.h"
+#include "Object.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
+#include "Player.h"
+#include "Unit.h"
+
+class Player;
+//class Map;
+
+namespace MaNGOS
+{
+
+ struct MANGOS_DLL_DECL PlayerNotifier
+ {
+ explicit PlayerNotifier(Player &pl) : i_player(pl) {}
+ void Visit(PlayerMapType &);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ Player &i_player;
+ };
+
+ struct MANGOS_DLL_DECL VisibleNotifier
+ {
+ Player &i_player;
+ UpdateData i_data;
+ UpdateDataMapType i_data_updates;
+ Player::ClientGUIDs i_clientGUIDs;
+ std::set<WorldObject*> i_visibleNow;
+
+ explicit VisibleNotifier(Player &player) : i_player(player),i_clientGUIDs(player.m_clientGUIDs) {}
+ template<class T> void Visit(GridRefManager<T> &m);
+ void Visit(PlayerMapType &);
+ void Notify(void);
+ };
+
+ struct MANGOS_DLL_DECL VisibleChangesNotifier
+ {
+ WorldObject &i_object;
+
+ explicit VisibleChangesNotifier(WorldObject &object) : i_object(object) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ void Visit(PlayerMapType &);
+ };
+
+ struct MANGOS_DLL_DECL GridUpdater
+ {
+ GridType &i_grid;
+ uint32 i_timeDiff;
+ GridUpdater(GridType &grid, uint32 diff) : i_grid(grid), i_timeDiff(diff) {}
+
+ template<class T> void updateObjects(GridRefManager<T> &m)
+ {
+ for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
+ iter->getSource()->Update(i_timeDiff);
+ }
+
+ void Visit(PlayerMapType &m) { updateObjects<Player>(m); }
+ void Visit(CreatureMapType &m){ updateObjects<Creature>(m); }
+ void Visit(GameObjectMapType &m) { updateObjects<GameObject>(m); }
+ void Visit(DynamicObjectMapType &m) { updateObjects<DynamicObject>(m); }
+ void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); }
+ };
+
+ struct MANGOS_DLL_DECL MessageDeliverer
+ {
+ Player &i_player;
+ WorldPacket *i_message;
+ bool i_toSelf;
+ MessageDeliverer(Player &pl, WorldPacket *msg, bool to_self) : i_player(pl), i_message(msg), i_toSelf(to_self) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectMessageDeliverer
+ {
+ WorldPacket *i_message;
+ explicit ObjectMessageDeliverer(WorldPacket *msg) : i_message(msg) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL MessageDistDeliverer
+ {
+ Player &i_player;
+ WorldPacket *i_message;
+ bool i_toSelf;
+ bool i_ownTeamOnly;
+ float i_dist;
+ MessageDistDeliverer(Player &pl, WorldPacket *msg, bool to_self, bool ownTeamOnly, float dist) : i_player(pl), i_message(msg), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly), i_dist(dist) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectMessageDistDeliverer
+ {
+ WorldObject &i_object;
+ WorldPacket *i_message;
+ float i_dist;
+ ObjectMessageDistDeliverer(WorldObject &obj, WorldPacket *msg, float dist) : i_object(obj), i_message(msg), i_dist(dist) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectUpdater
+ {
+ uint32 i_timeDiff;
+ explicit ObjectUpdater(const uint32 &diff) : i_timeDiff(diff) {}
+ template<class T> void Visit(GridRefManager<T> &m);
+ void Visit(PlayerMapType &) {}
+ void Visit(CorpseMapType &) {}
+ void Visit(CreatureMapType &);
+ };
+
+ template<class T>
+ struct MANGOS_DLL_DECL ObjectAccessorNotifier
+ {
+ T *& i_object;
+
+ uint64 i_id;
+ ObjectAccessorNotifier(T * &obj, uint64 id) : i_object(obj), i_id(id)
+ {
+ i_object = NULL;
+ }
+
+ void Visit(GridRefManager<T> &m )
+ {
+ if( i_object == NULL )
+ {
+ GridRefManager<T> *iter = m.find(i_id);
+ if( iter != m.end() )
+ {
+ assert( iter->second != NULL );
+ i_object = iter->second;
+ }
+ }
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ struct MANGOS_DLL_DECL PlayerRelocationNotifier
+ {
+ Player &i_player;
+ PlayerRelocationNotifier(Player &pl) : i_player(pl) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ void Visit(PlayerMapType &);
+ void Visit(CreatureMapType &);
+ };
+
+ struct MANGOS_DLL_DECL CreatureRelocationNotifier
+ {
+ Creature &i_creature;
+ CreatureRelocationNotifier(Creature &c) : i_creature(c) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ #ifdef WIN32
+ template<> void Visit(PlayerMapType &);
+ #endif
+ };
+
+ struct MANGOS_DLL_DECL DynamicObjectUpdater
+ {
+ DynamicObject &i_dynobject;
+ Unit* i_check;
+ DynamicObjectUpdater(DynamicObject &dynobject, Unit* caster) : i_dynobject(dynobject)
+ {
+ i_check = caster;
+ Unit* owner = i_check->GetOwner();
+ if(owner)
+ i_check = owner;
+ }
+
+ template<class T> inline void Visit(GridRefManager<T> &) {}
+ #ifdef WIN32
+ template<> inline void Visit<Player>(PlayerMapType &);
+ template<> inline void Visit<Creature>(CreatureMapType &);
+ #endif
+
+ void VisitHelper(Unit* target);
+ };
+
+ // SEARCHERS & LIST SEARCHERS & WORKERS
+
+ // WorldObject searchers & workers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL WorldObjectSearcher
+ {
+ WorldObject* &i_object;
+ Check &i_check;
+
+ WorldObjectSearcher(WorldObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+ void Visit(CorpseMapType &m);
+ void Visit(DynamicObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL WorldObjectListSearcher
+ {
+ std::list<WorldObject*> &i_objects;
+ Check& i_check;
+
+ WorldObjectListSearcher(std::list<WorldObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+ void Visit(CorpseMapType &m);
+ void Visit(GameObjectMapType &m);
+ void Visit(DynamicObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Do>
+ struct MANGOS_DLL_DECL WorldObjectWorker
+ {
+ Do const& i_do;
+
+ explicit WorldObjectWorker(Do const& _do) : i_do(_do) {}
+
+ void Visit(GameObjectMapType &m)
+ {
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(PlayerMapType &m)
+ {
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+ void Visit(CreatureMapType &m)
+ {
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(CorpseMapType &m)
+ {
+ for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(DynamicObjectMapType &m)
+ {
+ for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Gameobject searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectSearcher
+ {
+ GameObject* &i_object;
+ Check &i_check;
+
+ GameObjectSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check GO if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectLastSearcher
+ {
+ GameObject* &i_object;
+ Check& i_check;
+
+ GameObjectLastSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectListSearcher
+ {
+ std::list<GameObject*> &i_objects;
+ Check& i_check;
+
+ GameObjectListSearcher(std::list<GameObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Unit searchers
+
+ // First accepted by Check Unit if any
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitSearcher
+ {
+ Unit* &i_object;
+ Check & i_check;
+
+ UnitSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check Unit if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitLastSearcher
+ {
+ Unit* &i_object;
+ Check & i_check;
+
+ UnitLastSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // All accepted by Check units if any
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitListSearcher
+ {
+ std::list<Unit*> &i_objects;
+ Check& i_check;
+
+ UnitListSearcher(std::list<Unit*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Creature searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureSearcher
+ {
+ Creature* &i_object;
+ Check & i_check;
+
+ CreatureSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check Creature if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureLastSearcher
+ {
+ Creature* &i_object;
+ Check & i_check;
+
+ CreatureLastSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureListSearcher
+ {
+ std::list<Creature*> &i_objects;
+ Check& i_check;
+
+ CreatureListSearcher(std::list<Creature*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Player searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL PlayerSearcher
+ {
+ Player* &i_object;
+ Check & i_check;
+
+ PlayerSearcher(Player* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Do>
+ struct MANGOS_DLL_DECL PlayerWorker
+ {
+ Do& i_do;
+
+ explicit PlayerWorker(Do& _do) : i_do(_do) {}
+
+ void Visit(PlayerMapType &m)
+ {
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // CHECKS && DO classes
+
+ // WorldObject check classes
+ class CannibalizeObjectCheck
+ {
+ public:
+ CannibalizeObjectCheck(Unit* funit, float range) : i_funit(funit), i_range(range) {}
+ bool operator()(Player* u)
+ {
+ if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() )
+ return false;
+
+ if(i_funit->IsWithinDistInMap(u, i_range) )
+ return true;
+
+ return false;
+ }
+ bool operator()(Corpse* u);
+ bool operator()(Creature* u)
+ {
+ if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() ||
+ (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD)==0)
+ return false;
+
+ if(i_funit->IsWithinDistInMap(u, i_range) )
+ return true;
+
+ return false;
+ }
+ template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED* u) { return false; }
+ private:
+ Unit* const i_funit;
+ float i_range;
+ };
+
+ // WorldObject do classes
+
+ class RespawnDo
+ {
+ public:
+ RespawnDo() {}
+ void operator()(Creature* u) const { u->Respawn(); }
+ void operator()(GameObject* u) const { u->Respawn(); }
+ void operator()(WorldObject*) const {}
+ void operator()(Corpse*) const {}
+ };
+
+ // GameObject checks
+
+ class GameObjectFocusCheck
+ {
+ public:
+ GameObjectFocusCheck(Unit const* unit,uint32 focusId) : i_unit(unit), i_focusId(focusId) {}
+ bool operator()(GameObject* go) const
+ {
+ if(go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS)
+ return false;
+
+ if(go->GetGOInfo()->spellFocus.focusId != i_focusId)
+ return false;
+
+ float dist = go->GetGOInfo()->spellFocus.dist;
+
+ return go->IsWithinDistInMap(i_unit, dist);
+ }
+ private:
+ Unit const* i_unit;
+ uint32 i_focusId;
+ };
+
+ // Find the nearest Fishing hole and return true only if source object is in range of hole
+ class NearestGameObjectFishingHole
+ {
+ public:
+ NearestGameObjectFishingHole(WorldObject const& obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(GameObject* go)
+ {
+ if(go->GetGOInfo()->type == GAMEOBJECT_TYPE_FISHINGHOLE && go->isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, go->GetGOInfo()->fishinghole.radius))
+ {
+ i_range = i_obj.GetDistance(go);
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ float i_range;
+
+ // prevent clone
+ NearestGameObjectFishingHole(NearestGameObjectFishingHole const&);
+ };
+
+ // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
+ class NearestGameObjectEntryInObjectRangeCheck
+ {
+ public:
+ NearestGameObjectEntryInObjectRangeCheck(WorldObject const& obj,uint32 entry, float range) : i_obj(obj), i_entry(entry), i_range(range) {}
+ bool operator()(GameObject* go)
+ {
+ if(go->GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range))
+ {
+ i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_entry;
+ float i_range;
+
+ // prevent clone this object
+ NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&);
+ };
+
+ class GameObjectWithDbGUIDCheck
+ {
+ public:
+ GameObjectWithDbGUIDCheck(WorldObject const& obj,uint32 db_guid) : i_obj(obj), i_db_guid(db_guid) {}
+ bool operator()(GameObject const* go) const
+ {
+ return go->GetDBTableGUIDLow() == i_db_guid;
+ }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_db_guid;
+ };
+
+ // Unit checks
+
+ class AnyUnfriendlyUnitInObjectRangeCheck
+ {
+ public:
+ AnyUnfriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u))
+ return true;
+ else
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ class AnyFriendlyUnitInObjectRangeCheck
+ {
+ public:
+ AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && i_funit->IsFriendlyTo(u))
+ return true;
+ else
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ class AnyUnitInObjectRangeCheck
+ {
+ public:
+ AnyUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ float i_range;
+ };
+
+ // Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
+ class NearestAttackableUnitInObjectRangeCheck
+ {
+ public:
+ NearestAttackableUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if( u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) &&
+ !i_funit->IsFriendlyTo(u) && u->isVisibleForOrDetect(i_funit,false) )
+ {
+ i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
+ return true;
+ }
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+
+ // prevent clone this object
+ NearestAttackableUnitInObjectRangeCheck(NearestAttackableUnitInObjectRangeCheck const&);
+ };
+
+ class AnyAoETargetUnitInObjectRangeCheck
+ {
+ public:
+ AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range)
+ : i_obj(obj), i_funit(funit), i_range(range)
+ {
+ Unit const* check = i_funit;
+ Unit const* owner = i_funit->GetOwner();
+ if(owner)
+ check = owner;
+ i_targetForPlayer = ( check->GetTypeId()==TYPEID_PLAYER );
+ }
+ bool operator()(Unit* u)
+ {
+ // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems
+ if (!u->isTargetableForAttack())
+ return false;
+ if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isTotem())
+ return false;
+
+ if(( i_targetForPlayer ? !i_funit->IsFriendlyTo(u) : i_funit->IsHostileTo(u) )&& i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ bool i_targetForPlayer;
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ struct AnyDeadUnitCheck
+ {
+ bool operator()(Unit* u) { return !u->isAlive(); }
+ };
+
+ struct AnyStealthedCheck
+ {
+ bool operator()(Unit* u) { return u->GetVisibility()==VISIBILITY_GROUP_STEALTH; }
+ };
+
+ // Creature checks
+
+ class InAttackDistanceFromAnyHostileCreatureCheck
+ {
+ public:
+ explicit InAttackDistanceFromAnyHostileCreatureCheck(Unit* funit) : i_funit(funit) {}
+ bool operator()(Creature* u)
+ {
+ if(u->isAlive() && u->IsHostileTo(i_funit) && i_funit->IsWithinDistInMap(u, u->GetAttackDistance(i_funit)))
+ return true;
+
+ return false;
+ }
+ private:
+ Unit* const i_funit;
+ };
+
+ class AnyAssistCreatureInRangeCheck
+ {
+ public:
+ AnyAssistCreatureInRangeCheck(Unit* funit, Unit* enemy, float range)
+ : i_funit(funit), i_enemy(enemy), i_range(range)
+ {
+ }
+ bool operator()(Creature* u)
+ {
+ if(u == i_funit)
+ return false;
+
+ // we don't need help from zombies :)
+ if( !u->isAlive() )
+ return false;
+
+ // skip fighting creature
+ if( u->isInCombat() )
+ return false;
+
+ // only from same creature faction
+ if(u->getFaction() != i_funit->getFaction() )
+ return false;
+
+ // only free creature
+ if( u->GetCharmerOrOwnerGUID() )
+ return false;
+
+ // too far
+ if( !i_funit->IsWithinDistInMap(u, i_range) )
+ return false;
+
+ // skip non hostile to caster enemy creatures
+ if( !u->IsHostileTo(i_enemy) )
+ return false;
+
+ // only if see assisted creature
+ if(!u->IsWithinLOSInMap(i_funit) )
+ return false;
+
+ return true;
+ }
+ private:
+ Unit* const i_funit;
+ Unit* const i_enemy;
+ float i_range;
+ };
+
+ // Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature)
+ class NearestCreatureEntryWithLiveStateInObjectRangeCheck
+ {
+ public:
+ NearestCreatureEntryWithLiveStateInObjectRangeCheck(WorldObject const& obj,uint32 entry, bool alive, float range)
+ : i_obj(obj), i_entry(entry), i_alive(alive), i_range(range) {}
+
+ bool operator()(Creature* u)
+ {
+ if(u->GetEntry() == i_entry && u->isAlive()==i_alive && i_obj.IsWithinDistInMap(u, i_range))
+ {
+ i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_entry;
+ bool i_alive;
+ float i_range;
+
+ // prevent clone this object
+ NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&);
+ };
+
+ class AnyPlayerInObjectRangeCheck
+ {
+ public:
+ AnyPlayerInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(Player* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ float i_range;
+ };
+
+ #ifndef WIN32
+ template<> void PlayerRelocationNotifier::Visit<Creature>(CreatureMapType &);
+ template<> void PlayerRelocationNotifier::Visit<Player>(PlayerMapType &);
+ template<> void CreatureRelocationNotifier::Visit<Player>(PlayerMapType &);
+ template<> void CreatureRelocationNotifier::Visit<Creature>(CreatureMapType &);
+ template<> inline void DynamicObjectUpdater::Visit<Creature>(CreatureMapType &);
+ template<> inline void DynamicObjectUpdater::Visit<Player>(PlayerMapType &);
+ #endif
+}
+#endif
diff --git a/src/game/GridNotifiersImpl.h b/src/game/GridNotifiersImpl.h
new file mode 100644
index 00000000000..9dd422a3ad3
--- /dev/null
+++ b/src/game/GridNotifiersImpl.h
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GRIDNOTIFIERSIMPL_H
+#define MANGOS_GRIDNOTIFIERSIMPL_H
+
+#include "GridNotifiers.h"
+#include "WorldPacket.h"
+#include "Corpse.h"
+#include "Player.h"
+#include "UpdateData.h"
+#include "CreatureAI.h"
+#include "SpellAuras.h"
+
+template<class T>
+inline void
+MaNGOS::VisibleNotifier::Visit(GridRefManager<T> &m)
+{
+ for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
+ {
+ i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_data_updates,i_visibleNow);
+ i_clientGUIDs.erase(iter->getSource()->GetGUID());
+ }
+}
+
+inline void
+MaNGOS::ObjectUpdater::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ if(!iter->getSource()->isSpiritService())
+ iter->getSource()->Update(i_timeDiff);
+}
+
+inline void
+MaNGOS::PlayerRelocationNotifier::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if(&i_player==iter->getSource())
+ continue;
+
+ // visibility for players updated by ObjectAccessor::UpdateVisibilityFor calls in appropriate places
+
+ // Cancel Trade
+ if(i_player.GetTrader()==iter->getSource())
+ // iteraction distance
+ if(!i_player.IsWithinDistInMap(iter->getSource(), 5))
+ i_player.GetSession()->SendCancelTrade(); // will clode both side trade windows
+ }
+}
+
+inline void PlayerCreatureRelocationWorker(Player* pl, Creature* c)
+{
+ // update creature visibility at player/creature move
+ pl->UpdateVisibilityOf(c);
+
+ // Creature AI reaction
+ if(!c->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING))
+ {
+ if( c->AI() && c->AI()->IsVisible(pl) && !c->IsInEvadeMode() )
+ c->AI()->MoveInLineOfSight(pl);
+ }
+}
+
+inline void CreatureCreatureRelocationWorker(Creature* c1, Creature* c2)
+{
+ if(!c1->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING))
+ {
+ if( c1->AI() && c1->AI()->IsVisible(c2) && !c1->IsInEvadeMode() )
+ c1->AI()->MoveInLineOfSight(c2);
+ }
+
+ if(!c2->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING))
+ {
+ if( c2->AI() && c2->AI()->IsVisible(c1) && !c2->IsInEvadeMode() )
+ c2->AI()->MoveInLineOfSight(c1);
+ }
+}
+
+inline void
+MaNGOS::PlayerRelocationNotifier::Visit(CreatureMapType &m)
+{
+ if(!i_player.isAlive() || i_player.isInFlight())
+ return;
+
+ for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ if( iter->getSource()->isAlive())
+ PlayerCreatureRelocationWorker(&i_player,iter->getSource());
+}
+
+template<>
+inline void
+MaNGOS::CreatureRelocationNotifier::Visit(PlayerMapType &m)
+{
+ if(!i_creature.isAlive())
+ return;
+
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ if( iter->getSource()->isAlive() && !iter->getSource()->isInFlight())
+ PlayerCreatureRelocationWorker(iter->getSource(), &i_creature);
+}
+
+template<>
+inline void
+MaNGOS::CreatureRelocationNotifier::Visit(CreatureMapType &m)
+{
+ if(!i_creature.isAlive())
+ return;
+
+ for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ Creature* c = iter->getSource();
+ if( c != &i_creature && c->isAlive())
+ CreatureCreatureRelocationWorker(c, &i_creature);
+ }
+}
+
+inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target)
+{
+ if(!target->isAlive() || target->isInFlight() )
+ return;
+
+ if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isTotem())
+ return;
+
+ if (!i_dynobject.IsWithinDistInMap(target, i_dynobject.GetRadius()))
+ return;
+
+ //Check targets for not_selectable unit flag and remove
+ if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
+ return;
+
+ // Evade target
+ if( target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->IsInEvadeMode() )
+ return;
+
+ //Check player targets and remove if in GM mode or GM invisibility (for not self casting case)
+ if( target->GetTypeId()==TYPEID_PLAYER && target != i_check && (((Player*)target)->isGameMaster() || ((Player*)target)->GetVisibility()==VISIBILITY_OFF) )
+ return;
+
+ if( i_check->GetTypeId()==TYPEID_PLAYER )
+ {
+ if (i_check->IsFriendlyTo( target ))
+ return;
+ }
+ else
+ {
+ if (!i_check->IsHostileTo( target ))
+ return;
+ }
+
+ if (i_dynobject.IsAffecting(target))
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_dynobject.GetSpellId());
+ uint32 eff_index = i_dynobject.GetEffIndex();
+ // Check target immune to spell or aura
+ if (target->IsImmunedToSpell(spellInfo) || target->IsImmunedToSpellEffect(spellInfo->Effect[eff_index], spellInfo->EffectMechanic[eff_index]))
+ return;
+ // Apply PersistentAreaAura on target
+ PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, target, i_dynobject.GetCaster());
+ target->AddAura(Aur);
+ i_dynobject.AddAffected(target);
+}
+
+template<>
+inline void
+MaNGOS::DynamicObjectUpdater::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ VisitHelper(itr->getSource());
+}
+
+template<>
+inline void
+MaNGOS::DynamicObjectUpdater::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ VisitHelper(itr->getSource());
+}
+
+// SEARCHERS & LIST SEARCHERS & WORKERS
+
+// WorldObject searchers & workers
+
+template<class Check>
+void MaNGOS::WorldObjectSearcher<Check>::Visit(GameObjectMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::WorldObjectSearcher<Check>::Visit(PlayerMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::WorldObjectSearcher<Check>::Visit(CreatureMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::WorldObjectSearcher<Check>::Visit(CorpseMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::WorldObjectSearcher<Check>::Visit(DynamicObjectMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::WorldObjectListSearcher<Check>::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::WorldObjectListSearcher<Check>::Visit(CorpseMapType &m)
+{
+ for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::WorldObjectListSearcher<Check>::Visit(GameObjectMapType &m)
+{
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::WorldObjectListSearcher<Check>::Visit(DynamicObjectMapType &m)
+{
+ for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+// Gameobject searchers
+
+template<class Check>
+void MaNGOS::GameObjectSearcher<Check>::Visit(GameObjectMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::GameObjectLastSearcher<Check>::Visit(GameObjectMapType &m)
+{
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ i_object = itr->getSource();
+ }
+}
+
+template<class Check>
+void MaNGOS::GameObjectListSearcher<Check>::Visit(GameObjectMapType &m)
+{
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+// Unit searchers
+
+template<class Check>
+void MaNGOS::UnitSearcher<Check>::Visit(CreatureMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::UnitSearcher<Check>::Visit(PlayerMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::UnitLastSearcher<Check>::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ i_object = itr->getSource();
+ }
+}
+
+template<class Check>
+void MaNGOS::UnitLastSearcher<Check>::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ i_object = itr->getSource();
+ }
+}
+
+template<class Check>
+void MaNGOS::UnitListSearcher<Check>::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::UnitListSearcher<Check>::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+// Creature searchers
+
+template<class Check>
+void MaNGOS::CreatureSearcher<Check>::Visit(CreatureMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+template<class Check>
+void MaNGOS::CreatureLastSearcher<Check>::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ i_object = itr->getSource();
+ }
+}
+
+template<class Check>
+void MaNGOS::CreatureListSearcher<Check>::Visit(CreatureMapType &m)
+{
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ if(i_check(itr->getSource()))
+ i_objects.push_back(itr->getSource());
+}
+
+template<class Check>
+void MaNGOS::PlayerSearcher<Check>::Visit(PlayerMapType &m)
+{
+ // already found
+ if(i_object)
+ return;
+
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ if(i_check(itr->getSource()))
+ {
+ i_object = itr->getSource();
+ return;
+ }
+ }
+}
+
+#endif // MANGOS_GRIDNOTIFIERSIMPL_H
diff --git a/src/game/GridStates.cpp b/src/game/GridStates.cpp
new file mode 100644
index 00000000000..0c4557fb131
--- /dev/null
+++ b/src/game/GridStates.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "GridStates.h"
+#include "GridNotifiers.h"
+#include "ObjectAccessor.h"
+#include "GameSystem/Grid.h"
+#include "Log.h"
+
+void
+InvalidState::Update(Map &, NGridType &, GridInfo &, const uint32 &/*x*/, const uint32 &/*y*/, const uint32 &) const
+{
+}
+
+void
+ActiveState::Update(Map &m, NGridType &grid, GridInfo & info, const uint32 &x, const uint32 &y, const uint32 &t_diff) const
+{
+ // Only check grid activity every (grid_expiry/10) ms, because it's really useless to do it every cycle
+ info.UpdateTimeTracker(t_diff);
+ if( info.getTimeTracker().Passed() )
+ {
+ if( grid.ActiveObjectsInGrid() == 0 && !ObjectAccessor::Instance().PlayersNearGrid(x, y, m.GetId(), m.GetInstanceId()) )
+ {
+ ObjectGridStoper stoper(grid);
+ stoper.StopN();
+ grid.SetGridState(GRID_STATE_IDLE);
+ }
+ else
+ {
+ m.ResetGridExpiry(grid, 0.1f);
+ }
+ }
+}
+
+void
+IdleState::Update(Map &m, NGridType &grid, GridInfo &info, const uint32 &x, const uint32 &y, const uint32 &) const
+{
+ m.ResetGridExpiry(grid);
+ grid.SetGridState(GRID_STATE_REMOVAL);
+ sLog.outDebug("Grid[%u,%u] on map %u moved to IDLE state", x, y, m.GetId());
+}
+
+void
+RemovalState::Update(Map &m, NGridType &grid, GridInfo &info, const uint32 &x, const uint32 &y, const uint32 &t_diff) const
+{
+ if(info.getUnloadFlag())
+ {
+ info.UpdateTimeTracker(t_diff);
+ if( info.getTimeTracker().Passed() )
+ {
+ if( !m.UnloadGrid(x, y, false) )
+ {
+ sLog.outDebug("Grid[%u,%u] for map %u differed unloading due to players nearby", x, y, m.GetId());
+ m.ResetGridExpiry(grid);
+ }
+ }
+ }
+}
diff --git a/src/game/GridStates.h b/src/game/GridStates.h
new file mode 100644
index 00000000000..8a4e716ece4
--- /dev/null
+++ b/src/game/GridStates.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GRIDSTATES_H
+#define MANGOS_GRIDSTATES_H
+
+#include "Map.h"
+
+class MANGOS_DLL_DECL GridState
+{
+ public:
+#ifdef MANGOS_DEBUG
+#define MAGIC_TESTVAL 0xFBE823BA
+ GridState() { i_Magic = MAGIC_TESTVAL; }
+ bool checkMagic()
+ {
+ if(i_Magic != MAGIC_TESTVAL)
+ {
+ sLog.outError("!!! GridState: Magic value gone !!!");
+ return false;
+ }
+ return true;
+ }
+ void setMagic() { i_Magic = MAGIC_TESTVAL; }
+ unsigned int i_Magic;
+#endif
+ virtual void Update(Map &, NGridType&, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const = 0;
+};
+
+class MANGOS_DLL_DECL InvalidState : public GridState
+{
+ public:
+
+ void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
+};
+
+class MANGOS_DLL_DECL ActiveState : public GridState
+{
+ public:
+
+ void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
+};
+
+class MANGOS_DLL_DECL IdleState : public GridState
+{
+ public:
+
+ void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
+};
+
+class MANGOS_DLL_DECL RemovalState : public GridState
+{
+ public:
+
+ void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
+};
+#endif
diff --git a/src/game/Group.cpp b/src/game/Group.cpp
new file mode 100644
index 00000000000..e74a3778e87
--- /dev/null
+++ b/src/game/Group.cpp
@@ -0,0 +1,1406 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Group.h"
+#include "ObjectAccessor.h"
+#include "BattleGround.h"
+#include "MapManager.h"
+#include "InstanceSaveMgr.h"
+#include "MapInstanced.h"
+#include "Util.h"
+
+Group::Group()
+{
+ m_leaderGuid = 0;
+ m_mainTank = 0;
+ m_mainAssistant = 0;
+ m_groupType = (GroupType)0;
+ m_bgGroup = NULL;
+ m_lootMethod = (LootMethod)0;
+ m_looterGuid = 0;
+ m_lootThreshold = ITEM_QUALITY_UNCOMMON;
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = 0;
+}
+
+Group::~Group()
+{
+ if(m_bgGroup)
+ {
+ sLog.outDebug("Group::~Group: battleground group being deleted.");
+ if(m_bgGroup->GetBgRaid(ALLIANCE) == this) m_bgGroup->SetBgRaid(ALLIANCE, NULL);
+ else if(m_bgGroup->GetBgRaid(HORDE) == this) m_bgGroup->SetBgRaid(HORDE, NULL);
+ else sLog.outError("Group::~Group: battleground group is not linked to the correct battleground.");
+ }
+ Rolls::iterator itr;
+ while(!RollId.empty())
+ {
+ itr = RollId.begin();
+ Roll *r = *itr;
+ RollId.erase(itr);
+ delete(r);
+ }
+
+ // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
+ // will be unloaded first so we must be prepared for both cases
+ // this may unload some instance saves
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ itr->second.save->RemoveGroup(this);
+}
+
+bool Group::Create(const uint64 &guid, const char * name)
+{
+ m_leaderGuid = guid;
+ m_leaderName = name;
+
+ m_groupType = isBGGroup() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
+ m_lootMethod = GROUP_LOOT;
+ m_lootThreshold = ITEM_QUALITY_UNCOMMON;
+ m_looterGuid = guid;
+
+ m_difficulty = DIFFICULTY_NORMAL;
+ if(!isBGGroup())
+ {
+ Player *leader = objmgr.GetPlayer(guid);
+ if(leader) m_difficulty = leader->GetDifficulty();
+
+ Player::ConvertInstancesToGroup(leader, this, guid);
+
+ // store group in database
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("INSERT INTO groups(leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,isRaid,difficulty) "
+ "VALUES('%u','%u','%u','%u','%u','%u','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','" I64FMTD "','%u','%u')",
+ GUID_LOPART(m_leaderGuid), GUID_LOPART(m_mainTank), GUID_LOPART(m_mainAssistant), uint32(m_lootMethod),
+ GUID_LOPART(m_looterGuid), uint32(m_lootThreshold), m_targetIcons[0], m_targetIcons[1], m_targetIcons[2], m_targetIcons[3], m_targetIcons[4], m_targetIcons[5], m_targetIcons[6], m_targetIcons[7], isRaidGroup(), m_difficulty);
+ }
+
+ if(!AddMember(guid, name))
+ return false;
+
+ if(!isBGGroup()) CharacterDatabase.CommitTransaction();
+
+ return true;
+}
+
+bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool loadMembers)
+{
+ if(isBGGroup())
+ return false;
+
+ bool external = true;
+ if(!result)
+ {
+ external = false;
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty FROM groups WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
+ if(!result)
+ return false;
+ }
+
+ m_leaderGuid = leaderGuid;
+
+ // group leader not exist
+ if(!objmgr.GetPlayerNameByGUID(m_leaderGuid, m_leaderName))
+ {
+ if(!external) delete result;
+ return false;
+ }
+
+ m_groupType = (*result)[13].GetBool() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
+ m_difficulty = (*result)[14].GetUInt8();
+ m_mainTank = (*result)[0].GetUInt64();
+ m_mainAssistant = (*result)[1].GetUInt64();
+ m_lootMethod = (LootMethod)(*result)[2].GetUInt8();
+ m_looterGuid = MAKE_NEW_GUID((*result)[3].GetUInt32(), 0, HIGHGUID_PLAYER);
+ m_lootThreshold = (ItemQualities)(*result)[4].GetUInt16();
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = (*result)[5+i].GetUInt64();
+ if(!external) delete result;
+
+ if(loadMembers)
+ {
+ result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
+ if(!result)
+ return false;
+
+ do
+ {
+ LoadMemberFromDB((*result)[0].GetUInt32(), (*result)[2].GetUInt8(), (*result)[1].GetBool());
+ } while( result->NextRow() );
+ delete result;
+ // group too small
+ if(GetMembersCount() < 2)
+ return false;
+ }
+
+ return true;
+}
+
+bool Group::LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant)
+{
+ MemberSlot member;
+ member.guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
+
+ // skip non-existed member
+ if(!objmgr.GetPlayerNameByGUID(member.guid, member.name))
+ return false;
+
+ member.group = subgroup;
+ member.assistant = assistant;
+ m_memberSlots.push_back(member);
+ return true;
+}
+
+bool Group::AddInvite(Player *player)
+{
+ if(!player || player->GetGroupInvite() || player->GetGroup())
+ return false;
+
+ RemoveInvite(player);
+
+ m_invitees.insert(player->GetGUID());
+
+ player->SetGroupInvite(this);
+
+ return true;
+}
+
+bool Group::AddLeaderInvite(Player *player)
+{
+ if(!AddInvite(player))
+ return false;
+
+ m_leaderGuid = player->GetGUID();
+ m_leaderName = player->GetName();
+ return true;
+}
+
+uint32 Group::RemoveInvite(Player *player)
+{
+ for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
+ {
+ if((*itr) == player->GetGUID())
+ {
+ m_invitees.erase(itr);
+ break;
+ }
+ }
+
+ player->SetGroupInvite(NULL);
+ return GetMembersCount();
+}
+
+void Group::RemoveAllInvites()
+{
+ for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
+ {
+ Player *invitee = objmgr.GetPlayer(*itr);
+ if(invitee)
+ invitee->SetGroupInvite(NULL);
+ }
+ m_invitees.clear();
+}
+
+bool Group::AddMember(const uint64 &guid, const char* name)
+{
+ if(!_addMember(guid, name))
+ return false;
+ SendUpdate();
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ {
+ if(!IsLeader(player->GetGUID()) && !isBGGroup())
+ {
+ // reset the new member's instances, unless he is currently in one of them
+ // including raid/heroic instances that they are not permanently bound to!
+ player->ResetInstances(INSTANCE_RESET_GROUP_JOIN);
+
+ if(player->getLevel() >= LEVELREQUIREMENT_HEROIC && player->GetDifficulty() != GetDifficulty() )
+ {
+ player->SetDifficulty(m_difficulty);
+ player->SendDungeonDifficulty(true);
+ }
+ }
+ player->SetGroupUpdateFlag(GROUP_UPDATE_FULL);
+ UpdatePlayerOutOfRange(player);
+ }
+
+ return true;
+}
+
+uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method)
+{
+ // remove member and change leader (if need) only if strong more 2 members _before_ member remove
+ if(GetMembersCount() > (isBGGroup() ? 1 : 2)) // in BG group case allow 1 members group
+ {
+ bool leaderChanged = _removeMember(guid);
+
+ Player *player = objmgr.GetPlayer( guid );
+ if (player)
+ {
+ WorldPacket data;
+
+ if(method == 1)
+ {
+ data.Initialize( SMSG_GROUP_UNINVITE, 0 );
+ player->GetSession()->SendPacket( &data );
+ }
+
+ data.Initialize(SMSG_GROUP_LIST, 24);
+ data << uint64(0) << uint64(0) << uint64(0);
+ player->GetSession()->SendPacket(&data);
+
+ _homebindIfInstance(player);
+ }
+
+ if(leaderChanged)
+ {
+ WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1));
+ data << m_memberSlots.front().name;
+ BroadcastPacket(&data);
+ }
+
+ SendUpdate();
+ }
+ // if group before remove <= 2 disband it
+ else
+ Disband(true);
+
+ return m_memberSlots.size();
+}
+
+void Group::ChangeLeader(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+
+ if(slot==m_memberSlots.end())
+ return;
+
+ _setLeader(guid);
+
+ WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1);
+ data << slot->name;
+ BroadcastPacket(&data);
+ SendUpdate();
+}
+
+void Group::Disband(bool hideDestroy)
+{
+ Player *player;
+
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ player = objmgr.GetPlayer(citr->guid);
+ if(!player)
+ continue;
+
+ player->SetGroup(NULL);
+
+ if(!player->GetSession())
+ continue;
+
+ WorldPacket data;
+ if(!hideDestroy)
+ {
+ data.Initialize(SMSG_GROUP_DESTROYED, 0);
+ player->GetSession()->SendPacket(&data);
+ }
+
+ data.Initialize(SMSG_GROUP_LIST, 24);
+ data << uint64(0) << uint64(0) << uint64(0);
+ player->GetSession()->SendPacket(&data);
+
+ _homebindIfInstance(player);
+ }
+ RollId.clear();
+ m_memberSlots.clear();
+
+ RemoveAllInvites();
+
+ if(!isBGGroup())
+ {
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.CommitTransaction();
+ ResetInstances(INSTANCE_RESET_GROUP_DISBAND, NULL);
+ }
+
+ m_leaderGuid = 0;
+ m_leaderName = "";
+}
+
+/*********************************************************/
+/*** LOOT SYSTEM ***/
+/*********************************************************/
+
+void Group::SendLootStartRoll(uint32 CountDown, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_START_ROLL, (8+4+4+4+4+4));
+ data << uint64(r.itemGUID); // guid of rolled item
+ data << uint32(r.totalPlayersRolling); // maybe the number of players rolling for it???
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // item random property ID
+ data << uint32(CountDown); // the countdown time to choose "need" or "greed"
+
+ for (Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ROLL, (8+4+8+4+4+4+1+1));
+ data << uint64(SourceGuid); // guid of the item rolled
+ data << uint32(0); // unknown, maybe amount of players
+ data << uint64(TargetGuid);
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // Item random property ID
+ data << uint8(RollNumber); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
+ data << uint8(RollType); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
+ data << uint8(0); // 2.4.0
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ROLL_WON, (8+4+4+4+4+8+1+1));
+ data << uint64(SourceGuid); // guid of the item rolled
+ data << uint32(0); // unknown, maybe amount of players
+ data << uint32(r.itemid); // the itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomSuffix); // randomSuffix
+ data << uint32(r.itemRandomPropId); // Item random property
+ data << uint64(TargetGuid); // guid of the player who won.
+ data << uint8(RollNumber); // rollnumber realted to SMSG_LOOT_ROLL
+ data << uint8(RollType); // Rolltype related to SMSG_LOOT_ROLL
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r)
+{
+ WorldPacket data(SMSG_LOOT_ALL_PASSED, (8+4+4+4+4));
+ data << uint64(r.itemGUID); // Guid of the item rolled
+ data << uint32(NumberOfPlayers); // The number of players rolling for it???
+ data << uint32(r.itemid); // The itemEntryId for the item that shall be rolled for
+ data << uint32(r.itemRandomPropId); // Item random property ID
+ data << uint32(r.itemRandomSuffix); // Item random suffix ID
+
+ for( Roll::PlayerVote::const_iterator itr=r.playerVote.begin(); itr!=r.playerVote.end(); ++itr)
+ {
+ Player *p = objmgr.GetPlayer(itr->first);
+ if(!p || !p->GetSession())
+ continue;
+
+ if(itr->second != NOT_VALID)
+ p->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature)
+{
+ std::vector<LootItem>::iterator i;
+ ItemPrototype const *item;
+ uint8 itemSlot = 0;
+ Player *player = objmgr.GetPlayer(playerGUID);
+ Group *group = player->GetGroup();
+
+ for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
+ {
+ item = objmgr.GetItemPrototype(i->itemid);
+ if (!item)
+ {
+ //sLog.outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
+ continue;
+ }
+
+ //roll for over-threshold item if it's one-player loot
+ if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
+ {
+ uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
+ Roll* r=new Roll(newitemGUID,*i);
+
+ //a vector is filled with only near party members
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(!member || !member->GetSession())
+ continue;
+ if ( i->AllowedForPlayer(member) )
+ {
+ if (member->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
+ ++r->totalPlayersRolling;
+ }
+ }
+ }
+
+ r->setLoot(loot);
+ r->itemSlot = itemSlot;
+
+ group->SendLootStartRoll(60000, *r);
+
+ loot->items[itemSlot].is_blocked = true;
+ creature->m_groupLootTimer = 60000;
+ creature->lootingGroupLeaderGUID = GetLeaderGUID();
+
+ RollId.push_back(r);
+ }
+ else
+ i->is_underthreshold=1;
+
+ }
+}
+
+void Group::NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature)
+{
+ ItemPrototype const *item;
+ Player *player = objmgr.GetPlayer(playerGUID);
+ Group *group = player->GetGroup();
+
+ uint8 itemSlot = 0;
+ for(std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
+ {
+ item = objmgr.GetItemPrototype(i->itemid);
+
+ //only roll for one-player items, not for ones everyone can get
+ if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
+ {
+ uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
+ Roll* r=new Roll(newitemGUID,*i);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *playerToRoll = itr->getSource();
+ if(!playerToRoll || !playerToRoll->GetSession())
+ continue;
+
+ if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) )
+ {
+ if (playerToRoll->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
+ ++r->totalPlayersRolling;
+ }
+ }
+ }
+
+ if (r->totalPlayersRolling > 0)
+ {
+ r->setLoot(loot);
+ r->itemSlot = itemSlot;
+
+ group->SendLootStartRoll(60000, *r);
+
+ loot->items[itemSlot].is_blocked = true;
+
+ RollId.push_back(r);
+ }
+ else
+ {
+ delete r;
+ }
+ }
+ else
+ i->is_underthreshold=1;
+ }
+}
+
+void Group::MasterLoot(uint64 playerGUID, Loot* /*loot*/, Creature *creature)
+{
+ Player *player = objmgr.GetPlayer(playerGUID);
+ if(!player)
+ return;
+
+ sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName());
+
+ uint32 real_count = 0;
+
+ WorldPacket data(SMSG_LOOT_MASTER_LIST, 330);
+ data << (uint8)GetMembersCount();
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *looter = itr->getSource();
+ if (!looter->IsInWorld())
+ continue;
+
+ if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ data << looter->GetGUID();
+ ++real_count;
+ }
+ }
+
+ data.put<uint8>(0,real_count);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *looter = itr->getSource();
+ if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ looter->GetSession()->SendPacket(&data);
+ }
+}
+
+void Group::CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise)
+{
+ Rolls::iterator rollI = GetRoll(Guid);
+ if (rollI == RollId.end())
+ return;
+ Roll* roll = *rollI;
+
+ Roll::PlayerVote::iterator itr = roll->playerVote.find(playerGUID);
+ // this condition means that player joins to the party after roll begins
+ if (itr == roll->playerVote.end())
+ return;
+
+ if (roll->getLoot())
+ if (roll->getLoot()->items.empty())
+ return;
+
+ switch (Choise)
+ {
+ case 0: //Player choose pass
+ {
+ SendLootRoll(0, playerGUID, 128, 128, *roll);
+ ++roll->totalPass;
+ itr->second = PASS;
+ }
+ break;
+ case 1: //player choose Need
+ {
+ SendLootRoll(0, playerGUID, 1, 1, *roll);
+ ++roll->totalNeed;
+ itr->second = NEED;
+ }
+ break;
+ case 2: //player choose Greed
+ {
+ SendLootRoll(0, playerGUID, 2, 2, *roll);
+ ++roll->totalGreed;
+ itr->second = GREED;
+ }
+ break;
+ }
+ if (roll->totalPass + roll->totalGreed + roll->totalNeed >= roll->totalPlayersRolling)
+ {
+ CountTheRoll(rollI, NumberOfPlayers);
+ }
+}
+
+//called when roll timer expires
+void Group::EndRoll()
+{
+ Rolls::iterator itr;
+ while(!RollId.empty())
+ {
+ //need more testing here, if rolls disappear
+ itr = RollId.begin();
+ CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
+ }
+}
+
+void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
+{
+ Roll* roll = *rollI;
+ if(!roll->isValid()) // is loot already deleted ?
+ {
+ RollId.erase(rollI);
+ delete roll;
+ return;
+ }
+ //end of the roll
+ if (roll->totalNeed > 0)
+ {
+ if(!roll->playerVote.empty())
+ {
+ uint8 maxresul = 0;
+ uint64 maxguid = (*roll->playerVote.begin()).first;
+ Player *player;
+
+ for( Roll::PlayerVote::const_iterator itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
+ {
+ if (itr->second != NEED)
+ continue;
+
+ uint8 randomN = urand(1, 99);
+ SendLootRoll(0, itr->first, randomN, 1, *roll);
+ if (maxresul < randomN)
+ {
+ maxguid = itr->first;
+ maxresul = randomN;
+ }
+ }
+ SendLootRollWon(0, maxguid, maxresul, 1, *roll);
+ player = objmgr.GetPlayer(maxguid);
+
+ if(player && player->GetSession())
+ {
+ ItemPosCountVec dest;
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ item->is_looted = true;
+ roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
+ --roll->getLoot()->unlootedCount;
+ player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
+ }
+ else
+ {
+ item->is_blocked = false;
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ }
+ }
+ }
+ else if (roll->totalGreed > 0)
+ {
+ if(!roll->playerVote.empty())
+ {
+ uint8 maxresul = 0;
+ uint64 maxguid = (*roll->playerVote.begin()).first;
+ Player *player;
+
+ Roll::PlayerVote::iterator itr;
+ for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
+ {
+ if (itr->second != GREED)
+ continue;
+
+ uint8 randomN = urand(1, 99);
+ SendLootRoll(0, itr->first, randomN, 2, *roll);
+ if (maxresul < randomN)
+ {
+ maxguid = itr->first;
+ maxresul = randomN;
+ }
+ }
+ SendLootRollWon(0, maxguid, maxresul, 2, *roll);
+ player = objmgr.GetPlayer(maxguid);
+
+ if(player && player->GetSession())
+ {
+ ItemPosCountVec dest;
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ item->is_looted = true;
+ roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
+ --roll->getLoot()->unlootedCount;
+ player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
+ }
+ else
+ {
+ item->is_blocked = false;
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ }
+ }
+ }
+ else
+ {
+ SendLootAllPassed(NumberOfPlayers, *roll);
+ LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
+ if(item) item->is_blocked = false;
+ }
+ RollId.erase(rollI);
+ delete roll;
+}
+
+void Group::SetTargetIcon(uint8 id, uint64 guid)
+{
+ if(id >= TARGETICONCOUNT)
+ return;
+
+ // clean other icons
+ if( guid != 0 )
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ if( m_targetIcons[i] == guid )
+ SetTargetIcon(i, 0);
+
+ m_targetIcons[id] = guid;
+
+ WorldPacket data(MSG_RAID_TARGET_UPDATE, (2+8));
+ data << (uint8)0;
+ data << id;
+ data << guid;
+ BroadcastPacket(&data);
+}
+
+void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* member = itr->getSource();
+ if(!member || !member->isAlive()) // only for alive
+ continue;
+
+ if(!member->IsAtGroupRewardDistance(victim)) // at req. distance
+ continue;
+
+ ++count;
+ sum_level += member->getLevel();
+ if(!member_with_max_level || member_with_max_level->getLevel() < member->getLevel())
+ member_with_max_level = member;
+ }
+}
+
+void Group::SendTargetIconList(WorldSession *session)
+{
+ if(!session)
+ return;
+
+ WorldPacket data(MSG_RAID_TARGET_UPDATE, (1+TARGETICONCOUNT*9));
+ data << (uint8)1;
+
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ {
+ if(m_targetIcons[i] == 0)
+ continue;
+
+ data << (uint8)i;
+ data << m_targetIcons[i];
+ }
+
+ session->SendPacket(&data);
+}
+
+void Group::SendUpdate()
+{
+ Player *player;
+
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ player = objmgr.GetPlayer(citr->guid);
+ if(!player || !player->GetSession())
+ continue;
+ // guess size
+ WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20));
+ data << (uint8)m_groupType; // group type
+ data << (uint8)(isBGGroup() ? 1 : 0); // 2.0.x, isBattleGroundGroup?
+ data << (uint8)(citr->group); // groupid
+ data << (uint8)(citr->assistant?0x01:0); // 0x2 main assist, 0x4 main tank
+ data << uint64(0x50000000FFFFFFFELL); // related to voice chat?
+ data << uint32(GetMembersCount()-1);
+ for(member_citerator citr2 = m_memberSlots.begin(); citr2 != m_memberSlots.end(); ++citr2)
+ {
+ if(citr->guid == citr2->guid)
+ continue;
+
+ data << citr2->name;
+ data << (uint64)citr2->guid;
+ // online-state
+ data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0);
+ data << (uint8)(citr2->group); // groupid
+ data << (uint8)(citr2->assistant?0x01:0); // 0x2 main assist, 0x4 main tank
+ }
+
+ data << uint64(m_leaderGuid); // leader guid
+ if(GetMembersCount()-1)
+ {
+ data << (uint8)m_lootMethod; // loot method
+ data << (uint64)m_looterGuid; // looter guid
+ data << (uint8)m_lootThreshold; // loot threshold
+ data << (uint8)m_difficulty; // Heroic Mod Group
+
+ }
+ player->GetSession()->SendPacket( &data );
+ }
+}
+
+void Group::UpdatePlayerOutOfRange(Player* pPlayer)
+{
+ if(!pPlayer)
+ return;
+
+ Player *player;
+ WorldPacket data;
+ pPlayer->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer, &data);
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ player = itr->getSource();
+ if (player && player != pPlayer && !pPlayer->isVisibleFor(player))
+ player->GetSession()->SendPacket(&data);
+ }
+}
+
+void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+ if(!pl || (ignore != 0 && pl->GetGUID() == ignore))
+ continue;
+
+ if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group))
+ pl->GetSession()->SendPacket(packet);
+ }
+}
+
+void Group::BroadcastReadyCheck(WorldPacket *packet)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+ if(pl && pl->GetSession())
+ if(IsLeader(pl->GetGUID()) || IsAssistant(pl->GetGUID()))
+ pl->GetSession()->SendPacket(packet);
+ }
+}
+
+void Group::OfflineReadyCheck()
+{
+ for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
+ {
+ Player *pl = objmgr.GetPlayer(citr->guid);
+ if (!pl || !pl->GetSession())
+ {
+ WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
+ data << citr->guid;
+ data << (uint8)0;
+ BroadcastReadyCheck(&data);
+ }
+ }
+}
+
+bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant)
+{
+ // get first not-full group
+ uint8 groupid = 0;
+ std::vector<uint8> temp(MAXRAIDSIZE/MAXGROUPSIZE);
+ for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
+ {
+ if (itr->group >= temp.size()) continue;
+ ++temp[itr->group];
+ if(temp[groupid] >= MAXGROUPSIZE)
+ ++groupid;
+ }
+
+ return _addMember(guid, name, isAssistant, groupid);
+}
+
+bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group)
+{
+ if(IsFull())
+ return false;
+
+ if(!guid)
+ return false;
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ MemberSlot member;
+ member.guid = guid;
+ member.name = name;
+ member.group = group;
+ member.assistant = isAssistant;
+ m_memberSlots.push_back(member);
+
+ if(player)
+ {
+ player->SetGroupInvite(NULL);
+ player->SetGroup(this, group);
+ // if the same group invites the player back, cancel the homebind timer
+ InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty());
+ if(bind && bind->save->GetInstanceId() == player->GetInstanceId())
+ player->m_InstanceValid = true;
+ }
+
+ if(!isRaidGroup()) // reset targetIcons for non-raid-groups
+ {
+ for(int i=0; i<TARGETICONCOUNT; i++)
+ m_targetIcons[i] = 0;
+ }
+
+ if(!isBGGroup())
+ {
+ // insert into group table
+ CharacterDatabase.PExecute("INSERT INTO group_member(leaderGuid,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", GUID_LOPART(m_leaderGuid), GUID_LOPART(member.guid), ((member.assistant==1)?1:0), member.group);
+ }
+
+ return true;
+}
+
+bool Group::_removeMember(const uint64 &guid)
+{
+ Player *player = objmgr.GetPlayer(guid);
+ if (player)
+ {
+ player->SetGroup(NULL);
+ }
+
+ _removeRolls(guid);
+
+ member_witerator slot = _getMemberWSlot(guid);
+ if (slot != m_memberSlots.end())
+ m_memberSlots.erase(slot);
+
+ if(!isBGGroup())
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid='%u'", GUID_LOPART(guid));
+
+ if(m_leaderGuid == guid) // leader was removed
+ {
+ if(GetMembersCount() > 0)
+ _setLeader(m_memberSlots.front().guid);
+ return true;
+ }
+
+ return false;
+}
+
+void Group::_setLeader(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+ if(slot==m_memberSlots.end())
+ return;
+
+ if(!isBGGroup())
+ {
+ // TODO: set a time limit to have this function run rarely cause it can be slow
+ CharacterDatabase.BeginTransaction();
+
+ // update the group's bound instances when changing leaders
+
+ // remove all permanent binds from the group
+ // in the DB also remove solo binds that will be replaced with permbinds
+ // from the new leader
+ CharacterDatabase.PExecute(
+ "DELETE FROM group_instance WHERE leaderguid='%u' AND (permanent = 1 OR "
+ "instance IN (SELECT instance FROM character_instance WHERE guid = '%u')"
+ ")", GUID_LOPART(m_leaderGuid), GUID_LOPART(slot->guid)
+ );
+
+ Player *player = objmgr.GetPlayer(slot->guid);
+ if(player)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end();)
+ {
+ if(itr->second.perm)
+ {
+ itr->second.save->RemoveGroup(this);
+ m_boundInstances[i].erase(itr++);
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ // update the group's solo binds to the new leader
+ CharacterDatabase.PExecute("UPDATE group_instance SET leaderGuid='%u' WHERE leaderGuid = '%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+
+ // copy the permanent binds from the new leader to the group
+ // overwriting the solo binds with permanent ones if necessary
+ // in the DB those have been deleted already
+ Player::ConvertInstancesToGroup(player, this, slot->guid);
+
+ // update the group leader
+ CharacterDatabase.PExecute("UPDATE groups SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.PExecute("UPDATE group_member SET leaderGuid='%u' WHERE leaderGuid='%u'", GUID_LOPART(slot->guid), GUID_LOPART(m_leaderGuid));
+ CharacterDatabase.CommitTransaction();
+ }
+
+ m_leaderGuid = slot->guid;
+ m_leaderName = slot->name;
+}
+
+void Group::_removeRolls(const uint64 &guid)
+{
+ for (Rolls::iterator it = RollId.begin(); it < RollId.end(); it++)
+ {
+ Roll* roll = *it;
+ Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
+ if(itr2 == roll->playerVote.end())
+ continue;
+
+ if (itr2->second == GREED) --roll->totalGreed;
+ if (itr2->second == NEED) --roll->totalNeed;
+ if (itr2->second == PASS) --roll->totalPass;
+ if (itr2->second != NOT_VALID) --roll->totalPlayersRolling;
+
+ roll->playerVote.erase(itr2);
+
+ CountRollVote(guid, roll->itemGUID, GetMembersCount()-1, 3);
+ }
+}
+
+void Group::_convertToRaid()
+{
+ m_groupType = GROUPTYPE_RAID;
+
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
+}
+
+bool Group::_setMembersGroup(const uint64 &guid, const uint8 &group)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ slot->group = group;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET subgroup='%u' WHERE memberGuid='%u'", group, GUID_LOPART(guid));
+ return true;
+}
+
+bool Group::_setAssistantFlag(const uint64 &guid, const bool &state)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ slot->assistant = state;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE group_member SET assistant='%u' WHERE memberGuid='%u'", (state==true)?1:0, GUID_LOPART(guid));
+ return true;
+}
+
+bool Group::_setMainTank(const uint64 &guid)
+{
+ member_citerator slot = _getMemberCSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ if(m_mainAssistant == guid)
+ _setMainAssistant(0);
+ m_mainTank = guid;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainTank='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainTank), GUID_LOPART(m_leaderGuid));
+ return true;
+}
+
+bool Group::_setMainAssistant(const uint64 &guid)
+{
+ member_witerator slot = _getMemberWSlot(guid);
+ if(slot==m_memberSlots.end())
+ return false;
+
+ if(m_mainTank == guid)
+ _setMainTank(0);
+ m_mainAssistant = guid;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET mainAssistant='%u' WHERE leaderGuid='%u'", GUID_LOPART(m_mainAssistant), GUID_LOPART(m_leaderGuid));
+ return true;
+}
+
+bool Group::SameSubGroup(Player const* member1, Player const* member2) const
+{
+ if(!member1 || !member2) return false;
+ if (member1->GetGroup() != this || member2->GetGroup() != this) return false;
+ else return member1->GetSubGroup() == member2->GetSubGroup();
+}
+
+// allows setting subgroup for offline members
+void Group::ChangeMembersGroup(const uint64 &guid, const uint8 &group)
+{
+ if(!isRaidGroup())
+ return;
+ Player *player = objmgr.GetPlayer(guid);
+ if (!player)
+ {
+ if(_setMembersGroup(guid, group))
+ SendUpdate();
+ }
+ else ChangeMembersGroup(player, group);
+}
+
+// only for online members
+void Group::ChangeMembersGroup(Player *player, const uint8 &group)
+{
+ if(!player || !isRaidGroup())
+ return;
+ if(_setMembersGroup(player->GetGUID(), group))
+ {
+ player->GetGroupRef().setSubGroup(group);
+ SendUpdate();
+ }
+}
+
+void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
+{
+ switch (GetLootMethod())
+ {
+ case MASTER_LOOT:
+ case FREE_FOR_ALL:
+ return;
+ default:
+ // round robin style looting applies for all low
+ // quality items in each loot method except free for all and master loot
+ break;
+ }
+
+ member_citerator guid_itr = _getMemberCSlot(GetLooterGuid());
+ if(guid_itr != m_memberSlots.end())
+ {
+ if(ifneed)
+ {
+ // not update if only update if need and ok
+ Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid);
+ if(looter && looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ return;
+ }
+ ++guid_itr;
+ }
+
+ // search next after current
+ if(guid_itr != m_memberSlots.end())
+ {
+ for(member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
+ {
+ if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
+ {
+ if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
+ //if(refresh) // update loot for new looter
+ // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
+ SetLooterGuid(pl->GetGUID());
+ SendUpdate();
+ if(refresh) // update loot for new looter
+ pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
+ return;
+ }
+ }
+ }
+ }
+
+ // search from start
+ for(member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
+ {
+ if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
+ {
+ if (pl->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ {
+ bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
+ //if(refresh) // update loot for new looter
+ // pl->GetSession()->DoLootRelease(pl->GetLootGUID());
+ SetLooterGuid(pl->GetGUID());
+ SendUpdate();
+ if(refresh) // update loot for new looter
+ pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
+ return;
+ }
+ }
+ }
+
+ SetLooterGuid(0);
+ SendUpdate();
+}
+
+//===================================================
+//============== Roll ===============================
+//===================================================
+
+void Roll::targetObjectBuildLink()
+{
+ // called from link()
+ this->getTarget()->addLootValidatorRef(this);
+}
+
+void Group::SetDifficulty(uint8 difficulty)
+{
+ m_difficulty = difficulty;
+ if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET difficulty = %u WHERE leaderGuid ='%u'", m_difficulty, GUID_LOPART(m_leaderGuid));
+
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *player = itr->getSource();
+ if(!player->GetSession() || player->getLevel() < LEVELREQUIREMENT_HEROIC)
+ continue;
+ player->SetDifficulty(difficulty);
+ player->SendDungeonDifficulty(true);
+ }
+}
+
+bool Group::InCombatToInstance(uint32 instanceId)
+{
+ for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pPlayer = itr->getSource();
+ if(pPlayer->getAttackers().size() && pPlayer->GetInstanceId() == instanceId)
+ return true;
+ }
+ return false;
+}
+
+void Group::ResetInstances(uint8 method, Player* SendMsgTo)
+{
+ if(isBGGroup())
+ return;
+
+ // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
+
+ // we assume that when the difficulty changes, all instances that can be reset will be
+ uint8 dif = GetDifficulty();
+
+ for(BoundInstancesMap::iterator itr = m_boundInstances[dif].begin(); itr != m_boundInstances[dif].end();)
+ {
+ InstanceSave *p = itr->second.save;
+ const MapEntry *entry = sMapStore.LookupEntry(itr->first);
+ if(!entry || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND))
+ {
+ ++itr;
+ continue;
+ }
+
+ if(method == INSTANCE_RESET_ALL)
+ {
+ // the "reset all instances" method can only reset normal maps
+ if(dif == DIFFICULTY_HEROIC || entry->map_type == MAP_RAID)
+ {
+ ++itr;
+ continue;
+ }
+ }
+
+ bool isEmpty = true;
+ // if the map is loaded, reset it
+ Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
+ if(map && map->IsDungeon())
+ isEmpty = ((InstanceMap*)map)->Reset(method);
+
+ if(SendMsgTo)
+ {
+ if(isEmpty) SendMsgTo->SendResetInstanceSuccess(p->GetMapId());
+ else SendMsgTo->SendResetInstanceFailed(0, p->GetMapId());
+ }
+
+ if(isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
+ {
+ // do not reset the instance, just unbind if others are permanently bound to it
+ if(p->CanReset()) p->DeleteFromDB();
+ else CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId());
+ // i don't know for sure if hash_map iterators
+ m_boundInstances[dif].erase(itr);
+ itr = m_boundInstances[dif].begin();
+ // this unloads the instance save unless online players are bound to it
+ // (eg. permanent binds or GM solo binds)
+ p->RemoveGroup(this);
+ }
+ else
+ ++itr;
+ }
+}
+
+InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, uint8 difficulty)
+{
+ // some instances only have one difficulty
+ const MapEntry* entry = sMapStore.LookupEntry(mapid);
+ if(!entry || !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL;
+
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ if(itr != m_boundInstances[difficulty].end())
+ return &itr->second;
+ else
+ return NULL;
+}
+
+InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load)
+{
+ if(save && !isBGGroup())
+ {
+ InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
+ if(bind.save)
+ {
+ // when a boss is killed or when copying the players's binds to the group
+ if(permanent != bind.perm || save != bind.save)
+ if(!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GUID_LOPART(GetLeaderGUID()), bind.save->GetInstanceId());
+ }
+ else
+ if(!load) CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", GUID_LOPART(GetLeaderGUID()), save->GetInstanceId(), permanent);
+
+ if(bind.save != save)
+ {
+ if(bind.save) bind.save->RemoveGroup(this);
+ save->AddGroup(this);
+ }
+
+ bind.save = save;
+ bind.perm = permanent;
+ if(!load) sLog.outDebug("Group::BindToInstance: %d is now bound to map %d, instance %d, difficulty %d", GUID_LOPART(GetLeaderGUID()), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
+ return &bind;
+ }
+ else
+ return NULL;
+}
+
+void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
+{
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ if(itr != m_boundInstances[difficulty].end())
+ {
+ if(!unload) CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", GUID_LOPART(GetLeaderGUID()), itr->second.save->GetInstanceId());
+ itr->second.save->RemoveGroup(this); // save can become invalid
+ m_boundInstances[difficulty].erase(itr);
+ }
+}
+
+void Group::_homebindIfInstance(Player *player)
+{
+ if(player && !player->isGameMaster() && sMapStore.LookupEntry(player->GetMapId())->IsDungeon())
+ {
+ // leaving the group in an instance, the homebind timer is started
+ // unless the player is permanently saved to the instance
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(player->GetInstanceId());
+ InstancePlayerBind *playerBind = save ? player->GetBoundInstance(save->GetMapId(), save->GetDifficulty()) : NULL;
+ if(!playerBind || !playerBind->perm)
+ player->m_InstanceValid = false;
+ }
+}
diff --git a/src/game/Group.h b/src/game/Group.h
new file mode 100644
index 00000000000..14ea3ad7cba
--- /dev/null
+++ b/src/game/Group.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_GROUP_H
+#define MANGOSSERVER_GROUP_H
+
+#include "GroupReference.h"
+#include "GroupRefManager.h"
+#include "LootMgr.h"
+
+#include <map>
+#include <vector>
+
+#define MAXGROUPSIZE 5
+#define MAXRAIDSIZE 40
+#define TARGETICONCOUNT 8
+
+enum RollVote
+{
+ PASS = 0,
+ NEED = 1,
+ GREED = 2,
+ NOT_EMITED_YET = 3,
+ NOT_VALID = 4
+};
+
+enum GroupMemberOnlineStatus
+{
+ MEMBER_STATUS_OFFLINE = 0x0000,
+ MEMBER_STATUS_ONLINE = 0x0001,
+ MEMBER_STATUS_PVP = 0x0002,
+ MEMBER_STATUS_UNK0 = 0x0004, // dead? (health=0)
+ MEMBER_STATUS_UNK1 = 0x0008, // ghost? (health=1)
+ MEMBER_STATUS_UNK2 = 0x0010, // never seen
+ MEMBER_STATUS_UNK3 = 0x0020, // never seen
+ MEMBER_STATUS_UNK4 = 0x0040, // appears with dead and ghost flags
+ MEMBER_STATUS_UNK5 = 0x0080, // never seen
+};
+
+enum GroupType
+{
+ GROUPTYPE_NORMAL = 0,
+ GROUPTYPE_RAID = 1
+};
+
+class BattleGround;
+
+enum GroupUpdateFlags
+{
+ GROUP_UPDATE_FLAG_NONE = 0x00000000, // nothing
+ GROUP_UPDATE_FLAG_STATUS = 0x00000001, // uint16, flags
+ GROUP_UPDATE_FLAG_CUR_HP = 0x00000002, // uint16
+ GROUP_UPDATE_FLAG_MAX_HP = 0x00000004, // uint16
+ GROUP_UPDATE_FLAG_POWER_TYPE = 0x00000008, // uint8
+ GROUP_UPDATE_FLAG_CUR_POWER = 0x00000010, // uint16
+ GROUP_UPDATE_FLAG_MAX_POWER = 0x00000020, // uint16
+ GROUP_UPDATE_FLAG_LEVEL = 0x00000040, // uint16
+ GROUP_UPDATE_FLAG_ZONE = 0x00000080, // uint16
+ GROUP_UPDATE_FLAG_POSITION = 0x00000100, // uint16, uint16
+ GROUP_UPDATE_FLAG_AURAS = 0x00000200, // uint64 mask, for each bit set uint16 spellid + uint8 unk
+ GROUP_UPDATE_FLAG_PET_GUID = 0x00000400, // uint64 pet guid
+ GROUP_UPDATE_FLAG_PET_NAME = 0x00000800, // pet name, NULL terminated string
+ GROUP_UPDATE_FLAG_PET_MODEL_ID = 0x00001000, // uint16, model id
+ GROUP_UPDATE_FLAG_PET_CUR_HP = 0x00002000, // uint16 pet cur health
+ GROUP_UPDATE_FLAG_PET_MAX_HP = 0x00004000, // uint16 pet max health
+ GROUP_UPDATE_FLAG_PET_POWER_TYPE = 0x00008000, // uint8 pet power type
+ GROUP_UPDATE_FLAG_PET_CUR_POWER = 0x00010000, // uint16 pet cur power
+ GROUP_UPDATE_FLAG_PET_MAX_POWER = 0x00020000, // uint16 pet max power
+ GROUP_UPDATE_FLAG_PET_AURAS = 0x00040000, // uint64 mask, for each bit set uint16 spellid + uint8 unk, pet auras...
+ GROUP_UPDATE_PET = 0x0007FC00, // all pet flags
+ GROUP_UPDATE_FULL = 0x0007FFFF, // all known flags
+};
+
+#define GROUP_UPDATE_FLAGS_COUNT 20
+ // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19
+static const uint8 GroupUpdateLength[GROUP_UPDATE_FLAGS_COUNT] = { 0, 2, 2, 2, 1, 2, 2, 2, 2, 4, 8, 8, 1, 2, 2, 2, 1, 2, 2, 8};
+
+class InstanceSave;
+
+class Roll : public LootValidatorRef
+{
+ public:
+ Roll(uint64 _guid, LootItem const& li)
+ : itemGUID(_guid), itemid(li.itemid), itemRandomPropId(li.randomPropertyId), itemRandomSuffix(li.randomSuffix),
+ totalPlayersRolling(0), totalNeed(0), totalGreed(0), totalPass(0), itemSlot(0) {}
+ ~Roll() { }
+ void setLoot(Loot *pLoot) { link(pLoot, this); }
+ Loot *getLoot() { return getTarget(); }
+ void targetObjectBuildLink();
+
+ uint64 itemGUID;
+ uint32 itemid;
+ int32 itemRandomPropId;
+ uint32 itemRandomSuffix;
+ typedef std::map<uint64, RollVote> PlayerVote;
+ PlayerVote playerVote; //vote position correspond with player position (in group)
+ uint8 totalPlayersRolling;
+ uint8 totalNeed;
+ uint8 totalGreed;
+ uint8 totalPass;
+ uint8 itemSlot;
+};
+
+struct InstanceGroupBind
+{
+ InstanceSave *save;
+ bool perm;
+ /* permanent InstanceGroupBinds exist iff the leader has a permanent
+ PlayerInstanceBind for the same instance. */
+ InstanceGroupBind() : save(NULL), perm(false) {}
+};
+
+/** request member stats checken **/
+/** todo: uninvite people that not accepted invite **/
+class MANGOS_DLL_SPEC Group
+{
+ public:
+ struct MemberSlot
+ {
+ uint64 guid;
+ std::string name;
+ uint8 group;
+ bool assistant;
+ };
+ typedef std::list<MemberSlot> MemberSlotList;
+ typedef MemberSlotList::const_iterator member_citerator;
+
+ typedef HM_NAMESPACE::hash_map< uint32 /*mapId*/, InstanceGroupBind> BoundInstancesMap;
+ protected:
+ typedef MemberSlotList::iterator member_witerator;
+ typedef std::set<uint64> InvitesList;
+
+ typedef std::vector<Roll*> Rolls;
+
+ public:
+ Group();
+ ~Group();
+
+ // group manipulation methods
+ bool Create(const uint64 &guid, const char * name);
+ bool LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result = NULL, bool loadMembers = true);
+ bool LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant);
+ bool AddInvite(Player *player);
+ uint32 RemoveInvite(Player *player);
+ void RemoveAllInvites();
+ bool AddLeaderInvite(Player *player);
+ bool AddMember(const uint64 &guid, const char* name);
+ // method: 0=just remove, 1=kick
+ uint32 RemoveMember(const uint64 &guid, const uint8 &method);
+ void ChangeLeader(const uint64 &guid);
+ void SetLootMethod(LootMethod method) { m_lootMethod = method; }
+ void SetLooterGuid(const uint64 &guid) { m_looterGuid = guid; }
+ void UpdateLooterGuid( Creature* creature, bool ifneed = false );
+ void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; }
+ void Disband(bool hideDestroy=false);
+
+ // properties accessories
+ bool IsFull() const { return (m_groupType==GROUPTYPE_NORMAL) ? (m_memberSlots.size()>=MAXGROUPSIZE) : (m_memberSlots.size()>=MAXRAIDSIZE); }
+ bool isRaidGroup() const { return m_groupType==GROUPTYPE_RAID; }
+ bool isBGGroup() const { return m_bgGroup != NULL; }
+ bool IsCreated() const { return GetMembersCount() > 0; }
+ const uint64& GetLeaderGUID() const { return m_leaderGuid; }
+ const char * GetLeaderName() const { return m_leaderName.c_str(); }
+ LootMethod GetLootMethod() const { return m_lootMethod; }
+ const uint64& GetLooterGuid() const { return m_looterGuid; }
+ ItemQualities GetLootThreshold() const { return m_lootThreshold; }
+
+ // member manipulation methods
+ bool IsMember(uint64 guid) const { return _getMemberCSlot(guid) != m_memberSlots.end(); }
+ bool IsLeader(uint64 guid) const { return (GetLeaderGUID() == guid); }
+ bool IsAssistant(uint64 guid) const
+ {
+ member_citerator mslot = _getMemberCSlot(guid);
+ if(mslot==m_memberSlots.end())
+ return false;
+
+ return mslot->assistant;
+ }
+
+ bool SameSubGroup(uint64 guid1, uint64 guid2) const
+ {
+ member_citerator mslot2 = _getMemberCSlot(guid2);
+ if(mslot2==m_memberSlots.end())
+ return false;
+
+ return SameSubGroup(guid1,&*mslot2);
+ }
+
+ bool SameSubGroup(uint64 guid1, MemberSlot const* slot2) const
+ {
+ member_citerator mslot1 = _getMemberCSlot(guid1);
+ if(mslot1==m_memberSlots.end() || !slot2)
+ return false;
+
+ return (mslot1->group==slot2->group);
+ }
+
+ bool SameSubGroup(Player const* member1, Player const* member2) const;
+
+ MemberSlotList const& GetMemberSlots() const { return m_memberSlots; }
+ GroupReference* GetFirstMember() { return m_memberMgr.getFirst(); }
+ uint32 GetMembersCount() const { return m_memberSlots.size(); }
+ void GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level);
+ uint8 GetMemberGroup(uint64 guid) const
+ {
+ member_citerator mslot = _getMemberCSlot(guid);
+ if(mslot==m_memberSlots.end())
+ return (MAXRAIDSIZE/MAXGROUPSIZE+1);
+
+ return mslot->group;
+ }
+
+ // some additional raid methods
+ void ConvertToRaid()
+ {
+ _convertToRaid();
+ SendUpdate();
+ }
+ void SetBattlegroundGroup(BattleGround *bg) { m_bgGroup = bg; }
+
+ void ChangeMembersGroup(const uint64 &guid, const uint8 &group);
+ void ChangeMembersGroup(Player *player, const uint8 &group);
+
+ void SetAssistant(const uint64 &guid, const bool &state)
+ {
+ if(!isRaidGroup())
+ return;
+ if(_setAssistantFlag(guid, state))
+ SendUpdate();
+ }
+ void SetMainTank(const uint64 &guid)
+ {
+ if(!isRaidGroup())
+ return;
+
+ if(_setMainTank(guid))
+ SendUpdate();
+ }
+ void SetMainAssistant(const uint64 &guid)
+ {
+ if(!isRaidGroup())
+ return;
+
+ if(_setMainAssistant(guid))
+ SendUpdate();
+ }
+
+ void SetTargetIcon(uint8 id, uint64 guid);
+ void SetDifficulty(uint8 difficulty);
+ uint8 GetDifficulty() { return m_difficulty; }
+ uint16 InInstance();
+ bool InCombatToInstance(uint32 instanceId);
+ void ResetInstances(uint8 method, Player* SendMsgTo);
+
+ // -no description-
+ //void SendInit(WorldSession *session);
+ void SendTargetIconList(WorldSession *session);
+ void SendUpdate();
+ void UpdatePlayerOutOfRange(Player* pPlayer);
+ // ignore: GUID of player that will be ignored
+ void BroadcastPacket(WorldPacket *packet, int group=-1, uint64 ignore=0);
+ void BroadcastReadyCheck(WorldPacket *packet);
+ void OfflineReadyCheck();
+
+ /*********************************************************/
+ /*** LOOT SYSTEM ***/
+ /*********************************************************/
+
+ void SendLootStartRoll(uint32 CountDown, const Roll &r);
+ void SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
+ void SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
+ void SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r);
+ void GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature);
+ void NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature);
+ void MasterLoot(uint64 playerGUID, Loot *loot, Creature *creature);
+ Rolls::iterator GetRoll(uint64 Guid)
+ {
+ Rolls::iterator iter;
+ for (iter=RollId.begin(); iter != RollId.end(); ++iter)
+ {
+ if ((*iter)->itemGUID == Guid && (*iter)->isValid())
+ {
+ return iter;
+ }
+ }
+ return RollId.end();
+ }
+ void CountTheRoll(Rolls::iterator roll, uint32 NumberOfPlayers);
+ void CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise);
+ void EndRoll();
+
+ void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); }
+ void DelinkMember(GroupReference* /*pRef*/ ) { }
+
+ InstanceGroupBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false);
+ void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false);
+ InstanceGroupBind* GetBoundInstance(uint32 mapid, uint8 difficulty);
+ BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; }
+
+ protected:
+ bool _addMember(const uint64 &guid, const char* name, bool isAssistant=false);
+ bool _addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group);
+ bool _removeMember(const uint64 &guid); // returns true if leader has changed
+ void _setLeader(const uint64 &guid);
+
+ void _removeRolls(const uint64 &guid);
+
+ void _convertToRaid();
+ bool _setMembersGroup(const uint64 &guid, const uint8 &group);
+ bool _setAssistantFlag(const uint64 &guid, const bool &state);
+ bool _setMainTank(const uint64 &guid);
+ bool _setMainAssistant(const uint64 &guid);
+
+ void _homebindIfInstance(Player *player);
+
+ member_citerator _getMemberCSlot(uint64 Guid) const
+ {
+ for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
+ {
+ if (itr->guid == Guid)
+ return itr;
+ }
+ return m_memberSlots.end();
+ }
+
+ member_witerator _getMemberWSlot(uint64 Guid)
+ {
+ for(member_witerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
+ {
+ if (itr->guid == Guid)
+ return itr;
+ }
+ return m_memberSlots.end();
+ }
+
+ MemberSlotList m_memberSlots;
+ GroupRefManager m_memberMgr;
+ InvitesList m_invitees;
+ uint64 m_leaderGuid;
+ std::string m_leaderName;
+ uint64 m_mainTank;
+ uint64 m_mainAssistant;
+ GroupType m_groupType;
+ uint8 m_difficulty;
+ BattleGround* m_bgGroup;
+ uint64 m_targetIcons[TARGETICONCOUNT];
+ LootMethod m_lootMethod;
+ ItemQualities m_lootThreshold;
+ uint64 m_looterGuid;
+ Rolls RollId;
+ BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES];
+};
+#endif
diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp
new file mode 100644
index 00000000000..f2c05e14f5f
--- /dev/null
+++ b/src/game/GroupHandler.cpp
@@ -0,0 +1,934 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Group.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "SocialMgr.h"
+#include "Util.h"
+
+/* differeces from off:
+ -you can uninvite yourself - is is useful
+ -you can accept invitation even if leader went offline
+*/
+/* todo:
+ -group_destroyed msg is sent but not shown
+ -reduce xp gaining when in raid group
+ -quest sharing has to be corrected
+ -FIX sending PartyMemberStats
+*/
+
+void WorldSession::SendPartyResult(PartyOperation operation, std::string member, PartyResult res)
+{
+ WorldPacket data(SMSG_PARTY_COMMAND_RESULT, (8+member.size()+1));
+ data << (uint32)operation;
+ data << member;
+ data << (uint32)res;
+
+ SendPacket( &data );
+}
+
+void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data )
+{
+ std::string membername;
+ recv_data >> membername;
+
+ if(_player->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ // attempt add selected player
+
+ // cheating
+ if(!normalizePlayerName(membername))
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_CANT_FIND_TARGET);
+ return;
+ }
+
+ Player *player = objmgr.GetPlayer(membername.c_str());
+
+ // no player
+ if(!player)
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_CANT_FIND_TARGET);
+ return;
+ }
+
+ // can't group with
+ if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && GetPlayer()->GetTeam() != player->GetTeam())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_UNFRIENDLY);
+ return;
+ }
+ if(GetPlayer()->GetInstanceId() != 0 && player->GetInstanceId() != 0 && GetPlayer()->GetInstanceId() != player->GetInstanceId() && GetPlayer()->GetMapId() == player->GetMapId())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_NOT_IN_YOUR_INSTANCE);
+ return;
+ }
+ // just ignore us
+ if(player->GetInstanceId() != 0 && player->GetDifficulty() != GetPlayer()->GetDifficulty())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_IGNORE_YOU);
+ return;
+ }
+
+ if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_IGNORE_YOU);
+ return;
+ }
+
+ // player already in another group or invited
+ if(player->GetGroup() || player->GetGroupInvite() )
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_ALREADY_IN_GROUP);
+ return;
+ }
+
+ Group *group = GetPlayer()->GetGroup();
+
+ if(group)
+ {
+ // not have permissions for invite
+ if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_YOU_NOT_LEADER);
+ return;
+ }
+
+ // not have place
+ if(group->IsFull())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_PARTY_FULL);
+ return;
+ }
+ }
+
+ // ok, but group not exist, start a new group
+ // but don't create and save the group to the DB until
+ // at least one person joins
+ if(!group)
+ {
+ group = new Group;
+ // new group: if can't add then delete
+ if(!group->AddLeaderInvite(GetPlayer()))
+ {
+ delete group;
+ return;
+ }
+ if(!group->AddInvite(player))
+ {
+ delete group;
+ return;
+ }
+ }
+ else
+ {
+ // already existed group: if can't add then just leave
+ if(!group->AddInvite(player))
+ {
+ return;
+ }
+ }
+
+ // ok, we do it
+ WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
+ data << GetPlayer()->GetName();
+ player->GetSession()->SendPacket(&data);
+
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_OK);
+}
+
+void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ )
+{
+ Group *group = GetPlayer()->GetGroupInvite();
+ if (!group) return;
+
+ if(group->GetLeaderGUID() == GetPlayer()->GetGUID())
+ {
+ sLog.outError("HandleGroupAcceptOpcode: player %s(%d) tried to accept an invite to his own group", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
+ return;
+ }
+
+ // remove in from ivites in any case
+ group->RemoveInvite(GetPlayer());
+
+ /** error handling **/
+ /********************/
+
+ // not have place
+ if(group->IsFull())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_PARTY_FULL);
+ return;
+ }
+
+ Player* leader = objmgr.GetPlayer(group->GetLeaderGUID());
+
+ if(leader && leader->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ // forming a new group, create it
+ if(!group->IsCreated())
+ {
+ if(leader) group->RemoveInvite(leader);
+ group->Create(group->GetLeaderGUID(), group->GetLeaderName());
+ objmgr.AddGroup(group);
+ }
+
+ // everything's fine, do it
+ if(!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName()))
+ return;
+
+ uint8 subgroup = group->GetMemberGroup(GetPlayer()->GetGUID());
+
+ GetPlayer()->SetGroup(group, subgroup);
+}
+
+void WorldSession::HandleGroupDeclineOpcode( WorldPacket & /*recv_data*/ )
+{
+ Group *group = GetPlayer()->GetGroupInvite();
+ if (!group) return;
+
+ Player *leader = objmgr.GetPlayer(group->GetLeaderGUID());
+
+ /** error handling **/
+ if(!leader || !leader->GetSession())
+ return;
+ /********************/
+
+ // everything's fine, do it
+ if(!group->IsCreated())
+ {
+ // note: this means that if you invite more than one person
+ // and one of them declines before the first one accepts
+ // all invites will be cleared
+ // fixme: is that ok ?
+ group->RemoveAllInvites();
+ delete group;
+ }
+
+ GetPlayer()->SetGroupInvite(NULL);
+
+ WorldPacket data( SMSG_GROUP_DECLINE, 10 ); // guess size
+ data << GetPlayer()->GetName();
+ leader->GetSession()->SendPacket( &data );
+}
+
+void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ if(_player->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ std::string membername;
+ if(!objmgr.GetPlayerNameByGUID(guid, membername))
+ return; // not found
+
+ HandleGroupUninvite(guid, membername);
+}
+
+void WorldSession::HandleGroupUninviteNameOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ std::string membername;
+ recv_data >> membername;
+
+ if(_player->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ // player not found
+ if(!normalizePlayerName(membername))
+ return;
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(membername);
+
+ // player not found
+ if(!guid)
+ return;
+
+ HandleGroupUninvite(guid, membername);
+}
+
+void WorldSession::HandleGroupUninvite(uint64 guid, std::string name)
+{
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ if(_player->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ {
+ SendPartyResult(PARTY_OP_LEAVE, "", PARTY_RESULT_YOU_NOT_LEADER);
+ return;
+ }
+
+ if(!group->IsMember(guid) && (player && player->GetGroupInvite() != group))
+ {
+ SendPartyResult(PARTY_OP_LEAVE, name, PARTY_RESULT_NOT_IN_YOUR_PARTY);
+ return;
+ }
+
+ if(guid == GetPlayer()->GetGUID())
+ {
+ sLog.outError("WorldSession::HandleGroupUninvite: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
+ return;
+ }
+ /********************/
+
+ // everything's fine, do it
+
+ if(player && player->GetGroupInvite()) // uninvite invitee
+ player->UninviteFromGroup();
+ else // uninvite member
+ Player::RemoveFromGroup(group,guid);
+}
+
+void WorldSession::HandleGroupSetLeaderOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ /** error handling **/
+ if (!player || !group->IsLeader(GetPlayer()->GetGUID()) || player->GetGroup() != group)
+ return;
+ /********************/
+
+ // everything's fine, do it
+ group->ChangeLeader(guid);
+}
+
+void WorldSession::HandleGroupLeaveOpcode( WorldPacket & /*recv_data*/ )
+{
+ if(!GetPlayer()->GetGroup())
+ return;
+
+ if(_player->InBattleGround())
+ {
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED);
+ return;
+ }
+
+ /** error handling **/
+ /********************/
+
+ // everything's fine, do it
+ SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), PARTY_RESULT_OK);
+
+ GetPlayer()->RemoveFromGroup();
+}
+
+void WorldSession::HandleLootMethodOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8+4);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ uint32 lootMethod;
+ uint64 lootMaster;
+ uint32 lootThreshold;
+ recv_data >> lootMethod >> lootMaster >> lootThreshold;
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+ /********************/
+
+ // everything's fine, do it
+ group->SetLootMethod((LootMethod)lootMethod);
+ group->SetLooterGuid(lootMaster);
+ group->SetLootThreshold((ItemQualities)lootThreshold);
+ group->SendUpdate();
+}
+
+void WorldSession::HandleLootRoll( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+1);
+
+ if(!GetPlayer()->GetGroup())
+ return;
+
+ uint64 Guid;
+ uint32 NumberOfPlayers;
+ uint8 Choise;
+ recv_data >> Guid; //guid of the item rolled
+ recv_data >> NumberOfPlayers;
+ recv_data >> Choise; //0: pass, 1: need, 2: greed
+
+ //sLog.outDebug("WORLD RECIEVE CMSG_LOOT_ROLL, From:%u, Numberofplayers:%u, Choise:%u", (uint32)Guid, NumberOfPlayers, Choise);
+
+ Group* group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ // everything's fine, do it
+ group->CountRollVote(GetPlayer()->GetGUID(), Guid, NumberOfPlayers, Choise);
+}
+
+void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+4);
+
+ if(!GetPlayer()->GetGroup())
+ return;
+
+ float x, y;
+ recv_data >> x;
+ recv_data >> y;
+
+ //sLog.outDebug("Received opcode MSG_MINIMAP_PING X: %f, Y: %f", x, y);
+
+ /** error handling **/
+ /********************/
+
+ // everything's fine, do it
+ WorldPacket data(MSG_MINIMAP_PING, (8+4+4));
+ data << GetPlayer()->GetGUID();
+ data << x;
+ data << y;
+ GetPlayer()->GetGroup()->BroadcastPacket(&data, -1, GetPlayer()->GetGUID());
+}
+
+void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+4);
+
+ uint32 minimum, maximum, roll;
+ recv_data >> minimum;
+ recv_data >> maximum;
+
+ /** error handling **/
+ if(minimum > maximum || maximum > 10000) // < 32768 for urand call
+ return;
+ /********************/
+
+ // everything's fine, do it
+ roll = urand(minimum, maximum);
+
+ //sLog.outDebug("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll);
+
+ WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8);
+ data << minimum;
+ data << maximum;
+ data << roll;
+ data << GetPlayer()->GetGUID();
+ if(GetPlayer()->GetGroup())
+ GetPlayer()->GetGroup()->BroadcastPacket(&data);
+ else
+ SendPacket(&data);
+}
+
+void WorldSession::HandleRaidIconTargetOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ uint8 x;
+ recv_data >> x;
+
+ /** error handling **/
+ /********************/
+
+ // everything's fine, do it
+ if(x == 0xFF) // target icon request
+ {
+ group->SendTargetIconList(this);
+ }
+ else // target icon update
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,1+8);
+
+ if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+ group->SetTargetIcon(x, guid);
+ }
+}
+
+void WorldSession::HandleRaidConvertOpcode( WorldPacket & /*recv_data*/ )
+{
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ if(_player->InBattleGround())
+ return;
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()) || group->GetMembersCount() < 2)
+ return;
+ /********************/
+
+ // everything's fine, do it (is it 0 (PARTY_OP_INVITE) correct code)
+ SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_OK);
+ group->ConvertToRaid();
+}
+
+void WorldSession::HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ std::string name;
+ uint8 groupNr;
+ recv_data >> name;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1);
+
+ recv_data >> groupNr;
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ return;
+ /********************/
+
+ // everything's fine, do it
+ group->ChangeMembersGroup(objmgr.GetPlayer(name.c_str()), groupNr);
+}
+
+void WorldSession::HandleGroupAssistantOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ uint64 guid;
+ uint8 flag;
+ recv_data >> guid;
+ recv_data >> flag;
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+ /********************/
+
+ // everything's fine, do it
+ group->SetAssistant(guid, (flag==0?false:true));
+}
+
+void WorldSession::HandleGroupPromoteOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+1+8);
+
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ uint8 flag1, flag2;
+ uint64 guid;
+ recv_data >> flag1 >> flag2;
+ recv_data >> guid;
+ // if(flag1) Main Assist
+ // 0x4
+ // if(flag2) Main Tank
+ // 0x2
+
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()))
+ return;
+ /********************/
+
+ // everything's fine, do it
+ if(flag1 == 1)
+ group->SetMainAssistant(guid);
+ if(flag2 == 1)
+ group->SetMainTank(guid);
+}
+
+void WorldSession::HandleRaidReadyCheckOpcode( WorldPacket & recv_data )
+{
+ Group *group = GetPlayer()->GetGroup();
+ if(!group)
+ return;
+
+ if(recv_data.empty()) // request
+ {
+ /** error handling **/
+ if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ return;
+ /********************/
+
+ // everything's fine, do it
+ WorldPacket data(MSG_RAID_READY_CHECK, 8);
+ data << GetPlayer()->GetGUID();
+ group->BroadcastPacket(&data, -1);
+
+ group->OfflineReadyCheck();
+ }
+ else // answer
+ {
+ uint8 state;
+ recv_data >> state;
+
+ // everything's fine, do it
+ WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
+ data << GetPlayer()->GetGUID();
+ data << state;
+ group->BroadcastReadyCheck(&data);
+ }
+}
+
+void WorldSession::HandleRaidReadyCheckFinishOpcode( WorldPacket & recv_data )
+{
+ //Group* group = GetPlayer()->GetGroup();
+ //if(!group)
+ // return;
+
+ //if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ // return;
+
+ // Is any reaction need?
+}
+
+void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data)
+{
+ uint32 mask = player->GetGroupUpdateFlag();
+
+ if (mask == GROUP_UPDATE_FLAG_NONE)
+ return;
+
+ if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) // if update power type, update current/max power also
+ mask |= (GROUP_UPDATE_FLAG_CUR_POWER | GROUP_UPDATE_FLAG_MAX_POWER);
+
+ if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) // same for pets
+ mask |= (GROUP_UPDATE_FLAG_PET_CUR_POWER | GROUP_UPDATE_FLAG_PET_MAX_POWER);
+
+ uint32 byteCount = 0;
+ for (int i = 1; i < GROUP_UPDATE_FLAGS_COUNT; ++i)
+ if (mask & (1 << i))
+ byteCount += GroupUpdateLength[i];
+
+ data->Initialize(SMSG_PARTY_MEMBER_STATS, 8 + 4 + byteCount);
+ data->append(player->GetPackGUID());
+ *data << (uint32) mask;
+
+ if (mask & GROUP_UPDATE_FLAG_STATUS)
+ {
+ if (player)
+ {
+ if (player->IsPvP())
+ *data << (uint16) (MEMBER_STATUS_ONLINE | MEMBER_STATUS_PVP);
+ else
+ *data << (uint16) MEMBER_STATUS_ONLINE;
+ }
+ else
+ *data << (uint16) MEMBER_STATUS_OFFLINE;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_CUR_HP)
+ *data << (uint16) player->GetHealth();
+
+ if (mask & GROUP_UPDATE_FLAG_MAX_HP)
+ *data << (uint16) player->GetMaxHealth();
+
+ Powers powerType = player->getPowerType();
+ if (mask & GROUP_UPDATE_FLAG_POWER_TYPE)
+ *data << (uint8) powerType;
+
+ if (mask & GROUP_UPDATE_FLAG_CUR_POWER)
+ *data << (uint16) player->GetPower(powerType);
+
+ if (mask & GROUP_UPDATE_FLAG_MAX_POWER)
+ *data << (uint16) player->GetMaxPower(powerType);
+
+ if (mask & GROUP_UPDATE_FLAG_LEVEL)
+ *data << (uint16) player->getLevel();
+
+ if (mask & GROUP_UPDATE_FLAG_ZONE)
+ *data << (uint16) player->GetZoneId();
+
+ if (mask & GROUP_UPDATE_FLAG_POSITION)
+ *data << (uint16) player->GetPositionX() << (uint16) player->GetPositionY();
+
+ if (mask & GROUP_UPDATE_FLAG_AURAS)
+ {
+ uint64 auramask = player->GetAuraUpdateMask();
+ *data << uint64(auramask);
+ for(uint32 i = 0; i < MAX_AURAS; ++i)
+ {
+ if(auramask & (uint64(1) << i))
+ {
+ *data << uint16(player->GetUInt32Value(UNIT_FIELD_AURA + i));
+ *data << uint8(1);
+ }
+ }
+ }
+
+ Pet *pet = player->GetPet();
+ if (mask & GROUP_UPDATE_FLAG_PET_GUID)
+ {
+ if(pet)
+ *data << (uint64) pet->GetGUID();
+ else
+ *data << (uint64) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_NAME)
+ {
+ if(pet)
+ *data << pet->GetName();
+ else
+ *data << (uint8) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_MODEL_ID)
+ {
+ if(pet)
+ *data << (uint16) pet->GetDisplayId();
+ else
+ *data << (uint16) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP)
+ {
+ if(pet)
+ *data << (uint16) pet->GetHealth();
+ else
+ *data << (uint16) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP)
+ {
+ if(pet)
+ *data << (uint16) pet->GetMaxHealth();
+ else
+ *data << (uint16) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE)
+ {
+ if(pet)
+ *data << (uint8) pet->getPowerType();
+ else
+ *data << (uint8) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_CUR_POWER)
+ {
+ if(pet)
+ *data << (uint16) pet->GetPower(pet->getPowerType());
+ else
+ *data << (uint16) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_MAX_POWER)
+ {
+ if(pet)
+ *data << (uint16) pet->GetMaxPower(pet->getPowerType());
+ else
+ *data << (uint16) 0;
+ }
+
+ if (mask & GROUP_UPDATE_FLAG_PET_AURAS)
+ {
+ if(pet)
+ {
+ uint64 auramask = pet->GetAuraUpdateMask();
+ *data << uint64(auramask);
+ for(uint32 i = 0; i < MAX_AURAS; ++i)
+ {
+ if(auramask & (uint64(1) << i))
+ {
+ *data << uint16(pet->GetUInt32Value(UNIT_FIELD_AURA + i));
+ *data << uint8(1);
+ }
+ }
+ }
+ else
+ *data << (uint64) 0;
+ }
+}
+
+/*this procedure handles clients CMSG_REQUEST_PARTY_MEMBER_STATS request*/
+void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS");
+ uint64 Guid;
+ recv_data >> Guid;
+
+ Player *player = objmgr.GetPlayer(Guid);
+ if(!player)
+ {
+ WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 3+4+2);
+ data.appendPackGUID(Guid);
+ data << (uint32) GROUP_UPDATE_FLAG_STATUS;
+ data << (uint16) MEMBER_STATUS_OFFLINE;
+ SendPacket(&data);
+ return;
+ }
+
+ Pet *pet = player->GetPet();
+
+ WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 4+2+2+2+1+2*6+8+1+8);
+ data.append(player->GetPackGUID());
+
+ uint32 mask1 = 0x00040BFF; // common mask, real flags used 0x000040BFF
+ if(pet)
+ mask1 = 0x7FFFFFFF; // for hunters and other classes with pets
+
+ Powers powerType = player->getPowerType();
+ data << (uint32) mask1; // group update mask
+ data << (uint16) MEMBER_STATUS_ONLINE; // member's online status
+ data << (uint16) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP
+ data << (uint16) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP
+ data << (uint8) powerType; // GROUP_UPDATE_FLAG_POWER_TYPE
+ data << (uint16) player->GetPower(powerType); // GROUP_UPDATE_FLAG_CUR_POWER
+ data << (uint16) player->GetMaxPower(powerType); // GROUP_UPDATE_FLAG_MAX_POWER
+ data << (uint16) player->getLevel(); // GROUP_UPDATE_FLAG_LEVEL
+ data << (uint16) player->GetZoneId(); // GROUP_UPDATE_FLAG_ZONE
+ data << (uint16) player->GetPositionX(); // GROUP_UPDATE_FLAG_POSITION
+ data << (uint16) player->GetPositionY(); // GROUP_UPDATE_FLAG_POSITION
+
+ uint64 auramask = 0;
+ size_t maskPos = data.wpos();
+ data << (uint64) auramask; // placeholder
+ for(uint8 i = 0; i < MAX_AURAS; ++i)
+ {
+ if(uint32 aura = player->GetUInt32Value(UNIT_FIELD_AURA + i))
+ {
+ auramask |= (uint64(1) << i);
+ data << uint16(aura);
+ data << uint8(1);
+ }
+ }
+ data.put<uint64>(maskPos,auramask); // GROUP_UPDATE_FLAG_AURAS
+
+ if(pet)
+ {
+ Powers petpowertype = pet->getPowerType();
+ data << (uint64) pet->GetGUID(); // GROUP_UPDATE_FLAG_PET_GUID
+ data << pet->GetName(); // GROUP_UPDATE_FLAG_PET_NAME
+ data << (uint16) pet->GetDisplayId(); // GROUP_UPDATE_FLAG_PET_MODEL_ID
+ data << (uint16) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP
+ data << (uint16) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP
+ data << (uint8) petpowertype; // GROUP_UPDATE_FLAG_PET_POWER_TYPE
+ data << (uint16) pet->GetPower(petpowertype); // GROUP_UPDATE_FLAG_PET_CUR_POWER
+ data << (uint16) pet->GetMaxPower(petpowertype); // GROUP_UPDATE_FLAG_PET_MAX_POWER
+
+ uint64 petauramask = 0;
+ size_t petMaskPos = data.wpos();
+ data << (uint64) petauramask; // placeholder
+ for(uint8 i = 0; i < MAX_AURAS; ++i)
+ {
+ if(uint32 petaura = pet->GetUInt32Value(UNIT_FIELD_AURA + i))
+ {
+ petauramask |= (uint64(1) << i);
+ data << (uint16) petaura;
+ data << (uint8) 1;
+ }
+ }
+ data.put<uint64>(petMaskPos,petauramask); // GROUP_UPDATE_FLAG_PET_AURAS
+ }
+ else
+ {
+ data << (uint8) 0; // GROUP_UPDATE_FLAG_PET_NAME
+ data << (uint64) 0; // GROUP_UPDATE_FLAG_PET_AURAS
+ }
+
+ SendPacket(&data);
+}
+
+/*!*/void WorldSession::HandleRequestRaidInfoOpcode( WorldPacket & /*recv_data*/ )
+{
+ // every time the player checks the character screen
+ _player->SendRaidInfo();
+}
+
+/*void WorldSession::HandleGroupCancelOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug( "WORLD: got CMSG_GROUP_CANCEL." );
+}*/
+
+void WorldSession::HandleGroupPassOnLootOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("WORLD: Received CMSG_GROUP_PASS_ON_LOOT");
+
+ uint32 unkn;
+ recv_data >> unkn;
+
+ // ignore if player not loaded
+ if(!GetPlayer()) // needed because STATUS_AUTHED
+ {
+ if(unkn!=0)
+ sLog.outError("CMSG_GROUP_PASS_ON_LOOT value<>0 for not-loaded character!");
+ return;
+ }
+
+ if(unkn!=0)
+ sLog.outError("CMSG_GROUP_PASS_ON_LOOT: activation not implemented!");
+}
diff --git a/src/game/GroupRefManager.h b/src/game/GroupRefManager.h
new file mode 100644
index 00000000000..da849214416
--- /dev/null
+++ b/src/game/GroupRefManager.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _GROUPREFMANAGER
+#define _GROUPREFMANAGER
+
+#include "Utilities/LinkedReference/RefManager.h"
+
+class Group;
+class Player;
+class GroupReference;
+
+class GroupRefManager : public RefManager<Group, Player>
+{
+ public:
+ GroupReference* getFirst() { return ((GroupReference* ) RefManager<Group, Player>::getFirst()); }
+};
+#endif
diff --git a/src/game/GroupReference.cpp b/src/game/GroupReference.cpp
new file mode 100644
index 00000000000..33aff764e92
--- /dev/null
+++ b/src/game/GroupReference.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Player.h"
+#include "Group.h"
+#include "GroupReference.h"
+
+void GroupReference::targetObjectBuildLink()
+{
+ // called from link()
+ getTarget()->LinkMember(this);
+}
+
+void GroupReference::targetObjectDestroyLink()
+{
+ // called from unlink()
+ getTarget()->DelinkMember(this);
+}
+
+void GroupReference::sourceObjectDestroyLink()
+{
+ // called from invalidate()
+ getTarget()->DelinkMember(this);
+}
diff --git a/src/game/GroupReference.h b/src/game/GroupReference.h
new file mode 100644
index 00000000000..1e4453b020a
--- /dev/null
+++ b/src/game/GroupReference.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _GROUPREFERENCE_H
+#define _GROUPREFERENCE_H
+
+#include "Utilities/LinkedReference/Reference.h"
+
+class Group;
+class Player;
+
+class MANGOS_DLL_SPEC GroupReference : public Reference<Group, Player>
+{
+ protected:
+ uint8 iSubGroup;
+ void targetObjectBuildLink();
+ void targetObjectDestroyLink();
+ void sourceObjectDestroyLink();
+ public:
+ GroupReference() : Reference<Group, Player>(), iSubGroup(0) {}
+ ~GroupReference() { unlink(); }
+ GroupReference *next() { return (GroupReference*)Reference<Group, Player>::next(); }
+ uint8 getSubGroup() const { return iSubGroup; }
+ void setSubGroup(uint8 pSubGroup) { iSubGroup = pSubGroup; }
+};
+#endif
diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp
new file mode 100644
index 00000000000..fffbb7d4000
--- /dev/null
+++ b/src/game/GuardAI.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "GuardAI.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "World.h"
+
+int GuardAI::Permissible(const Creature *creature)
+{
+ if( creature->isGuard())
+ return PERMIT_BASE_SPECIAL;
+
+ return PERMIT_BASE_NO;
+}
+
+GuardAI::GuardAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
+{
+}
+
+void GuardAI::MoveInLineOfSight(Unit *u)
+{
+ if( !i_creature.getVictim() && u->isTargetableForAttack() &&
+ ( u->IsHostileToPlayers() || i_creature.IsHostileTo(u) /*|| u->getVictim() && i_creature.IsFriendlyTo(u->getVictim())*/ ) &&
+ u->isInAccessablePlaceFor(&i_creature))
+ {
+ float attackRadius = i_creature.GetAttackDistance(u);
+ if(i_creature.IsWithinDistInMap(u,attackRadius) && i_creature.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
+ {
+ //Need add code to let guard support player
+ AttackStart(u);
+ u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ }
+ }
+}
+
+void GuardAI::EnterEvadeMode()
+{
+ if( !i_creature.isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", i_creature.GetGUIDLow());
+ i_creature.StopMoving();
+ i_creature.GetMotionMaster()->MoveIdle();
+
+ i_state = STATE_NORMAL;
+
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.DeleteThreatList();
+ return;
+ }
+
+ Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
+
+ if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( !victim ->isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim ->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim ->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", i_creature.GetGUIDLow());
+ }
+
+ i_creature.RemoveAllAuras();
+ i_creature.DeleteThreatList();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_state = STATE_NORMAL;
+
+ // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+ if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ i_creature.GetMotionMaster()->MoveTargetedHome();
+}
+
+void GuardAI::UpdateAI(const uint32 /*diff*/)
+{
+ // update i_victimGuid if i_creature.getVictim() !=0 and changed
+ if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
+ return;
+
+ i_victimGuid = i_creature.getVictim()->GetGUID();
+
+ if( i_creature.isAttackReady() )
+ {
+ if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
+ {
+ i_creature.AttackerStateUpdate(i_creature.getVictim());
+ i_creature.resetAttackTimer();
+ }
+ }
+}
+
+bool GuardAI::IsVisible(Unit *pl) const
+{
+ return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
+ && pl->isVisibleForOrDetect(&i_creature,true);
+}
+
+void GuardAI::AttackStart(Unit *u)
+{
+ if( !u )
+ return;
+
+ // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
+ if(i_creature.Attack(u,true))
+ {
+ i_creature.SetInCombatWith(u);
+ u->SetInCombatWith(&i_creature);
+
+ i_creature.AddThreat(u, 0.0f);
+ i_victimGuid = u->GetGUID();
+ i_creature.GetMotionMaster()->MoveChase(u);
+ }
+}
+
+void GuardAI::JustDied(Unit *killer)
+{
+ if(Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
+ i_creature.SendZoneUnderAttackMessage(pkiller);
+}
+
diff --git a/src/game/GuardAI.h b/src/game/GuardAI.h
new file mode 100644
index 00000000000..f11dad6870e
--- /dev/null
+++ b/src/game/GuardAI.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_GUARDAI_H
+#define MANGOS_GUARDAI_H
+
+#include "CreatureAI.h"
+#include "Timer.h"
+
+class Creature;
+
+class MANGOS_DLL_DECL GuardAI : public CreatureAI
+{
+ enum GuardState
+ {
+ STATE_NORMAL = 1,
+ STATE_LOOK_AT_VICTIM = 2
+ };
+
+ public:
+
+ GuardAI(Creature &c);
+
+ void MoveInLineOfSight(Unit *);
+ void AttackStart(Unit *);
+ void EnterEvadeMode();
+ void JustDied(Unit *);
+ bool IsVisible(Unit *) const;
+
+ void UpdateAI(const uint32);
+ static int Permissible(const Creature *);
+
+ private:
+ Creature &i_creature;
+ uint64 i_victimGuid;
+ GuardState i_state;
+ TimeTracker i_tracker;
+};
+#endif
diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp
new file mode 100644
index 00000000000..bce616be394
--- /dev/null
+++ b/src/game/Guild.cpp
@@ -0,0 +1,1940 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "MapManager.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "Guild.h"
+#include "Chat.h"
+#include "SocialMgr.h"
+#include "Util.h"
+
+Guild::Guild()
+{
+ Id = 0;
+ name = "";
+ leaderGuid = 0;
+ GINFO = MOTD = "";
+ EmblemStyle = 0;
+ EmblemColor = 0;
+ BorderStyle = 0;
+ BorderColor = 0;
+ BackgroundColor = 0;
+
+ CreatedYear = 0;
+ CreatedMonth = 0;
+ CreatedDay = 0;
+}
+
+Guild::~Guild()
+{
+
+}
+
+bool Guild::create(uint64 lGuid, std::string gname)
+{
+ std::string rname;
+ std::string lName;
+
+ if(!objmgr.GetPlayerNameByGUID(lGuid, lName))
+ return false;
+ if(objmgr.GetGuildByName(gname))
+ return false;
+
+ sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(lGuid));
+
+ leaderGuid = lGuid;
+ name = gname;
+ GINFO = "";
+ MOTD = "No message set.";
+ guildbank_money = 0;
+ purchased_tabs = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" );
+ if( result )
+ {
+ Id = (*result)[0].GetUInt32()+1;
+ delete result;
+ }
+ else Id = 1;
+
+ // gname already assigned to Guild::name, use it to encode string for DB
+ CharacterDatabase.escape_string(gname);
+
+ std::string dbGINFO = GINFO;
+ std::string dbMOTD = MOTD;
+ CharacterDatabase.escape_string(dbGINFO);
+ CharacterDatabase.escape_string(dbMOTD);
+
+ CharacterDatabase.BeginTransaction();
+ // CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid='%u'", Id); - MAX(guildid)+1 not exist
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guildid='%u'", Id);
+ CharacterDatabase.PExecute("INSERT INTO guild (guildid,name,leaderguid,info,motd,createdate,EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor,BankMoney) "
+ "VALUES('%u','%s','%u', '%s', '%s', NOW(),'%u','%u','%u','%u','%u','" I64FMTD "')",
+ Id, gname.c_str(), GUID_LOPART(leaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, guildbank_money);
+ CharacterDatabase.CommitTransaction();
+
+ rname = "Guild Master";
+ CreateRank(rname,GR_RIGHT_ALL);
+ rname = "Officer";
+ CreateRank(rname,GR_RIGHT_ALL);
+ rname = "Veteran";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ rname = "Member";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ rname = "Initiate";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+
+ return AddMember(lGuid, (uint32)GR_GUILDMASTER);
+}
+
+bool Guild::AddMember(uint64 plGuid, uint32 plRank)
+{
+ if(Player::GetGuildIdFromDB(plGuid) != 0) // player already in guild
+ return false;
+
+ // remove all player signs from another petitions
+ // this will be prevent attempt joining player to many guilds and corrupt guild data integrity
+ Player::RemovePetitionsAndSigns(plGuid, 9);
+
+ // fill player data
+ MemberSlot newmember;
+
+ if(!FillPlayerData(plGuid, &newmember)) // problems with player data collection
+ return false;
+
+ newmember.RankId = plRank;
+ newmember.OFFnote = (std::string)"";
+ newmember.Pnote = (std::string)"";
+ newmember.logout_time = time(NULL);
+ newmember.BankResetTimeMoney = 0; // this will force update at first query
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ newmember.BankResetTimeTab[i] = 0;
+ members[GUID_LOPART(plGuid)] = newmember;
+
+ std::string dbPnote = newmember.Pnote;
+ std::string dbOFFnote = newmember.OFFnote;
+ CharacterDatabase.escape_string(dbPnote);
+ CharacterDatabase.escape_string(dbOFFnote);
+
+ CharacterDatabase.PExecute("INSERT INTO guild_member (guildid,guid,rank,pnote,offnote) VALUES ('%u', '%u', '%u','%s','%s')",
+ Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str());
+
+ Player* pl = objmgr.GetPlayer(plGuid);
+ if(pl)
+ {
+ pl->SetInGuild(Id);
+ pl->SetRank(newmember.RankId);
+ pl->SetGuildIdInvited(0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDID, Id, plGuid);
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newmember.RankId, plGuid);
+ }
+ return true;
+}
+
+void Guild::SetMOTD(std::string motd)
+{
+ MOTD = motd;
+
+ // motd now can be used for encoding to DB
+ CharacterDatabase.escape_string(motd);
+ CharacterDatabase.PExecute("UPDATE guild SET motd='%s' WHERE guildid='%u'", motd.c_str(), Id);
+}
+
+void Guild::SetGINFO(std::string ginfo)
+{
+ GINFO = ginfo;
+
+ // ginfo now can be used for encoding to DB
+ CharacterDatabase.escape_string(ginfo);
+ CharacterDatabase.PExecute("UPDATE guild SET info='%s' WHERE guildid='%u'", ginfo.c_str(), Id);
+}
+
+bool Guild::LoadGuildFromDB(uint32 GuildId)
+{
+ if(!LoadRanksFromDB(GuildId))
+ return false;
+
+ if(!LoadMembersFromDB(GuildId))
+ return false;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT MAX(TabId) FROM guild_bank_tab WHERE guildid='%u'", GuildId);
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ purchased_tabs = fields[0].GetUInt8()+1; // Because TabId begins at 0
+ delete result;
+ }
+ else
+ purchased_tabs = 0;
+
+ LoadBankRightsFromDB(GuildId); // Must be after LoadRanksFromDB because it populates rank struct
+
+ // 0 1 2 3 4 5 6
+ result = CharacterDatabase.PQuery("SELECT guildid, name, leaderguid, EmblemStyle, EmblemColor, BorderStyle, BorderColor,"
+ // 7 8 9 10 11
+ "BackgroundColor, info, motd, createdate, BankMoney FROM guild WHERE guildid = '%u'", GuildId);
+
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ Id = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ leaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ EmblemStyle = fields[3].GetUInt32();
+ EmblemColor = fields[4].GetUInt32();
+ BorderStyle = fields[5].GetUInt32();
+ BorderColor = fields[6].GetUInt32();
+ BackgroundColor = fields[7].GetUInt32();
+ GINFO = fields[8].GetCppString();
+ MOTD = fields[9].GetCppString();
+ uint64 time = fields[10].GetUInt64(); //datetime is uint64 type ... YYYYmmdd:hh:mm:ss
+ guildbank_money = fields[11].GetUInt64();
+
+ delete result;
+
+ uint64 dTime = time /1000000;
+ CreatedDay = dTime%100;
+ CreatedMonth = (dTime/100)%100;
+ CreatedYear = (dTime/10000)%10000;
+
+ // If the leader does not exist attempt to promote another member
+ if(!objmgr.GetPlayerAccountIdByGUID(leaderGuid ))
+ {
+ DelMember(leaderGuid);
+
+ // check no members case (disbanded)
+ if(members.empty())
+ return false;
+ }
+
+ sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, CreatedDay, CreatedMonth, CreatedYear);
+ m_bankloaded = false;
+ m_eventlogloaded = false;
+ m_onlinemembers = 0;
+ RenumBankLogs();
+ RenumGuildEventlog();
+ return true;
+}
+
+bool Guild::LoadRanksFromDB(uint32 GuildId)
+{
+ Field *fields;
+ QueryResult *result = CharacterDatabase.PQuery("SELECT rname,rights,BankMoneyPerDay,rid FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId);
+
+ if(!result)
+ return false;
+
+ bool broken_ranks = false;
+
+ do
+ {
+ fields = result->Fetch();
+
+ std::string rankName = fields[0].GetCppString();
+ uint32 rankRights = fields[1].GetUInt32();
+ uint32 rankMoney = fields[2].GetUInt32();
+ uint32 rankRID = fields[3].GetUInt32();
+
+ if(rankRID != m_ranks.size()+1) // guild_rank.rid always store rank+1
+ broken_ranks = true;
+
+ if(m_ranks.size()==GR_GUILDMASTER) // prevent loss leader rights
+ rankRights |= GR_RIGHT_ALL;
+
+ AddRank(rankName,rankRights,rankMoney);
+ }while( result->NextRow() );
+ delete result;
+
+ if(m_ranks.size()==0) // empty rank table?
+ {
+ AddRank("Guild Master",GR_RIGHT_ALL,0);
+ broken_ranks = true;
+ }
+
+ // guild_rank have wrong numbered ranks, repair
+ if(broken_ranks)
+ {
+ sLog.outError("Guild %u have broken `guild_rank` data, repairing...",GuildId);
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", GuildId);
+ for(size_t i =0; i < m_ranks.size(); ++i)
+ {
+ // guild_rank.rid always store rank+1
+ 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.CommitTransaction();
+ }
+
+ return true;
+}
+
+bool Guild::LoadMembersFromDB(uint32 GuildId)
+{
+ // 0 1 2 3 4 5
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guild_member.guid,rank, pnote, offnote, BankResetTimeMoney,BankRemMoney,"
+ // 6 7 8 9 10 11
+ "BankResetTimeTab0, BankRemSlotsTab0, BankResetTimeTab1, BankRemSlotsTab1, BankResetTimeTab2, BankRemSlotsTab2,"
+ // 12 13 14 15 16 17
+ "BankResetTimeTab3, BankRemSlotsTab3, BankResetTimeTab4, BankRemSlotsTab4, BankResetTimeTab5, BankRemSlotsTab5,"
+ // 18
+ "logout_time FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid WHERE guildid = '%u'", GuildId);
+
+ if(!result)
+ return false;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ MemberSlot newmember;
+ newmember.RankId = fields[1].GetUInt32();
+ uint64 guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ // Player does not exist
+ if(!FillPlayerData(guid, &newmember))
+ continue;
+
+ newmember.Pnote = fields[2].GetCppString();
+ newmember.OFFnote = fields[3].GetCppString();
+ newmember.BankResetTimeMoney = fields[4].GetUInt32();
+ newmember.BankRemMoney = fields[5].GetUInt32();
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ newmember.BankResetTimeTab[i] = fields[6+(2*i)].GetUInt32();
+ newmember.BankRemSlotsTab[i] = fields[7+(2*i)].GetUInt32();
+ }
+ newmember.logout_time = fields[18].GetUInt64();
+ members[GUID_LOPART(guid)] = newmember;
+
+ }while( result->NextRow() );
+ delete result;
+
+ if(members.empty())
+ return false;
+
+ return true;
+}
+
+bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot)
+{
+ std::string plName;
+ uint32 plLevel;
+ uint32 plClass;
+ uint32 plZone;
+
+ Player* pl = objmgr.GetPlayer(guid);
+ if(pl)
+ {
+ plName = pl->GetName();
+ plLevel = pl->getLevel();
+ plClass = pl->getClass();
+ plZone = pl->GetZoneId();
+ }
+ else
+ {
+ if(!objmgr.GetPlayerNameByGUID(guid, plName)) // player doesn't exist
+ return false;
+
+ plLevel = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL, guid);
+ if(plLevel<1||plLevel>255) // can be at broken `data` field
+ {
+ sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`data`.",GUID_LOPART(guid));
+ return false;
+ }
+ plZone = Player::GetZoneIdFromDB(guid);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid='%u'", GUID_LOPART(guid));
+ if(!result)
+ return false;
+ plClass = (*result)[0].GetUInt32();
+ if(plClass<CLASS_WARRIOR||plClass>=MAX_CLASSES) // can be at broken `class` field
+ {
+ sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`class`.",GUID_LOPART(guid));
+ return false;
+ }
+
+ delete result;
+ }
+
+ memslot->name = plName;
+ memslot->level = plLevel;
+ memslot->Class = plClass;
+ memslot->zoneId = plZone;
+
+ return(true);
+}
+
+void Guild::LoadPlayerStatsByGuid(uint64 guid)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if (itr == members.end() )
+ return;
+
+ Player *pl = ObjectAccessor::FindPlayer(guid);
+ if(!pl)
+ return;
+ itr->second.name = pl->GetName();
+ itr->second.level = pl->getLevel();
+ itr->second.Class = pl->getClass();
+}
+
+void Guild::SetLeader(uint64 guid)
+{
+ leaderGuid = guid;
+ this->ChangeRank(guid, GR_GUILDMASTER);
+
+ CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), Id);
+}
+
+void Guild::DelMember(uint64 guid, bool isDisbanding)
+{
+ if(this->leaderGuid == guid && !isDisbanding)
+ {
+ std::ostringstream ss;
+ ss<<"SELECT guid FROM guild_member WHERE guildid='"<<Id<<"' AND guid!='"<<this->leaderGuid<<"' ORDER BY rank ASC LIMIT 1";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint64 newLeaderGUID;
+ Player *newLeader;
+ std::string newLeaderName, oldLeaderName;
+
+ newLeaderGUID = (*result)[0].GetUInt64();
+ delete result;
+
+ this->SetLeader(newLeaderGUID);
+
+ newLeader = objmgr.GetPlayer(newLeaderGUID);
+ if(newLeader)
+ {
+ newLeader->SetRank(GR_GUILDMASTER);
+ newLeaderName = newLeader->GetName();
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, newLeaderGUID);
+ objmgr.GetPlayerNameByGUID(newLeaderGUID, newLeaderName);
+ }
+
+ // when leader non-exist (at guild load with deleted leader only) not send broadcasts
+ if(objmgr.GetPlayerNameByGUID(guid, oldLeaderName))
+ {
+ WorldPacket data(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1+newLeaderName.size()+1));
+ data << (uint8)GE_LEADER_CHANGED;
+ data << (uint8)2;
+ data << oldLeaderName;
+ data << newLeaderName;
+ this->BroadcastPacket(&data);
+
+ data.Initialize(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1));
+ data << (uint8)GE_LEFT;
+ data << (uint8)1;
+ data << oldLeaderName;
+ this->BroadcastPacket(&data);
+ }
+
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_EVENT)" );
+ }
+ else
+ {
+ this->Disband();
+ return;
+ }
+ }
+
+ members.erase(GUID_LOPART(guid));
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ {
+ player->SetInGuild(0);
+ player->SetRank(0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDID, 0, guid);
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, guid);
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid));
+}
+
+void Guild::ChangeRank(uint64 guid, uint32 newRank)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr != members.end() )
+ itr->second.RankId = newRank;
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ player->SetRank(newRank);
+ else
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newRank, guid);
+
+ CharacterDatabase.PExecute( "UPDATE guild_member SET rank='%u' WHERE guid='%u'", newRank, GUID_LOPART(guid) );
+}
+
+void Guild::SetPNOTE(uint64 guid,std::string pnote)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr == members.end() )
+ return;
+
+ itr->second.Pnote = pnote;
+
+ // pnote now can be used for encoding to DB
+ CharacterDatabase.escape_string(pnote);
+ CharacterDatabase.PExecute("UPDATE guild_member SET pnote = '%s' WHERE guid = '%u'", pnote.c_str(), itr->first);
+}
+
+void Guild::SetOFFNOTE(uint64 guid,std::string offnote)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr == members.end() )
+ return;
+ itr->second.OFFnote = offnote;
+ // offnote now can be used for encoding to DB
+ CharacterDatabase.escape_string(offnote);
+ CharacterDatabase.PExecute("UPDATE guild_member SET offnote = '%s' WHERE guid = '%u'", offnote.c_str(), itr->first);
+}
+
+void Guild::BroadcastToGuild(WorldSession *session, std::string msg, uint32 language)
+{
+ if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_GCHATSPEAK))
+ {
+ WorldPacket data;
+ ChatHandler(session).FillMessageData(&data, CHAT_MSG_GUILD, language, 0, msg.c_str());
+
+ for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_GCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()) )
+ pl->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Guild::BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language)
+{
+ if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_OFFCHATSPEAK))
+ {
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, session, CHAT_MSG_OFFICER, language, NULL, 0, msg.c_str(),NULL);
+
+ Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_OFFCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()))
+ pl->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Guild::BroadcastPacket(WorldPacket *packet)
+{
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(player)
+ player->GetSession()->SendPacket(packet);
+ }
+}
+
+void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId)
+{
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ if (itr->second.RankId == rankId)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(player)
+ player->GetSession()->SendPacket(packet);
+ }
+ }
+}
+
+void Guild::CreateRank(std::string name_,uint32 rights)
+{
+ if(m_ranks.size() >= GUILD_MAX_RANKS)
+ return;
+
+ AddRank(name_,rights,0);
+
+ for (int i = 0; i < purchased_tabs; ++i)
+ {
+ CreateBankRightForTab(m_ranks.size()-1, uint8(i));
+ }
+
+ // guild_rank.rid always store rank+1 value
+
+ // name now can be used for encoding to DB
+ CharacterDatabase.escape_string(name_);
+ CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, m_ranks.size(), name_.c_str(), rights );
+}
+
+void Guild::AddRank(std::string name_,uint32 rights, uint32 money)
+{
+ m_ranks.push_back(RankInfo(name_,rights,money));
+}
+
+void Guild::DelRank()
+{
+ if(m_ranks.empty())
+ return;
+
+ // guild_rank.rid always store rank+1 value
+ uint32 rank = m_ranks.size()-1;
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", (rank+1), Id);
+
+ m_ranks.pop_back();
+}
+
+std::string Guild::GetRankName(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return "<unknown>";
+
+ return m_ranks[rankId].name;
+}
+
+uint32 Guild::GetRankRights(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return 0;
+
+ return m_ranks[rankId].rights;
+}
+
+void Guild::SetRankName(uint32 rankId, std::string name_)
+{
+ if(rankId >= m_ranks.size())
+ return;
+
+ m_ranks[rankId].name = name_;
+
+ // name now can be used for encoding to DB
+ CharacterDatabase.escape_string(name_);
+ CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), (rankId+1), Id);
+}
+
+void Guild::SetRankRights(uint32 rankId, uint32 rights)
+{
+ if(rankId >= m_ranks.size())
+ return;
+
+ m_ranks[rankId].rights = rights;
+
+ CharacterDatabase.PExecute("UPDATE guild_rank SET rights='%u' WHERE rid='%u' AND guildid='%u'", rights, (rankId+1), Id);
+}
+
+void Guild::Disband()
+{
+ WorldPacket data(SMSG_GUILD_EVENT, 1);
+ data << (uint8)GE_DISBANDED;
+ this->BroadcastPacket(&data);
+
+ while (!members.empty())
+ {
+ MemberList::iterator itr = members.begin();
+ DelMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER), true);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid = '%u'",Id);
+ CharacterDatabase.CommitTransaction();
+ objmgr.RemoveGuild(this);
+}
+
+void Guild::Roster(WorldSession *session)
+{
+ // we can only guess size
+ WorldPacket data(SMSG_GUILD_ROSTER, (4+MOTD.length()+1+GINFO.length()+1+4+m_ranks.size()*(4+4+GUILD_BANK_MAX_TABS*(4+4))+members.size()*50));
+ data << (uint32)members.size();
+ data << MOTD;
+ data << GINFO;
+
+ data << (uint32)m_ranks.size();
+ for (RankList::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 (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)
+ }
+ }
+ for (MemberList::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 << itr->second.Pnote;
+ data << itr->second.OFFnote;
+ }
+ else
+ {
+ data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ 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 << itr->second.Pnote;
+ data << itr->second.OFFnote;
+ }
+ }
+ session->SendPacket(&data);;
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_ROSTER)" );
+}
+
+void Guild::Query(WorldSession *session)
+{
+ WorldPacket data(SMSG_GUILD_QUERY_RESPONSE, (8*32+200));// we can only guess size
+
+ data << Id;
+ data << name;
+ RankList::iterator itr;
+ for (size_t i = 0 ; i < 10; ++i) // show always 10 ranks
+ {
+ if(i < m_ranks.size())
+ data << m_ranks[i].name;
+ else
+ data << (uint8)0; // null string
+ }
+
+ data << uint32(EmblemStyle);
+ data << uint32(EmblemColor);
+ data << uint32(BorderStyle);
+ data << uint32(BorderColor);
+ data << uint32(BackgroundColor);
+
+ session->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)" );
+}
+
+void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor)
+{
+ this->EmblemStyle = emblemStyle;
+ this->EmblemColor = emblemColor;
+ this->BorderStyle = borderStyle;
+ this->BorderColor = borderColor;
+ this->BackgroundColor = backgroundColor;
+
+ CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, Id);
+}
+
+void Guild::UpdateLogoutTime(uint64 guid)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if (itr == members.end() )
+ return;
+
+ itr->second.logout_time = time(NULL);
+
+ if (m_onlinemembers > 0)
+ --m_onlinemembers;
+ else
+ {
+ UnloadGuildBank();
+ UnloadGuildEventlog();
+ }
+}
+
+// *************************************************
+// Guild Eventlog part
+// *************************************************
+// Display guild eventlog
+void Guild::DisplayGuildEventlog(WorldSession *session)
+{
+ // Load guild eventlog, if not already done
+ if (!m_eventlogloaded)
+ LoadGuildEventLogFromDB();
+
+ // Sending result
+ WorldPacket data(MSG_GUILD_EVENT_LOG_QUERY, 0);
+ // count, max count == 100
+ data << uint8(m_GuildEventlog.size());
+ for (GuildEventlog::const_iterator itr = m_GuildEventlog.begin(); itr != m_GuildEventlog.end(); ++itr)
+ {
+ // Event type
+ data << uint8((*itr)->EventType);
+ // Player 1
+ 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);
+ // 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);
+ // Event timestamp
+ data << uint32(time(NULL)-(*itr)->TimeStamp);
+ }
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent (MSG_GUILD_EVENT_LOG_QUERY)");
+}
+
+// Load guild eventlog from DB
+void Guild::LoadGuildEventLogFromDB()
+{
+ // Return if already loaded
+ if (m_eventlogloaded)
+ return;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY LogGuid DESC LIMIT %u", Id, GUILD_EVENTLOG_MAX_ENTRIES);
+ if(!result)
+ return;
+ do
+ {
+ Field *fields = result->Fetch();
+ GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
+ // 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();
+ // Add entry to map
+ m_GuildEventlog.push_front(NewEvent);
+
+ } while( result->NextRow() );
+ delete result;
+
+ // 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);
+ }
+ m_eventlogloaded = true;
+}
+
+// Unload guild eventlog
+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_eventlogloaded = false;
+}
+
+// This will renum guids used at load to prevent always going up until infinit
+void Guild::RenumGuildEventlog()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_eventlog WHERE guildid = %u", Id);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ CharacterDatabase.PExecute("UPDATE guild_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
+ GuildEventlogMaxGuid = fields[1].GetUInt32()+1;
+ delete result;
+}
+
+// Add entry to guild eventlog
+void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank)
+{
+ GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
+ // Fill entry
+ 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());
+ 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','" I64FMTD "')",
+ Id, NewEvent->LogGuid, uint32(NewEvent->EventType), NewEvent->PlayerGuid1, NewEvent->PlayerGuid2, uint32(NewEvent->NewRank), NewEvent->TimeStamp);
+}
+
+// *************************************************
+// Guild Bank part
+// *************************************************
+// Bank content related
+void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId)
+{
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ if(!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ return;
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+ data << uint32(GetMemberSlotWithdrawRem(session->GetPlayer()->GetGUIDLow(), TabId));
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(GUILD_BANK_MAX_SLOTS);
+
+ for (int i=0; i<GUILD_BANK_MAX_SLOTS; ++i)
+ AppendDisplayGuildBankSlot(data, tab, i);
+
+ session->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankMoneyUpdate()
+{
+ WorldPacket data(SMSG_GUILD_BANK_LIST, 8+1+4+1+1);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(0);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(0); // not send items
+
+ BroadcastPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2)
+{
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ if(slot2==-1) // single item in slot1
+ {
+ data << uint8(1);
+
+ AppendDisplayGuildBankSlot(data, tab, slot1);
+ }
+ else // 2 items (in slot1 and slot2)
+ {
+ data << uint8(2);
+
+ if(slot1 > slot2)
+ std::swap(slot1,slot2);
+
+ AppendDisplayGuildBankSlot(data, tab, slot1);
+ AppendDisplayGuildBankSlot(data, tab, slot2);
+ }
+
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(!player)
+ continue;
+
+ if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ continue;
+
+ data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
+ player->GetSession()->SendPacket(&data);
+ }
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots)
+{
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(slots.size()); // updates count
+
+ for(GuildItemPosCountVec::const_iterator itr = slots.begin(); itr != slots.end(); ++itr)
+ AppendDisplayGuildBankSlot(data, tab, itr->slot);
+
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(!player)
+ continue;
+
+ if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ continue;
+
+ data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
+ player->GetSession()->SendPacket(&data);
+ }
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+Item* Guild::GetItem(uint8 TabId, uint8 SlotId)
+{
+ if (TabId >= m_TabListMap.size() || SlotId >= GUILD_BANK_MAX_SLOTS)
+ return NULL;
+ return m_TabListMap[TabId]->Slots[SlotId];
+}
+
+// *************************************************
+// Tab related
+
+void Guild::DisplayGuildBankTabsInfo(WorldSession *session)
+{
+ // Time to load bank if not already done
+ if (!m_bankloaded)
+ LoadGuildBankFromDB();
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST, 500);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(0); // TabInfo packet must be for TabId 0
+ data << uint32(0xFFFFFFFF); // bit 9 must be set for this packet to work
+ data << uint8(1); // Tell Client this is a TabInfo packet
+
+ data << uint8(purchased_tabs); // here is the number of tabs
+
+ for(int i = 0; i < purchased_tabs; ++i)
+ {
+ data << m_TabListMap[i]->Name.c_str();
+ data << m_TabListMap[i]->Icon.c_str();
+ }
+ data << uint8(0); // Do not send tab content
+ session->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::CreateNewBankTab()
+{
+ if (purchased_tabs >= GUILD_BANK_MAX_TABS)
+ return;
+
+ ++purchased_tabs;
+
+ GuildBankTab* AnotherTab = new GuildBankTab;
+ memset(AnotherTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
+ m_TabListMap.resize(purchased_tabs);
+ m_TabListMap[purchased_tabs-1] = AnotherTab;
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", Id, uint32(purchased_tabs-1));
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", Id, uint32(purchased_tabs-1));
+ CharacterDatabase.CommitTransaction();
+}
+
+void Guild::SetGuildBankTabInfo(uint8 TabId, std::string Name, std::string Icon)
+{
+ if (TabId >= GUILD_BANK_MAX_TABS)
+ return;
+ if (TabId >= m_TabListMap.size())
+ return;
+
+ if (!m_TabListMap[TabId])
+ return;
+
+ if(m_TabListMap[TabId]->Name == Name && m_TabListMap[TabId]->Icon == Icon)
+ return;
+
+ m_TabListMap[TabId]->Name = Name;
+ m_TabListMap[TabId]->Icon = Icon;
+
+ CharacterDatabase.escape_string(Name);
+ CharacterDatabase.escape_string(Icon);
+ CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabName='%s',TabIcon='%s' WHERE guildid='%u' AND TabId='%u'", Name.c_str(), Icon.c_str(), Id, uint32(TabId));
+}
+
+void Guild::CreateBankRightForTab(uint32 rankId, uint8 TabId)
+{
+ sLog.outDebug("CreateBankRightForTab. rank: %u, TabId: %u", rankId, uint32(TabId));
+ if (rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return;
+
+ m_ranks[rankId].TabRight[TabId]=0;
+ m_ranks[rankId].TabSlotPerDay[TabId]=0;
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u' AND TabId = '%u' AND rid = '%u'", Id, uint32(TabId), rankId);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid) VALUES ('%u','%u','%u')", Id, uint32(TabId), rankId);
+ CharacterDatabase.CommitTransaction();
+}
+
+uint32 Guild::GetBankRights(uint32 rankId, uint8 TabId) const
+{
+ if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return 0;
+
+ return m_ranks[rankId].TabRight[TabId];
+}
+
+// *************************************************
+// Guild bank loading/unloading related
+
+// This load should be called when the bank is first accessed by a guild member
+void Guild::LoadGuildBankFromDB()
+{
+ if (m_bankloaded)
+ return;
+
+ m_bankloaded = true;
+ LoadGuildBankEventLogFromDB();
+
+ // 0 1 2 3
+ QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, TabName, TabIcon, TabText FROM guild_bank_tab WHERE guildid='%u' ORDER BY TabId", Id);
+ if(!result)
+ {
+ purchased_tabs = 0;
+ return;
+ }
+
+ m_TabListMap.resize(purchased_tabs);
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+
+ GuildBankTab *NewTab = new GuildBankTab;
+ memset(NewTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
+
+ NewTab->Name = fields[1].GetCppString();
+ NewTab->Icon = fields[2].GetCppString();
+ NewTab->Text = fields[3].GetCppString();
+
+ m_TabListMap[TabId] = NewTab;
+ }while( result->NextRow() );
+
+ delete result;
+
+ // 0 1 2 3
+ result = CharacterDatabase.PQuery("SELECT TabId, SlotId, item_guid, item_entry FROM guild_bank_item WHERE guildid='%u' ORDER BY TabId", Id);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+ uint8 SlotId = fields[1].GetUInt8();
+ uint32 ItemGuid = fields[2].GetUInt32();
+ uint32 ItemEntry = fields[3].GetUInt32();
+
+ if (TabId >= purchased_tabs || TabId >= GUILD_BANK_MAX_TABS)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Invalid tab for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ if (SlotId >= GUILD_BANK_MAX_SLOTS)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Invalid slot for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ ItemPrototype const *proto = objmgr.GetItemPrototype(ItemEntry);
+
+ if(!proto)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Unknown item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ Item *pItem = NewItemOrBag(proto);
+ if(!pItem->LoadFromDB(ItemGuid, 0))
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", Id, uint32(TabId), uint32(SlotId));
+ sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid);
+ delete pItem;
+ continue;
+ }
+
+ pItem->AddToWorld();
+ m_TabListMap[TabId]->Slots[SlotId] = pItem;
+ }while( result->NextRow() );
+
+ delete result;
+}
+
+// This unload should be called when the last member of the guild gets offline
+void Guild::UnloadGuildBank()
+{
+ if (!m_bankloaded)
+ return;
+ for (uint8 i = 0 ; i < purchased_tabs ; ++i )
+ {
+ for (uint8 j = 0 ; j < GUILD_BANK_MAX_SLOTS ; ++j)
+ {
+ if (m_TabListMap[i]->Slots[j])
+ {
+ m_TabListMap[i]->Slots[j]->RemoveFromWorld();
+ delete m_TabListMap[i]->Slots[j];
+ }
+ }
+ delete m_TabListMap[i];
+ }
+ m_TabListMap.clear();
+
+ UnloadGuildBankEventLog();
+ m_bankloaded = false;
+}
+
+// *************************************************
+// Money deposit/withdraw related
+
+void Guild::SendMoneyInfo(WorldSession *session, uint32 LowGuid)
+{
+ WorldPacket data(MSG_GUILD_BANK_MONEY_WITHDRAWN, 4);
+ data << uint32(GetMemberMoneyWithdrawRem(LowGuid));
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent MSG_GUILD_BANK_MONEY_WITHDRAWN");
+}
+
+bool Guild::MemberMoneyWithdraw(uint32 amount, uint32 LowGuid)
+{
+ uint32 MoneyWithDrawRight = GetMemberMoneyWithdrawRem(LowGuid);
+
+ if (MoneyWithDrawRight < amount || GetGuildBankMoney() < amount)
+ return false;
+
+ SetBankMoney(GetGuildBankMoney()-amount);
+
+ if (MoneyWithDrawRight < WITHDRAW_MONEY_UNLIMITED)
+ {
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+ itr->second.BankRemMoney -= amount;
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
+ itr->second.BankRemMoney, Id, LowGuid);
+ }
+ return true;
+}
+
+void Guild::SetBankMoney(int64 money)
+{
+ if (money < 0) // I don't know how this happens, it does!!
+ money = 0;
+ guildbank_money = money;
+
+ CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" I64FMTD "' WHERE guildid='%u'", money, Id);
+}
+
+// *************************************************
+// Item per day and money per day related
+
+bool Guild::MemberItemWithdraw(uint8 TabId, uint32 LowGuid)
+{
+ uint32 SlotsWithDrawRight = GetMemberSlotWithdrawRem(LowGuid, TabId);
+
+ if (SlotsWithDrawRight == 0)
+ return false;
+
+ if (SlotsWithDrawRight < WITHDRAW_SLOT_UNLIMITED)
+ {
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+ --itr->second.BankRemSlotsTab[TabId];
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
+ uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
+ }
+ return true;
+}
+
+bool Guild::IsMemberHaveRights(uint32 LowGuid, uint8 TabId, uint32 rights) const
+{
+ MemberList::const_iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return true;
+
+ return (GetBankRights(itr->second.RankId,TabId) & rights)==rights;
+}
+
+uint32 Guild::GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId)
+{
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return 0;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return WITHDRAW_SLOT_UNLIMITED;
+
+ if((GetBankRights(itr->second.RankId,TabId) & GUILD_BANK_RIGHT_VIEW_TAB)!=GUILD_BANK_RIGHT_VIEW_TAB)
+ return 0;
+
+ uint32 curTime = uint32(time(NULL)/MINUTE);
+ if (curTime - itr->second.BankResetTimeTab[TabId] >= 24*HOUR/MINUTE)
+ {
+ itr->second.BankResetTimeTab[TabId] = curTime;
+ itr->second.BankRemSlotsTab[TabId] = GetBankSlotPerDay(itr->second.RankId, TabId);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='%u',BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
+ uint32(TabId), itr->second.BankResetTimeTab[TabId], uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
+ }
+ return itr->second.BankRemSlotsTab[TabId];
+}
+
+uint32 Guild::GetMemberMoneyWithdrawRem(uint32 LowGuid)
+{
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return 0;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return WITHDRAW_MONEY_UNLIMITED;
+
+ uint32 curTime = uint32(time(NULL)/MINUTE); // minutes
+ // 24 hours
+ if (curTime > itr->second.BankResetTimeMoney + 24*HOUR/MINUTE)
+ {
+ itr->second.BankResetTimeMoney = curTime;
+ itr->second.BankRemMoney = GetBankMoneyPerDay(itr->second.RankId);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='%u',BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
+ itr->second.BankResetTimeMoney, itr->second.BankRemMoney, Id, LowGuid);
+ }
+ return itr->second.BankRemMoney;
+}
+
+void Guild::SetBankMoneyPerDay(uint32 rankId, uint32 money)
+{
+ if (rankId >= m_ranks.size())
+ return;
+
+ if (rankId == GR_GUILDMASTER)
+ money = WITHDRAW_MONEY_UNLIMITED;
+
+ m_ranks[rankId].BankMoneyPerDay = money;
+
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ if (itr->second.RankId == rankId)
+ itr->second.BankResetTimeMoney = 0;
+
+ CharacterDatabase.PExecute("UPDATE guild_rank SET BankMoneyPerDay='%u' WHERE rid='%u' AND guildid='%u'", money, (rankId+1), Id);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", Id, rankId);
+}
+
+void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 nbSlots, bool db)
+{
+ if(rankId >= m_ranks.size() ||
+ TabId >= GUILD_BANK_MAX_TABS ||
+ TabId >= purchased_tabs)
+ return;
+
+ if (rankId == GR_GUILDMASTER)
+ {
+ nbSlots = WITHDRAW_SLOT_UNLIMITED;
+ right = GUILD_BANK_RIGHT_FULL;
+ }
+
+ m_ranks[rankId].TabSlotPerDay[TabId]=nbSlots;
+ m_ranks[rankId].TabRight[TabId]=right;
+
+ if (db)
+ {
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ if (itr->second.RankId == rankId)
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ itr->second.BankResetTimeTab[i] = 0;
+
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid='%u' AND TabId='%u' AND rid='%u'", Id, uint32(TabId), rankId);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid,gbright,SlotPerDay) VALUES "
+ "('%u','%u','%u','%u','%u')", Id, uint32(TabId), rankId, m_ranks[rankId].TabRight[TabId], m_ranks[rankId].TabSlotPerDay[TabId]);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='0' WHERE guildid='%u' AND rank='%u'", uint32(TabId), Id, rankId);
+ }
+}
+
+uint32 Guild::GetBankMoneyPerDay(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return 0;
+
+ if (rankId == GR_GUILDMASTER)
+ return WITHDRAW_MONEY_UNLIMITED;
+ return m_ranks[rankId].BankMoneyPerDay;
+}
+
+uint32 Guild::GetBankSlotPerDay(uint32 rankId, uint8 TabId)
+{
+ if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return 0;
+
+ if (rankId == GR_GUILDMASTER)
+ return WITHDRAW_SLOT_UNLIMITED;
+ return m_ranks[rankId].TabSlotPerDay[TabId];
+}
+
+// *************************************************
+// Rights per day related
+
+void Guild::LoadBankRightsFromDB(uint32 GuildId)
+{
+ // 0 1 2 3
+ QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, rid, gbright, SlotPerDay FROM guild_bank_right WHERE guildid = '%u' ORDER BY TabId", GuildId);
+
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+ uint32 rankId = fields[1].GetUInt32();
+ uint16 right = fields[2].GetUInt16();
+ uint16 SlotPerDay = fields[3].GetUInt16();
+
+ SetBankRightsAndSlots(rankId, TabId, right, SlotPerDay, false);
+
+ }while( result->NextRow() );
+ delete result;
+
+ return;
+}
+
+// *************************************************
+// Bank log related
+
+void Guild::LoadGuildBankEventLogFromDB()
+{
+ // We can't add a limit as in Guild::LoadGuildEventLogFromDB since we fetch both money and bank log and know nothing about the composition
+ // 0 1 2 3 4 5 6 7
+ QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, LogEntry, TabId, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' ORDER BY TimeStamp DESC", Id);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ GuildBankEvent *NewEvent = new GuildBankEvent;
+
+ 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();
+
+ 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;
+ continue;
+ }
+ if (NewEvent->isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS
+ || m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS)
+ {
+ delete NewEvent;
+ continue;
+ }
+ if (NewEvent->isMoneyEvent())
+ m_GuildBankEventLog_Money.push_front(NewEvent);
+ else
+ m_GuildBankEventLog_Item[TabId].push_front(NewEvent);
+
+ }while( result->NextRow() );
+ delete result;
+
+ // Check lists size in case to many event entries in db for a tab or for money
+ // This cases can happen only if a crash occured somewhere and table has too many log entries
+ if (!m_GuildBankEventLog_Money.empty())
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u",
+ Id, m_GuildBankEventLog_Money.front()->LogGuid);
+ }
+ for (int 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);
+ }
+ }
+}
+
+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() );
+ }
+
+ for (int 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() );
+ }
+ }
+}
+
+void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId)
+{
+ if (TabId > GUILD_BANK_MAX_TABS)
+ return;
+
+ if (TabId == GUILD_BANK_MAX_TABS)
+ {
+ // Here we display money logs
+ WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Money.size()*(4*4+1)+1+1);
+ data << uint8(TabId); // Here GUILD_BANK_MAX_TABS
+ 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));
+ data << uint32((*itr)->ItemOrMoney);
+ data << uint32(time(NULL)-(*itr)->TimeStamp);
+ }
+ session->SendPacket(&data);
+ }
+ else
+ {
+ // here we display current tab logs
+ WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Item[TabId].size()*(4*4+1+1)+1+1);
+ data << uint8(TabId); // Here a real Tab Id
+ // number of log entries
+ 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));
+ data << uint32((*itr)->ItemOrMoney);
+ data << uint8((*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);
+ }
+ session->SendPacket(&data);
+ }
+ sLog.outDebug("WORLD: Sent (MSG_GUILD_BANK_LOG_QUERY)");
+}
+
+void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId)
+{
+ GuildBankEvent *NewEvent = new GuildBankEvent;
+
+ 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 (m_GuildBankEventLog_Money.size() > GUILD_BANK_MAX_LOGS)
+ {
+ GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Money.begin());
+ 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);
+ }
+ else
+ {
+ if (m_GuildBankEventLog_Item[TabId].size() > GUILD_BANK_MAX_LOGS)
+ {
+ GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Item[TabId].begin());
+ 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','" I64FMTD "')",
+ 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
+void Guild::RenumBankLogs()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_bank_eventlog WHERE guildid = %u", Id);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ CharacterDatabase.PExecute("UPDATE guild_bank_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
+ LogMaxGuid = fields[1].GetUInt32()+1;
+ delete result;
+}
+
+bool Guild::AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry )
+{
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u' AND TabId = '%u'AND SlotId = '%u'", GuildId, BankTab, BankTabSlot);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_item (guildid,TabId,SlotId,item_guid,item_entry) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u')", GuildId, BankTab, BankTabSlot, GUIDLow, Entry);
+ return true;
+}
+
+void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int slot )
+{
+ Item *pItem = tab->Slots[slot];
+ uint32 entry = pItem ? pItem->GetEntry() : 0;
+
+ data << uint8(slot);
+ data << uint32(entry);
+ if (entry)
+ {
+ // random item property id +8
+ data << (uint32) pItem->GetItemRandomPropertyId();
+ if (pItem->GetItemRandomPropertyId())
+ // SuffixFactor +4
+ data << (uint32) pItem->GetItemSuffixFactor();
+ // +12 // ITEM_FIELD_STACK_COUNT
+ data << uint8(pItem->GetCount());
+ data << uint32(0); // +16 // Unknown value
+ data << uint8(0); // unknown 2.4.2
+ if (uint32 Enchant0 = pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT))
+ {
+ data << uint8(1); // number of enchantments (max 3) why max 3?
+ data << uint8(PERM_ENCHANTMENT_SLOT); // enchantment slot (range: 0:2)
+ data << uint32(Enchant0); // enchantment id
+ }
+ else
+ data << uint8(0); // no enchantments (0)
+ }
+}
+
+Item* Guild::StoreItem(uint8 tabId, GuildItemPosCountVec const& dest, Item* pItem )
+{
+ if( !pItem )
+ return NULL;
+
+ Item* lastItem = pItem;
+
+ for(GuildItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
+ {
+ uint8 slot = itr->slot;
+ uint32 count = itr->count;
+
+ ++itr;
+
+ if(itr == dest.end())
+ {
+ lastItem = _StoreItem(tabId,slot,pItem,count,false);
+ break;
+ }
+
+ lastItem = _StoreItem(tabId,slot,pItem,count,true);
+ }
+
+ return lastItem;
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Guild::_StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone )
+{
+ if( !pItem )
+ return NULL;
+
+ sLog.outDebug( "GUILD STORAGE: StoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
+
+ Item* pItem2 = m_TabListMap[tab]->Slots[slot];
+
+ if( !pItem2 )
+ {
+ if(clone)
+ pItem = pItem->CloneItem(count);
+ else
+ pItem->SetCount(count);
+
+ if(!pItem)
+ return NULL;
+
+ m_TabListMap[tab]->Slots[slot] = pItem;
+
+ pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
+ pItem->SetUInt64Value(ITEM_FIELD_OWNER, 0);
+ AddGBankItemToDB(GetId(), tab, slot, pItem->GetGUIDLow(), pItem->GetEntry());
+ pItem->FSetState(ITEM_NEW);
+ pItem->SaveToDB(); // not in onventory and can be save standalone
+
+ return pItem;
+ }
+ else
+ {
+ pItem2->SetCount( pItem2->GetCount() + count );
+ pItem2->FSetState(ITEM_CHANGED);
+ pItem2->SaveToDB(); // not in onventory and can be save standalone
+
+ if(!clone)
+ {
+ pItem->RemoveFromWorld();
+ pItem->DeleteFromDB();
+ delete pItem;
+ }
+
+ return pItem2;
+ }
+}
+
+void Guild::RemoveItem(uint8 tab, uint8 slot )
+{
+ m_TabListMap[tab]->Slots[slot] = NULL;
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'",
+ GetId(), uint32(tab), uint32(slot));
+}
+
+uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32& count, bool swap, Item* pSrcItem ) const
+{
+ Item* pItem2 = m_TabListMap[tab]->Slots[slot];
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ uint32 need_space;
+
+ // empty specific slot - check item fit to slot
+ if( !pItem2 || swap )
+ {
+ // non empty stack with space
+ need_space = pSrcItem->GetMaxStackCount();
+ }
+ // non empty slot, check item type
+ else
+ {
+ // check item type
+ if(pItem2->GetEntry() != pSrcItem->GetEntry())
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ // check free space
+ if(pItem2->GetCount() >= pSrcItem->GetMaxStackCount())
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
+ }
+
+ if(need_space > count)
+ need_space = count;
+
+ dest.push_back(GuildItemPosCount(slot,need_space));
+ count -= need_space;
+ return EQUIP_ERR_OK;
+}
+
+uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const
+{
+ for(uint32 j = 0; j < GUILD_BANK_MAX_SLOTS; j++)
+ {
+ // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
+ if(j==skip_slot)
+ continue;
+
+ Item* pItem2 = m_TabListMap[tab]->Slots[j];
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pSrcItem->GetEntry() && pItem2->GetCount() < pSrcItem->GetMaxStackCount() )
+ {
+ uint32 need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+
+ dest.push_back(GuildItemPosCount(j,need_space));
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ else
+ {
+ uint32 need_space = pSrcItem->GetMaxStackCount();
+ if(need_space > count)
+ need_space = count;
+
+ dest.push_back(GuildItemPosCount(j,need_space));
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const
+{
+ sLog.outDebug( "GUILD STORAGE: CanStoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
+
+ if(count > pItem->GetCount())
+ return EQUIP_ERR_COULDNT_SPLIT_ITEMS;
+
+ if(pItem->IsSoulBound())
+ return EQUIP_ERR_CANT_DROP_SOULBOUND;
+
+ // in specific slot
+ if( slot != NULL_SLOT )
+ {
+ uint8 res = _CanStoreItem_InSpecificSlot(tab,slot,dest,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // search stack in tab for merge to
+ if( pItem->GetMaxStackCount() > 1 )
+ {
+ uint8 res = _CanStoreItem_InTab(tab,dest,count,true,pItem,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // search free slot in bag for place to
+ uint8 res = _CanStoreItem_InTab(tab,dest,count,false,pItem,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ return EQUIP_ERR_BANK_FULL;
+}
+
+void Guild::SetGuildBankTabText(uint8 TabId, std::string text)
+{
+ if (TabId >= GUILD_BANK_MAX_TABS)
+ return;
+ if (TabId >= m_TabListMap.size())
+ return;
+ if (!m_TabListMap[TabId])
+ return;
+
+ if(m_TabListMap[TabId]->Text==text)
+ return;
+
+ utf8truncate(text,500); // DB and client size limitation
+
+ m_TabListMap[TabId]->Text = text;
+
+ CharacterDatabase.escape_string(text);
+ CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), Id, uint32(TabId));
+}
+
+void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId)
+{
+ if (TabId > GUILD_BANK_MAX_TABS)
+ return;
+
+ GuildBankTab const *tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1);
+ data << uint8(TabId);
+ data << tab->Text;
+ session->SendPacket(&data);
+}
diff --git a/src/game/Guild.h b/src/game/Guild.h
new file mode 100644
index 00000000000..3f16d80987d
--- /dev/null
+++ b/src/game/Guild.h
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_GUILD_H
+#define MANGOSSERVER_GUILD_H
+
+#define WITHDRAW_MONEY_UNLIMITED 0xFFFFFFFF
+#define WITHDRAW_SLOT_UNLIMITED 0xFFFFFFFF
+
+#include "Item.h"
+
+class Item;
+
+enum GuildDefaultRanks
+{
+ GR_GUILDMASTER = 0,
+ GR_OFFICER = 1,
+ //GR_VETERAN = 2, -- not used anywhere and possible incorrect in modified rank list
+ //GR_MEMBER = 3,
+ //GR_INITIATE = 4, -- use Guild::GetLowestRank() instead for lowest rank
+};
+
+enum GuildRankRights
+{
+ GR_RIGHT_EMPTY = 0x00000040,
+ GR_RIGHT_GCHATLISTEN = 0x00000041,
+ GR_RIGHT_GCHATSPEAK = 0x00000042,
+ GR_RIGHT_OFFCHATLISTEN = 0x00000044,
+ GR_RIGHT_OFFCHATSPEAK = 0x00000048,
+ GR_RIGHT_PROMOTE = 0x000000C0,
+ GR_RIGHT_DEMOTE = 0x00000140,
+ GR_RIGHT_INVITE = 0x00000050,
+ GR_RIGHT_REMOVE = 0x00000060,
+ GR_RIGHT_SETMOTD = 0x00001040,
+ GR_RIGHT_EPNOTE = 0x00002040,
+ GR_RIGHT_VIEWOFFNOTE = 0x00004040,
+ GR_RIGHT_EOFFNOTE = 0x00008040,
+ GR_RIGHT_MODIFY_GUILD_INFO = 0x00010040,
+ GR_RIGHT_REPAIR_FROM_GUILD = 0x00020000, // unused in 2.4.x?, Remove money withdraw capacity
+ GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
+ GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
+ GR_RIGHT_ALL = 0x000FF1FF
+};
+
+enum Typecommand
+{
+ GUILD_CREATE_S = 0x00,
+ GUILD_INVITE_S = 0x01,
+ GUILD_QUIT_S = 0x03,
+ GUILD_FOUNDER_S = 0x0E,
+ GUILD_UNK1 = 0x10,
+ GUILD_BANK_S = 0x15,
+ GUILD_UNK3 = 0x16
+};
+
+enum CommandErrors
+{
+ GUILD_PLAYER_NO_MORE_IN_GUILD = 0x00,
+ GUILD_INTERNAL = 0x01,
+ GUILD_ALREADY_IN_GUILD = 0x02,
+ ALREADY_IN_GUILD = 0x03,
+ INVITED_TO_GUILD = 0x04,
+ ALREADY_INVITED_TO_GUILD = 0x05,
+ GUILD_NAME_INVALID = 0x06,
+ GUILD_NAME_EXISTS = 0x07,
+ GUILD_LEADER_LEAVE = 0x08,
+ GUILD_PERMISSIONS = 0x08,
+ GUILD_PLAYER_NOT_IN_GUILD = 0x09,
+ GUILD_PLAYER_NOT_IN_GUILD_S = 0x0A,
+ GUILD_PLAYER_NOT_FOUND = 0x0B,
+ GUILD_NOT_ALLIED = 0x0C,
+ GUILD_RANK_TOO_HIGH_S = 0x0D,
+ GUILD_ALREADY_LOWEST_RANK_S = 0x0E,
+ GUILD_TEMP_ERROR = 0x11,
+ GUILD_RANK_IN_USE = 0x12,
+ GUILD_IGNORE = 0x13,
+ GUILD_ERR_UNK1 = 0x17,
+ GUILD_WITHDRAW_TOO_MUCH = 0x18,
+ GUILD_BANK_NO_MONEY = 0x19,
+ GUILD_BANK_TAB_IS_FULL = 0x1B,
+ GUILD_BANK_ITEM_NOT_FOUND = 0x1C
+};
+
+enum GuildEvents
+{
+ GE_PROMOTION = 0x00,
+ GE_DEMOTION = 0x01,
+ GE_MOTD = 0x02,
+ GE_JOINED = 0x03,
+ GE_LEFT = 0x04,
+ GE_REMOVED = 0x05,
+ GE_LEADER_IS = 0x06,
+ GE_LEADER_CHANGED = 0x07,
+ GE_DISBANDED = 0x08,
+ GE_TABARDCHANGE = 0x09,
+ GE_UNK1 = 0x0A, // string, string
+ GE_UNK2 = 0x0B,
+ GE_SIGNED_ON = 0x0C,
+ GE_SIGNED_OFF = 0x0D,
+ GE_UNK3 = 0x0E,
+ GE_BANKTAB_PURCHASED= 0x0F,
+ GE_UNK5 = 0x10,
+ GE_UNK6 = 0x11, // string 0000000000002710 is 1 gold
+ GE_UNK7 = 0x12
+};
+
+enum PetitionTurns
+{
+ PETITION_TURN_OK = 0,
+ PETITION_TURN_ALREADY_IN_GUILD = 2,
+ PETITION_TURN_NEED_MORE_SIGNATURES = 4,
+};
+
+enum PetitionSigns
+{
+ PETITION_SIGN_OK = 0,
+ PETITION_SIGN_ALREADY_SIGNED = 1,
+ PETITION_SIGN_ALREADY_IN_GUILD = 2,
+ PETITION_SIGN_CANT_SIGN_OWN = 3,
+ PETITION_SIGN_NOT_SERVER = 4,
+};
+
+enum GuildBankRights
+{
+ GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
+ GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
+ GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
+
+ GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
+ GUILD_BANK_RIGHT_FULL = 0xFF,
+};
+
+enum GuildBankLogEntries
+{
+ GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
+ GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
+ GUILD_BANK_LOG_MOVE_ITEM = 3,
+ GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
+ GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
+ GUILD_BANK_LOG_REPAIR_MONEY = 6,
+ GUILD_BANK_LOG_MOVE_ITEM2 = 7,
+};
+
+enum GuildEventLogEntryTypes
+{
+ GUILD_EVENT_LOG_INVITE_PLAYER = 1,
+ GUILD_EVENT_LOG_JOIN_GUILD = 2,
+ GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
+ GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
+ GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
+ GUILD_EVENT_LOG_LEAVE_GUILD = 6,
+};
+
+struct GuildEventlogEntry
+{
+ uint32 LogGuid;
+ uint8 EventType;
+ uint32 PlayerGuid1;
+ uint32 PlayerGuid2;
+ uint8 NewRank;
+ uint64 TimeStamp;
+};
+
+enum GuildEmblem
+{
+ ERR_GUILDEMBLEM_SUCCESS = 0,
+ ERR_GUILDEMBLEM_INVALID_TABARD_COLORS = 1,
+ ERR_GUILDEMBLEM_NOGUILD = 2,
+ ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
+ ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
+ ERR_GUILDEMBLEM_INVALIDVENDOR = 5
+};
+
+struct GuildBankEvent
+{
+ uint32 LogGuid;
+ uint8 LogEntry;
+ uint8 TabId;
+ uint32 PlayerGuid;
+ uint32 ItemOrMoney;
+ uint8 ItemStackCount;
+ uint8 DestTabId;
+ uint64 TimeStamp;
+
+ const bool isMoneyEvent()
+ {
+ return LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY ||
+ LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY ||
+ LogEntry == GUILD_BANK_LOG_REPAIR_MONEY;
+ }
+};
+
+struct GuildBankTab
+{
+ Item* Slots[GUILD_BANK_MAX_SLOTS];
+ std::string Name;
+ std::string Icon;
+ std::string Text;
+};
+
+struct GuildItemPosCount
+{
+ GuildItemPosCount(uint8 _slot, uint8 _count) : slot(_slot), count(_count) {}
+
+ uint8 slot;
+ uint8 count;
+};
+typedef std::vector<GuildItemPosCount> GuildItemPosCountVec;
+
+struct MemberSlot
+{
+ uint64 logout_time;
+ std::string name;
+ std::string Pnote;
+ std::string OFFnote;
+ uint32 RankId;
+ uint32 zoneId;
+ uint8 level;
+ uint8 Class;
+ uint32 BankResetTimeMoney;
+ uint32 BankRemMoney;
+ uint32 BankResetTimeTab[GUILD_BANK_MAX_TABS];
+ uint32 BankRemSlotsTab[GUILD_BANK_MAX_TABS];
+};
+
+struct RankInfo
+{
+ RankInfo(std::string _name, uint32 _rights, uint32 _money) : name(_name), rights(_rights), BankMoneyPerDay(_money)
+ {
+ for(uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ TabRight[i] = 0;
+ TabSlotPerDay[i] = 0;
+ }
+ }
+
+ std::string name;
+ uint32 rights;
+ uint32 BankMoneyPerDay;
+ uint32 TabRight[GUILD_BANK_MAX_TABS];
+ uint32 TabSlotPerDay[GUILD_BANK_MAX_TABS];
+};
+
+class Guild
+{
+ public:
+ Guild();
+ ~Guild();
+
+ bool create(uint64 lGuid, std::string gname);
+ void Disband();
+
+ typedef std::map<uint32, MemberSlot> MemberList;
+ typedef std::vector<RankInfo> RankList;
+
+ uint32 GetId(){ return Id; }
+ const uint64& GetLeader(){ return leaderGuid; }
+ std::string GetName(){ return name; }
+ std::string GetMOTD(){ return MOTD; }
+ std::string GetGINFO(){ return GINFO; }
+
+ uint32 GetCreatedYear(){ return CreatedYear; }
+ uint32 GetCreatedMonth(){ return CreatedMonth; }
+ uint32 GetCreatedDay(){ return CreatedDay; }
+
+ uint32 GetEmblemStyle(){ return EmblemStyle; }
+ uint32 GetEmblemColor(){ return EmblemColor; }
+ uint32 GetBorderStyle(){ return BorderStyle; }
+ uint32 GetBorderColor(){ return BorderColor; }
+ uint32 GetBackgroundColor(){ return BackgroundColor; }
+
+ void SetLeader(uint64 guid);
+ bool AddMember(uint64 plGuid, uint32 plRank);
+ void ChangeRank(uint64 guid, uint32 newRank);
+ void DelMember(uint64 guid, bool isDisbanding=false);
+ uint32 GetLowestRank() const { return GetNrRanks()-1; }
+
+ void SetMOTD(std::string motd);
+ void SetGINFO(std::string ginfo);
+ void SetPNOTE(uint64 guid,std::string pnote);
+ void SetOFFNOTE(uint64 guid,std::string offnote);
+ void SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor);
+
+ uint32 GetMemberSize() const { return members.size(); }
+
+ bool LoadGuildFromDB(uint32 GuildId);
+ bool LoadRanksFromDB(uint32 GuildId);
+ bool LoadMembersFromDB(uint32 GuildId);
+
+ bool FillPlayerData(uint64 guid, MemberSlot* memslot);
+ void LoadPlayerStatsByGuid(uint64 guid);
+
+ void BroadcastToGuild(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
+ void BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
+ void BroadcastPacketToRank(WorldPacket *packet, uint32 rankId);
+ void BroadcastPacket(WorldPacket *packet);
+
+ void CreateRank(std::string name,uint32 rights);
+ void DelRank();
+ std::string GetRankName(uint32 rankId);
+ uint32 GetRankRights(uint32 rankId);
+ uint32 GetNrRanks() const { return m_ranks.size(); }
+
+ void SetRankName(uint32 rankId, std::string name);
+ void SetRankRights(uint32 rankId, uint32 rights);
+ bool HasRankRight(uint32 rankId, uint32 right)
+ {
+ return ((GetRankRights(rankId) & right) != GR_RIGHT_EMPTY) ? true : false;
+ }
+
+ void Roster(WorldSession *session);
+ void Query(WorldSession *session);
+
+ void UpdateLogoutTime(uint64 guid);
+ // Guild eventlog
+ void LoadGuildEventLogFromDB();
+ void UnloadGuildEventlog();
+ void DisplayGuildEventlog(WorldSession *session);
+ void LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank);
+ void RenumGuildEventlog();
+
+ // ** Guild bank **
+ // Content & item deposit/withdraw
+ void DisplayGuildBankContent(WorldSession *session, uint8 TabId);
+ void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1);
+ void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots);
+ void DisplayGuildBankMoneyUpdate();
+
+ Item* GetItem(uint8 TabId, uint8 SlotId);
+ uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const;
+ Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem );
+ void RemoveItem(uint8 tab, uint8 slot );
+
+ // Tabs
+ void DisplayGuildBankTabsInfo(WorldSession *session);
+ void CreateNewBankTab();
+ void SetGuildBankTabText(uint8 TabId, std::string text);
+ void SendGuildBankTabText(WorldSession *session, uint8 TabId);
+ void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon);
+ void CreateBankRightForTab(uint32 rankid, uint8 TabId);
+ const GuildBankTab *GetBankTab(uint8 index) { if(index >= m_TabListMap.size()) return NULL; return m_TabListMap[index]; }
+ const uint8 GetPurchasedTabs() const { return purchased_tabs; }
+ uint32 GetBankRights(uint32 rankId, uint8 TabId) const;
+ bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const;
+ bool CanMemberViewTab(uint32 LowGuid, uint8 TabId) const;
+ // Load/unload
+ void LoadGuildBankFromDB();
+ void UnloadGuildBank();
+ void IncOnlineMemberCount() { ++m_onlinemembers; }
+ // Money deposit/withdraw
+ void SendMoneyInfo(WorldSession *session, uint32 LowGuid);
+ bool MemberMoneyWithdraw(uint32 amount, uint32 LowGuid);
+ uint64 GetGuildBankMoney() { return guildbank_money; }
+ void SetBankMoney(int64 money);
+ // per days
+ bool MemberItemWithdraw(uint8 TabId, uint32 LowGuid);
+ uint32 GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId);
+ uint32 GetMemberMoneyWithdrawRem(uint32 LowGuid);
+ void SetBankMoneyPerDay(uint32 rankId, uint32 money);
+ void SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 SlotPerDay, bool db);
+ uint32 GetBankMoneyPerDay(uint32 rankId);
+ uint32 GetBankSlotPerDay(uint32 rankId, uint8 TabId);
+ // rights per day
+ void LoadBankRightsFromDB(uint32 GuildId);
+ // logs
+ void LoadGuildBankEventLogFromDB();
+ void UnloadGuildBankEventLog();
+ void DisplayGuildBankLogs(WorldSession *session, uint8 TabId);
+ void LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0);
+ void RenumBankLogs();
+ bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry );
+
+ protected:
+ void AddRank(std::string name,uint32 rights,uint32 money);
+
+ uint32 Id;
+ std::string name;
+ uint64 leaderGuid;
+ std::string MOTD;
+ std::string GINFO;
+ uint32 CreatedYear;
+ uint32 CreatedMonth;
+ uint32 CreatedDay;
+
+ uint32 EmblemStyle;
+ uint32 EmblemColor;
+ uint32 BorderStyle;
+ uint32 BorderColor;
+ uint32 BackgroundColor;
+
+ RankList m_ranks;
+
+ MemberList members;
+
+ typedef std::vector<GuildBankTab*> TabListMap;
+ 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;
+ GuildEventlog m_GuildEventlog;
+ GuildBankEventLog m_GuildBankEventLog_Money;
+ GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS];
+
+ bool m_bankloaded;
+ bool m_eventlogloaded;
+ uint32 m_onlinemembers;
+ uint64 guildbank_money;
+ uint8 purchased_tabs;
+
+ uint32 LogMaxGuid;
+ uint32 GuildEventlogMaxGuid;
+ private:
+ // internal common parts for CanStore/StoreItem functions
+ void AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int32 slot );
+ uint8 _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const;
+ uint8 _CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item *pSrcItem, uint8 skip_slot ) const;
+ Item* _StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone );
+};
+#endif
diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp
new file mode 100644
index 00000000000..a82a45fde40
--- /dev/null
+++ b/src/game/GuildHandler.cpp
@@ -0,0 +1,1815 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Guild.h"
+#include "MapManager.h"
+#include "GossipDef.h"
+#include "SocialMgr.h"
+
+void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 4);
+
+ uint32 guildId;
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_QUERY");
+
+ recvPacket >> guildId;
+
+ guild = objmgr.GetGuildById(guildId);
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ guild->Query(this);
+}
+
+void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string gname;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_CREATE");
+
+ recvPacket >> gname;
+
+ if(GetPlayer()->GetGuildId())
+ return;
+
+ Guild *guild = new Guild;
+ if(!guild->create(GetPlayer()->GetGUID(),gname))
+ {
+ delete guild;
+ return;
+ }
+
+ objmgr.AddGuild(guild);
+}
+
+void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string Invitedname, plname;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_INVITE");
+
+ Player * player = NULL;
+
+ recvPacket >> Invitedname;
+
+ if(normalizePlayerName(Invitedname))
+ player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str());
+
+ if(!player)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, Invitedname, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ // OK result but not send invite
+ if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ return;
+
+ // not let enemies sign guild charter
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, Invitedname, GUILD_NOT_ALLIED);
+ return;
+ }
+
+ if(player->GetGuildId())
+ {
+ plname = player->GetName();
+ SendGuildCommandResult(GUILD_INVITE_S, plname, ALREADY_IN_GUILD);
+ return;
+ }
+
+ if(player->GetGuildIdInvited())
+ {
+ plname = player->GetName();
+ SendGuildCommandResult(GUILD_INVITE_S, plname, ALREADY_INVITED_TO_GUILD);
+ return;
+ }
+
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_INVITE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ sLog.outDebug("Player %s Invited %s to Join his Guild", GetPlayer()->GetName(), Invitedname.c_str());
+
+ player->SetGuildIdInvited(GetPlayer()->GetGuildId());
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_INVITE_PLAYER, GetPlayer()->GetGUIDLow(), player->GetGUIDLow(), 0);
+
+ WorldPacket data(SMSG_GUILD_INVITE, (8+10)); // guess size
+ data << GetPlayer()->GetName();
+ data << guild->GetName();
+ player->GetSession()->SendPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_INVITE)");
+}
+
+void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string plName;
+ uint64 plGuid;
+ uint32 plGuildId;
+ Guild *guild;
+ Player *player;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_REMOVE");
+
+ recvPacket >> plName;
+
+ if(!normalizePlayerName(plName))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str());
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+
+ if(player)
+ {
+ plGuid = player->GetGUID();
+ plGuildId = player->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName);
+ plGuildId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ if(!plGuid)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_REMOVE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ if(plGuid == guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_QUIT_S, "", GUILD_LEADER_LEAVE);
+ return;
+ }
+
+ if(GetPlayer()->GetGuildId() != plGuildId)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+
+ guild->DelMember(plGuid);
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), 0);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+20)); // guess size
+ data << (uint8)GE_REMOVED;
+ data << (uint8)2; // strings count
+ data << plName;
+ data << GetPlayer()->GetName();
+ guild->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/)
+{
+ Guild *guild;
+ Player *player = GetPlayer();
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_ACCEPT");
+
+ guild = objmgr.GetGuildById(player->GetGuildIdInvited());
+ if(!guild || player->GetGuildId())
+ return;
+
+ // not let enemies sign guild charter
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != objmgr.GetPlayerTeamByGUID(guild->GetLeader()))
+ return;
+
+ if(!guild->AddMember(GetPlayer()->GetGUID(),guild->GetLowestRank()))
+ return;
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_JOIN_GUILD, GetPlayer()->GetGUIDLow(), 0, 0);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size
+ data << (uint8)GE_JOINED;
+ data << (uint8)1;
+ data << player->GetName();
+ guild->BroadcastPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)");
+}
+
+void WorldSession::HandleGuildDeclineOpcode(WorldPacket& /*recvPacket*/)
+{
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_DECLINE");
+
+ GetPlayer()->SetGuildIdInvited(0);
+ GetPlayer()->SetInGuild(0);
+}
+
+void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/)
+{
+ Guild *guild;
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_INFO");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ WorldPacket data(SMSG_GUILD_INFO, (5*4 + guild->GetName().size() + 1));
+ data << guild->GetName();
+ data << guild->GetCreatedDay();
+ data << guild->GetCreatedMonth();
+ data << guild->GetCreatedYear();
+ data << guild->GetMemberSize();
+ data << guild->GetMemberSize();
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleGuildRosterOpcode(WorldPacket& /*recvPacket*/)
+{
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_ROSTER");
+
+ Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ return;
+
+ guild->Roster(this);
+}
+
+void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string plName;
+ uint64 plGuid;
+ uint32 plGuildId;
+ uint32 plRankId;
+ Player *player;
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_PROMOTE");
+
+ recvPacket >> plName;
+
+ if(!normalizePlayerName(plName))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str());
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(player)
+ {
+ plGuid = player->GetGUID();
+ plGuildId = player->GetGuildId();
+ plRankId = player->GetRank();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName);
+ plGuildId = Player::GetGuildIdFromDB(plGuid);
+ plRankId = Player::GetRankFromDB(plGuid);
+ }
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ else if(!plGuid)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+ else if(plGuid == GetPlayer()->GetGUID())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID);
+ return;
+ }
+ else if(GetPlayer()->GetGuildId() != plGuildId)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+ else if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_PROMOTE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+ else if((plRankId-1) == 0 || (plRankId-1) < this->GetPlayer()->GetRank())
+ return;
+
+ if(plRankId < 1)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_INTERNAL);
+ return;
+ }
+
+ uint32 newRankId = plRankId < guild->GetNrRanks() ? plRankId-1 : guild->GetNrRanks()-1;
+
+ guild->ChangeRank(plGuid, newRankId);
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_PROMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), newRankId);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size
+ data << (uint8)GE_PROMOTION;
+ data << (uint8)3;
+ data << GetPlayer()->GetName();
+ data << plName;
+ data << guild->GetRankName(newRankId);
+ guild->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string plName;
+ uint64 plGuid;
+ uint32 plGuildId;
+ uint32 plRankId;
+ Player *player;
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_DEMOTE");
+
+ recvPacket >> plName;
+
+ if(!normalizePlayerName(plName))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str());
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(player)
+ {
+ plGuid = player->GetGUID();
+ plGuildId = player->GetGuildId();
+ plRankId = player->GetRank();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName);
+ plGuildId = Player::GetGuildIdFromDB(plGuid);
+ plRankId = Player::GetRankFromDB(plGuid);
+ }
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ if( !plGuid )
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+
+ if(plGuid == GetPlayer()->GetGUID())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID);
+ return;
+ }
+
+ if(GetPlayer()->GetGuildId() != plGuildId)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_DEMOTE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ if((plRankId+1) >= guild->GetNrRanks() || plRankId <= this->GetPlayer()->GetRank())
+ return;
+
+ guild->ChangeRank(plGuid, (plRankId+1));
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), (plRankId+1));
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size
+ data << (uint8)GE_DEMOTION;
+ data << (uint8)3;
+ data << GetPlayer()->GetName();
+ data << plName;
+ data << guild->GetRankName(plRankId+1);
+ guild->BroadcastPacket(&data);
+}
+
+void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/)
+{
+ std::string plName;
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_LEAVE");
+
+ guild = objmgr.GetGuildById(_player->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ if(_player->GetGUID() == guild->GetLeader() && guild->GetMemberSize() > 1)
+ {
+ SendGuildCommandResult(GUILD_QUIT_S, "", GUILD_LEADER_LEAVE);
+ return;
+ }
+
+ if(_player->GetGUID() == guild->GetLeader())
+ {
+ guild->Disband();
+ return;
+ }
+
+ plName = _player->GetName();
+
+ guild->DelMember(_player->GetGUID());
+ // Put record into guildlog
+ guild->LogGuildEvent(GUILD_EVENT_LOG_LEAVE_GUILD, _player->GetGUIDLow(), 0, 0);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size
+ data << (uint8)GE_LEFT;
+ data << (uint8)1;
+ data << plName;
+ guild->BroadcastPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)");
+
+ SendGuildCommandResult(GUILD_QUIT_S, guild->GetName(), GUILD_PLAYER_NO_MORE_IN_GUILD);
+}
+
+void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/)
+{
+ std::string name;
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_DISBAND");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ if(GetPlayer()->GetGUID() != guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ guild->Disband();
+
+ //sLog.outDebug("WORLD: Guild Sucefully Disbanded");
+}
+
+void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ std::string name;
+ Player *newLeader;
+ uint64 newLeaderGUID;
+ uint32 newLeaderGuild;
+ Player *oldLeader = GetPlayer();
+ Guild *guild;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_LEADER");
+
+ recvPacket >> name;
+
+ if(!normalizePlayerName(name))
+ return;
+
+ newLeader = ObjectAccessor::Instance().FindPlayerByName(name.c_str());
+ if(newLeader)
+ {
+ newLeaderGUID = newLeader->GetGUID();
+ newLeaderGuild = newLeader->GetGuildId();
+ }
+ else
+ {
+ newLeaderGUID = objmgr.GetPlayerGUIDByName(name);
+ newLeaderGuild = Player::GetGuildIdFromDB(newLeaderGUID);
+ }
+ guild = objmgr.GetGuildById(oldLeader->GetGuildId());
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ else if(!newLeaderGUID)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+ if(oldLeader->GetGuildId() != newLeaderGuild)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+ if(oldLeader->GetGUID() != guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ guild->SetLeader(newLeaderGUID);
+ guild->ChangeRank(oldLeader->GetGUID(), GR_OFFICER);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+20)); // guess size
+ data << (uint8)GE_LEADER_CHANGED;
+ data << (uint8)2;
+ data << oldLeader->GetName();
+ data << name.c_str();
+ guild->BroadcastPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)");
+}
+
+void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket)
+{
+ Guild *guild;
+ std::string MOTD;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_MOTD");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_SETMOTD))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ if(!recvPacket.empty())
+ recvPacket >> MOTD;
+ else
+ MOTD = "";
+
+ guild->SetMOTD(MOTD);
+
+ WorldPacket data(SMSG_GUILD_EVENT, (2+MOTD.size()+1));
+ data << (uint8)GE_MOTD;
+ data << (uint8)1;
+ data << MOTD;
+ guild->BroadcastPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)");
+}
+
+void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ Guild *guild;
+ Player *player;
+ uint64 plGuid;
+ uint32 plGuildId;
+ std::string name,PNOTE;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_SET_PUBLIC_NOTE");
+
+ recvPacket >> name;
+
+ if(!normalizePlayerName(name))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(name.c_str());
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(player)
+ {
+ plGuid = player->GetGUID();
+ plGuildId = player->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(name);
+ plGuildId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ else if(!plGuid)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+ else if(GetPlayer()->GetGuildId() != plGuildId)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_EPNOTE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ recvPacket >> PNOTE;
+ guild->SetPNOTE(plGuid, PNOTE);
+
+ guild->Roster(this);
+}
+
+void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ Guild *guild;
+ Player *player;
+ uint64 plGuid;
+ uint32 plGuildId;
+ std::string plName, OFFNOTE;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_SET_OFFICER_NOTE");
+
+ recvPacket >> plName;
+
+ if(!normalizePlayerName(plName))
+ return;
+
+ player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str());
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(player)
+ {
+ plGuid = player->GetGUID();
+ plGuildId = player->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName);
+ plGuildId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+ else if( !plGuid )
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND);
+ return;
+ }
+ else if(GetPlayer()->GetGuildId() != plGuildId)
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
+ return;
+ }
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_EOFFNOTE))
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ recvPacket >> OFFNOTE;
+ guild->SetOFFNOTE(plGuid, OFFNOTE);
+
+ guild->Roster(this);
+}
+
+void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 4+4+1+4*13);
+ //recvPacket.hexlike();
+
+ Guild *guild;
+ std::string rankname;
+ uint32 rankId;
+ uint32 rights, MoneyPerDay;
+ uint32 BankRights;
+ uint32 BankSlotPerDay;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_RANK");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ else if(GetPlayer()->GetGUID() != guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ recvPacket >> rankId;
+ recvPacket >> rights;
+ recvPacket >> rankname;
+ recvPacket >> MoneyPerDay;
+
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ recvPacket >> BankRights;
+ recvPacket >> BankSlotPerDay;
+ guild->SetBankRightsAndSlots(rankId, uint8(i), uint16(BankRights & 0xFF), uint16(BankSlotPerDay), true);
+ }
+ sLog.outDebug("WORLD: Changed RankName to %s , Rights to 0x%.4X", rankname.c_str(), rights);
+
+ guild->SetBankMoneyPerDay(rankId, MoneyPerDay);
+ guild->SetRankName(rankId, rankname);
+
+ if(rankId==GR_GUILDMASTER) // prevent loss leader rights
+ rights |= GR_RIGHT_ALL;
+
+ guild->SetRankRights(rankId, rights);
+
+ guild->Query(this);
+ guild->Roster(this);
+}
+
+void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ Guild *guild;
+ std::string rankname;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_ADD_RANK");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ if(GetPlayer()->GetGUID() != guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ if(guild->GetNrRanks() >= GUILD_MAX_RANKS) // client not let create more 10 than ranks
+ return;
+
+ recvPacket >> rankname;
+
+ guild->CreateRank(rankname, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+
+ guild->Query(this);
+ guild->Roster(this);
+}
+
+void WorldSession::HandleGuildDelRankOpcode(WorldPacket& /*recvPacket*/)
+{
+ Guild *guild;
+ std::string rankname;
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_DEL_RANK");
+
+ guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ else if(GetPlayer()->GetGUID() != guild->GetLeader())
+ {
+ SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ guild->DelRank();
+
+ guild->Query(this);
+ guild->Roster(this);
+}
+
+void WorldSession::SendGuildCommandResult(uint32 typecmd,std::string str,uint32 cmdresult)
+{
+ WorldPacket data(SMSG_GUILD_COMMAND_RESULT, (8+str.size()+1));
+ data << typecmd;
+ data << str;
+ data << cmdresult;
+ SendPacket(&data);
+
+ //sLog.outDebug("WORLD: Sent (SMSG_GUILD_COMMAND_RESULT)");
+}
+
+void WorldSession::HandleGuildChangeInfoOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ //sLog.outDebug("WORLD: Received CMSG_GUILD_INFO_TEXT");
+
+ std::string GINFO;
+
+ recvPacket >> GINFO;
+
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+ return;
+ }
+
+ if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_MODIFY_GUILD_INFO))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PERMISSIONS);
+ return;
+ }
+
+ guild->SetGINFO(GINFO);
+}
+
+void WorldSession::HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 8+4+4+4+4+4);
+
+ //sLog.outDebug("WORLD: Received MSG_SAVE_GUILD_EMBLEM");
+
+ uint64 vendorGuid;
+
+ uint32 EmblemStyle;
+ uint32 EmblemColor;
+ uint32 BorderStyle;
+ uint32 BorderColor;
+ uint32 BackgroundColor;
+
+ recvPacket >> vendorGuid;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorGuid,UNIT_NPC_FLAG_TABARDDESIGNER);
+ if (!pCreature)
+ {
+ //"That's not an emblem vendor!"
+ SendSaveGuildEmblem(ERR_GUILDEMBLEM_INVALIDVENDOR);
+ sLog.outDebug("WORLD: HandleGuildSaveEmblemOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(vendorGuid));
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ recvPacket >> EmblemStyle;
+ recvPacket >> EmblemColor;
+ recvPacket >> BorderStyle;
+ recvPacket >> BorderColor;
+ recvPacket >> BackgroundColor;
+
+ Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
+ if(!guild)
+ {
+ //"You are not part of a guild!";
+ SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOGUILD);
+ return;
+ }
+
+ if (guild->GetLeader() != GetPlayer()->GetGUID())
+ {
+ //"Only guild leaders can create emblems."
+ SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOTGUILDMASTER);
+ return;
+ }
+
+ if(GetPlayer()->GetMoney() < 10*GOLD)
+ {
+ //"You can't afford to do that."
+ SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOTENOUGHMONEY);
+ return;
+ }
+
+ GetPlayer()->ModifyMoney(-10*GOLD);
+ guild->SetEmblem(EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor);
+
+ //"Guild Emblem saved."
+ SendSaveGuildEmblem(ERR_GUILDEMBLEM_SUCCESS);
+
+ guild->Query(this);
+}
+
+void WorldSession::HandleGuildEventLogOpcode(WorldPacket& /* recvPacket */)
+{
+ // empty
+ sLog.outDebug("WORLD: Received (MSG_GUILD_EVENT_LOG_QUERY)");
+ //recvPacket.hexlike();
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ pGuild->DisplayGuildEventlog(this);
+}
+
+/****** GUILD BANK *******/
+
+void WorldSession::HandleGuildBankGetMoneyAmount( WorldPacket & /* recv_data */ )
+{
+ sLog.outDebug("WORLD: Received (MSG_GUILD_BANK_MONEY_WITHDRAWN)");
+ //recv_data.hexlike();
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow());
+}
+
+void WorldSession::HandleGuildBankGetRights( WorldPacket& /* recv_data */ )
+{
+ sLog.outDebug("WORLD: Received (MSG_GUILD_PERMISSIONS)");
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ uint32 rankId = GetPlayer()->GetRank();
+
+ WorldPacket data(MSG_GUILD_PERMISSIONS, 4*15+1);
+ data << uint32(rankId); // guild rank id
+ data << uint32(pGuild->GetRankRights(rankId)); // rank rights
+ // money per day left
+ data << uint32(pGuild->GetMemberMoneyWithdrawRem(GetPlayer()->GetGUIDLow()));
+ data << uint8(pGuild->GetPurchasedTabs()); // tabs count
+ for(int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ data << uint32(pGuild->GetBankRights(rankId, uint8(i)));
+ data << uint32(pGuild->GetMemberSlotWithdrawRem(GetPlayer()->GetGUIDLow(), uint8(i)));
+ }
+ SendPacket(&data);
+ sLog.outDebug("WORLD: Sent (MSG_GUILD_PERMISSIONS)");
+}
+
+/* Called when clicking on Guild bank gameobject */
+void WorldSession::HandleGuildBankQuery( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANKER_ACTIVATE)");
+ CHECK_PACKET_SIZE(recv_data,8+1);
+ uint64 GoGuid;
+ uint8 unk;
+ recv_data >> GoGuid >> unk;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ if (uint32 GuildId = GetPlayer()->GetGuildId())
+ {
+ if(Guild *pGuild = objmgr.GetGuildById(GuildId))
+ {
+ pGuild->DisplayGuildBankTabsInfo(this);
+ return;
+ }
+ }
+
+ SendGuildCommandResult(GUILD_BANK_S, "", GUILD_PLAYER_NOT_IN_GUILD);
+}
+
+/* Called when opening guild bank tab only (first one) */
+void WorldSession::HandleGuildBankTabColon( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_QUERY_TAB)");
+ CHECK_PACKET_SIZE(recv_data,8+1+1);
+ uint64 GoGuid;
+ uint8 TabId,unk1;
+ recv_data >> GoGuid >> TabId >> unk1;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ // Let's update the amount of gold the player can withdraw before displaying the content
+ // This is usefull if money withdraw right has changed
+ pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow());
+
+ pGuild->DisplayGuildBankContent(this, TabId);
+}
+
+void WorldSession::HandleGuildBankDeposit( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_DEPOSIT_MONEY)");
+ CHECK_PACKET_SIZE(recv_data,8+4);
+ uint64 GoGuid;
+ uint32 money;
+ recv_data >> GoGuid >> money;
+
+ if (!money)
+ return;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ if (GetPlayer()->GetMoney() < money)
+ return;
+
+ CharacterDatabase.BeginTransaction();
+
+ pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money);
+ GetPlayer()->ModifyMoney(-int(money));
+ GetPlayer()->SaveGoldToDB();
+
+ CharacterDatabase.CommitTransaction();
+
+ // logging money
+ if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId);
+ }
+
+ // log
+ pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money);
+
+ pGuild->DisplayGuildBankTabsInfo(this);
+ pGuild->DisplayGuildBankContent(this, 0);
+ pGuild->DisplayGuildBankMoneyUpdate();
+}
+
+void WorldSession::HandleGuildBankWithdraw( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_WITHDRAW_MONEY)");
+ CHECK_PACKET_SIZE(recv_data,8+4);
+ uint64 GoGuid;
+ uint32 money;
+ recv_data >> GoGuid >> money;
+
+ if (!money)
+ return;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ if (pGuild->GetGuildBankMoney()<money) // not enough money in bank
+ return;
+
+ if (!pGuild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_WITHDRAW_GOLD))
+ return;
+
+ CharacterDatabase.BeginTransaction();
+
+ if (!pGuild->MemberMoneyWithdraw(money, GetPlayer()->GetGUIDLow()))
+ {
+ CharacterDatabase.RollbackTransaction();
+ return;
+ }
+
+ GetPlayer()->ModifyMoney(money);
+ GetPlayer()->SaveGoldToDB();
+
+ CharacterDatabase.CommitTransaction();
+
+ // Log
+ pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money);
+
+ pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow());
+ pGuild->DisplayGuildBankTabsInfo(this);
+ pGuild->DisplayGuildBankContent(this, 0);
+ pGuild->DisplayGuildBankMoneyUpdate();
+}
+
+void WorldSession::HandleGuildBankDepositItem( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_SWAP_ITEMS)");
+ //recv_data.hexlike();
+
+ uint64 GoGuid;
+ uint8 BankToBank;
+
+ uint8 BankTab, BankTabSlot, AutoStore, AutoStoreCount, PlayerSlot, PlayerBag, SplitedAmount = 0;
+ uint8 BankTabDst, BankTabSlotDst, unk2, ToChar = 1;
+ uint32 ItemEntry, unk1;
+ bool BankToChar = false;
+
+ CHECK_PACKET_SIZE(recv_data,8+1);
+ recv_data >> GoGuid >> BankToBank;
+ if (BankToBank)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1+4+1+1+4+1+1);
+ recv_data >> BankTabDst;
+ recv_data >> BankTabSlotDst;
+ recv_data >> unk1; // always 0
+ recv_data >> BankTab;
+ recv_data >> BankTabSlot;
+ recv_data >> ItemEntry;
+ recv_data >> unk2; // always 0
+ recv_data >> SplitedAmount;
+
+ if (BankTabSlotDst >= GUILD_BANK_MAX_SLOTS)
+ return;
+ if (BankTabDst == BankTab && BankTabSlotDst == BankTabSlot)
+ return;
+ }
+ else
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1+4+1);
+ recv_data >> BankTab;
+ recv_data >> BankTabSlot;
+ recv_data >> ItemEntry;
+ recv_data >> AutoStore;
+ if (AutoStore)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1);
+ recv_data >> AutoStoreCount;
+ }
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1);
+ recv_data >> PlayerBag;
+ recv_data >> PlayerSlot;
+ if (!AutoStore)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1);
+ recv_data >> ToChar;
+ recv_data >> SplitedAmount;
+ }
+
+ if (BankTabSlot >= GUILD_BANK_MAX_SLOTS && BankTabSlot != 0xFF)
+ return;
+ }
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ Player *pl = GetPlayer();
+
+ // Bank <-> Bank
+ if (BankToBank)
+ {
+ // empty operation
+ if(BankTab==BankTabDst && BankTabSlot==BankTabSlotDst)
+ return;
+
+ Item *pItemSrc = pGuild->GetItem(BankTab, BankTabSlot);
+ if (!pItemSrc) // may prevent crash
+ return;
+
+ if(SplitedAmount > pItemSrc->GetCount())
+ return; // cheating?
+ else if(SplitedAmount == pItemSrc->GetCount())
+ SplitedAmount = 0; // no split
+
+ Item *pItemDst = pGuild->GetItem(BankTabDst, BankTabSlotDst);
+
+ if(BankTab!=BankTabDst)
+ {
+ // check dest pos rights (if different tabs)
+ if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTabDst, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
+ return;
+
+ // check source pos rights (if different tabs)
+ uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
+ if(remRight <= 0)
+ return;
+ }
+
+ if (SplitedAmount)
+ { // Bank -> Bank item split (in empty or non empty slot
+ GuildItemPosCountVec dest;
+ uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,dest,SplitedAmount,pItemSrc,false);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemSrc, NULL );
+ return;
+ }
+
+ Item *pNewItem = pItemSrc->CloneItem( SplitedAmount );
+ if( !pNewItem )
+ {
+ pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemSrc, NULL );
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), SplitedAmount, BankTabDst);
+
+ pl->ItemRemovedQuestCheck( pItemSrc->GetEntry(), SplitedAmount );
+ pItemSrc->SetCount( pItemSrc->GetCount() - SplitedAmount );
+ pItemSrc->FSetState(ITEM_CHANGED);
+ pItemSrc->SaveToDB(); // not in inventory and can be save standalone
+ pGuild->StoreItem(BankTabDst,dest,pNewItem);
+ CharacterDatabase.CommitTransaction();
+ }
+ else // non split
+ {
+ GuildItemPosCountVec gDest;
+ uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,false);
+ if( msg == EQUIP_ERR_OK ) // merge to
+ {
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst);
+
+ pGuild->RemoveItem(BankTab, BankTabSlot);
+ pGuild->StoreItem(BankTabDst, gDest, pItemSrc);
+ CharacterDatabase.CommitTransaction();
+ }
+ else // swap
+ {
+ gDest.clear();
+ uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemSrc, NULL );
+ return;
+ }
+
+ GuildItemPosCountVec gSrc;
+ msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gSrc,pItemDst->GetCount(),pItemDst,true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemDst, NULL );
+ return;
+ }
+
+ if(BankTab!=BankTabDst)
+ {
+ // check source pos rights (item swapped to src)
+ if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
+ return;
+
+ // check dest pos rights (item swapped to src)
+ uint32 remRightDst = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTabDst);
+ if(remRightDst <= 0)
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst);
+ pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTabDst, pl->GetGUIDLow(), pItemDst->GetEntry(), pItemDst->GetCount(), BankTab);
+
+ pGuild->RemoveItem(BankTab, BankTabSlot);
+ pGuild->RemoveItem(BankTabDst, BankTabSlotDst);
+ pGuild->StoreItem(BankTab, gSrc, pItemDst);
+ pGuild->StoreItem(BankTabDst, gDest, pItemSrc);
+ CharacterDatabase.CommitTransaction();
+ }
+ }
+ pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot,BankTab==BankTabDst ? BankTabSlotDst : -1);
+ if(BankTab!=BankTabDst)
+ pGuild->DisplayGuildBankContentUpdate(BankTabDst,BankTabSlotDst);
+ return;
+ }
+
+ // Player <-> Bank
+
+ // char->bank autostore click return BankTabSlot = 255 = NULL_SLOT
+ // do similar for bank->char
+ if(AutoStore && ToChar)
+ {
+ PlayerBag = NULL_BAG;
+ PlayerSlot = NULL_SLOT;
+ }
+
+ // allow work with inventory only
+ if(!Player::IsInventoryPos(PlayerBag,PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) )
+ {
+ _player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL );
+ return;
+ }
+
+ Item *pItemBank = pGuild->GetItem(BankTab, BankTabSlot);
+ Item *pItemChar = GetPlayer()->GetItemByPos(PlayerBag, PlayerSlot);
+ if (!pItemChar && !pItemBank) // Nothing to do
+ return;
+
+ if (!pItemChar && !ToChar) // Problem to get item from player
+ return;
+
+ if (!pItemBank && ToChar) // Problem to get bank item
+ return;
+
+ // BankToChar swap or char to bank remaining
+
+ if (ToChar) // Bank -> Char cases
+ {
+ if(SplitedAmount > pItemBank->GetCount())
+ return; // cheating?
+ else if(SplitedAmount == pItemBank->GetCount())
+ SplitedAmount = 0; // no split
+
+ if (SplitedAmount)
+ { // Bank -> Char split to slot (patly move)
+ Item *pNewItem = pItemBank->CloneItem( SplitedAmount );
+ if( !pNewItem )
+ {
+ pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemBank, NULL );
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pNewItem, NULL );
+ delete pNewItem;
+ return;
+ }
+
+ // check source pos rights (item moved to inventory)
+ uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
+ if(remRight <= 0)
+ {
+ delete pNewItem;
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), SplitedAmount);
+
+ pItemBank->SetCount(pItemBank->GetCount()-SplitedAmount);
+ pItemBank->FSetState(ITEM_CHANGED);
+ pItemBank->SaveToDB(); // not in inventory and can be save standalone
+ pl->MoveItemToInventory(dest,pNewItem,true);
+ pl->SaveInventoryAndGoldToDB();
+
+ pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+ }
+ else // Bank -> Char swap with slot (move)
+ {
+ ItemPosCountVec dest;
+ uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false);
+ if( msg == EQUIP_ERR_OK ) // merge case
+ {
+ // check source pos rights (item moved to inventory)
+ uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
+ if(remRight <= 0)
+ return;
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
+
+ pGuild->RemoveItem(BankTab, BankTabSlot);
+ pl->MoveItemToInventory(dest,pItemBank,true);
+ pl->SaveInventoryAndGoldToDB();
+
+ pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+ }
+ else // Bank <-> Char swap items
+ {
+ // check source pos rights (item swapped to bank)
+ if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
+ return;
+
+ if(pItemChar)
+ {
+ if(!pItemChar->CanBeTraded())
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL );
+ return;
+ }
+ }
+
+ ItemPosCountVec iDest;
+ msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemBank, NULL );
+ return;
+ }
+
+ GuildItemPosCountVec gDest;
+ if(pItemChar)
+ {
+ msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemChar, NULL );
+ return;
+ }
+ }
+
+ // check source pos rights (item moved to inventory)
+ uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
+ if(remRight <= 0)
+ return;
+
+ if(pItemChar)
+ {
+ // logging item move to bank
+ if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(),
+ GuildId);
+ }
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
+ if(pItemChar)
+ pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
+ pGuild->RemoveItem(BankTab, BankTabSlot);
+ if(pItemChar)
+ {
+ pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
+ pItemChar->DeleteFromInventoryDB();
+ }
+
+ if(pItemChar)
+ pGuild->StoreItem(BankTab, gDest, pItemChar);
+ pl->MoveItemToInventory(iDest,pItemBank,true);
+ pl->SaveInventoryAndGoldToDB();
+
+ pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+ }
+ }
+ pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot);
+ return;
+ } // End "To char" part
+
+ // Char -> Bank cases
+
+ if(!pItemChar->CanBeTraded())
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL );
+ return;
+ }
+
+ // check source pos rights (item moved to bank)
+ if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
+ return;
+
+ if(SplitedAmount > pItemChar->GetCount())
+ return; // cheating?
+ else if(SplitedAmount == pItemChar->GetCount())
+ SplitedAmount = 0; // no split
+
+ if (SplitedAmount)
+ { // Char -> Bank split to empty or non-empty slot (partly move)
+ GuildItemPosCountVec dest;
+ uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,SplitedAmount,pItemChar,false);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemChar, NULL );
+ return;
+ }
+
+ Item *pNewItem = pItemChar->CloneItem( SplitedAmount );
+ if( !pNewItem )
+ {
+ pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemChar, NULL );
+ return;
+ }
+
+ // logging item move to bank (before items merge
+ if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ pItemChar->GetProto()->Name1,pItemChar->GetEntry(),SplitedAmount,GuildId);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), SplitedAmount);
+
+ pl->ItemRemovedQuestCheck( pItemChar->GetEntry(), SplitedAmount );
+ pItemChar->SetCount(pItemChar->GetCount()-SplitedAmount);
+ pItemChar->SetState(ITEM_CHANGED);
+ pl->SaveInventoryAndGoldToDB();
+ pGuild->StoreItem(BankTab, dest, pNewItem);
+ CharacterDatabase.CommitTransaction();
+
+ pGuild->DisplayGuildBankContentUpdate(BankTab,dest);
+ }
+ else // Char -> Bank swap with empty or non-empty (move)
+ {
+ GuildItemPosCountVec dest;
+ uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,pItemChar->GetCount(),pItemChar,false);
+ if( msg == EQUIP_ERR_OK ) // merge
+ {
+ // logging item move to bank
+ if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(),
+ GuildId);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
+ pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
+ pItemChar->DeleteFromInventoryDB();
+
+ pGuild->StoreItem(BankTab,dest,pItemChar);
+ pl->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+
+ pGuild->DisplayGuildBankContentUpdate(BankTab,dest);
+ }
+ else // Char <-> Bank swap items (posible NULL bank item)
+ {
+ ItemPosCountVec iDest;
+ if(pItemBank)
+ {
+ msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemBank, NULL );
+ return;
+ }
+ }
+
+ GuildItemPosCountVec gDest;
+ msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pl->SendEquipError( msg, pItemChar, NULL );
+ return;
+ }
+
+ if(pItemBank)
+ {
+ // check bank pos rights (item swapped with inventory)
+ uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
+ if(remRight <= 0)
+ return;
+ }
+
+ // logging item move to bank
+ if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(),
+ GuildId);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if(pItemBank)
+ pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
+ pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
+ pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
+ pItemChar->DeleteFromInventoryDB();
+ if(pItemBank)
+ pGuild->RemoveItem(BankTab, BankTabSlot);
+
+ pGuild->StoreItem(BankTab,gDest,pItemChar);
+ if(pItemBank)
+ pl->MoveItemToInventory(iDest,pItemBank,true);
+ pl->SaveInventoryAndGoldToDB();
+ if(pItemBank)
+ pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+
+ pGuild->DisplayGuildBankContentUpdate(BankTab,gDest);
+ }
+ }
+}
+
+void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_BUY_TAB)");
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+ //recv_data.hexlike();
+ uint64 GoGuid;
+ uint8 TabId;
+
+ recv_data >> GoGuid;
+ recv_data >> TabId;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId==0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ uint32 TabCost = objmgr.GetGuildBankTabPrice(TabId) * GOLD;
+ if (!TabCost)
+ return;
+
+ if (pGuild->GetPurchasedTabs() >= GUILD_BANK_MAX_TABS)
+ return;
+
+ if (TabId != pGuild->GetPurchasedTabs()) // purchased_tabs = 0 when buying Tab 0, that is why this check can be made
+ {
+ sLog.outError("Error: trying to buy a tab non contigous to owned ones");
+ return;
+ }
+
+ if (GetPlayer()->GetMoney() < TabCost) // Should not happen, this is checked by client
+ return;
+
+ // Go on with creating tab
+ pGuild->CreateNewBankTab();
+ GetPlayer()->ModifyMoney(-int(TabCost));
+ pGuild->SetBankMoneyPerDay(GetPlayer()->GetRank(), WITHDRAW_MONEY_UNLIMITED);
+ pGuild->SetBankRightsAndSlots(GetPlayer()->GetRank(), TabId, GUILD_BANK_RIGHT_FULL, WITHDRAW_SLOT_UNLIMITED, true);
+ pGuild->Roster(this);
+ pGuild->DisplayGuildBankTabsInfo(this);
+}
+
+void WorldSession::HandleGuildBankModifyTab( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_UPDATE_TAB)");
+ //recv_data.hexlike();
+ CHECK_PACKET_SIZE(recv_data, 8+1+1+1);
+ uint64 GoGuid;
+ uint8 TabId;
+ std::string Name;
+ std::string IconIndex;
+
+ recv_data >> GoGuid;
+ recv_data >> TabId;
+ recv_data >> Name;
+ recv_data >> IconIndex;
+
+ if(Name.empty())
+ return;
+
+ if(IconIndex.empty())
+ return;
+
+ if (!objmgr.IsGuildVaultGameObject(_player, GoGuid))
+ return;
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId==0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ pGuild->SetGuildBankTabInfo(TabId, Name, IconIndex);
+ pGuild->DisplayGuildBankTabsInfo(this);
+ pGuild->DisplayGuildBankContent(this, TabId);
+}
+
+void WorldSession::HandleGuildBankLog( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: Received (MSG_GUILD_BANK_LOG_QUERY)");
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ uint8 TabId;
+ recv_data >> TabId;
+
+ pGuild->DisplayGuildBankLogs(this, TabId);
+}
+
+void WorldSession::HandleGuildBankTabText(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Received MSG_QUERY_GUILD_BANK_TEXT");
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ uint8 TabId;
+ recv_data >> TabId;
+
+ pGuild->SendGuildBankTabText(this, TabId);
+}
+
+void WorldSession::HandleGuildBankSetTabText(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_SET_GUILD_BANK_TEXT");
+ CHECK_PACKET_SIZE(recv_data, 1+1);
+
+ uint32 GuildId = GetPlayer()->GetGuildId();
+ if (GuildId == 0)
+ return;
+
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if(!pGuild)
+ return;
+
+ uint8 TabId;
+ std::string Text;
+ recv_data >> TabId;
+ recv_data >> Text;
+
+ pGuild->SetGuildBankTabText(TabId, Text);
+}
+
+void WorldSession::SendSaveGuildEmblem( uint32 msg )
+{
+ WorldPacket data(MSG_SAVE_GUILD_EMBLEM, 4);
+ data << uint32(msg); // not part of guild
+ SendPacket( &data );
+}
diff --git a/src/game/HateMatrix.h b/src/game/HateMatrix.h
new file mode 100644
index 00000000000..fc0cbcee625
--- /dev/null
+++ b/src/game/HateMatrix.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_HATEMATRIX_H
+#define MANGOS_HATEMATRIX_H
+
+#include "Utilities/HashMap.h"
+#include <cassert>
+
+class Unit;
+
+struct MANGOS_DLL_DECL HateMatrix
+{
+ typedef hash_map<Unit *, uint32> HateMatrixMapType;
+
+ inline uint32 operator[](Unit *unit) const
+ {
+ HateMatrixMapType::const_iterator iter = i_hateValues.find(unit);
+ return (iter == i_hateValues.end() ? 0 : iter->second);
+ }
+
+ inline uint32& operator[](Unit *unit)
+ {
+ HateMatrixMapType::iterator iter = i_hateValues.find(unit);
+ if( iter == i_hateValues.end() )
+ {
+ std::pair<HateMatrixMapType::iterator, bool> p = i_hateValues.insert( HateMatrixMapType::value_type(unit, 0) );
+ assert(p.second);
+ iter = p.first;
+ }
+
+ return iter->second;
+ }
+
+ inline void ClearMatrix(void) { i_hateValues.clear(); }
+
+ inline void RemoveValue(Unit *unit)
+ {
+ HateMatrixMapType::iterator iter = i_hateValues.find(unit);
+ if( iter != i_hateValues.end() )
+ i_hateValues.erase( iter );
+ }
+
+ inline void AddValue(Unit *unit, uint32 val)
+ {
+ (*this)[unit] += val;
+ }
+
+ private:
+ HateMatrixMapType i_hateValues;
+};
+
+struct HateBinder
+{
+ static uint32 si_noHateValue;
+ uint32 &i_hateValue;
+ Unit *i_unit;
+ HateBinder(uint32 &val, Unit *u) : i_hateValue(val), i_unit(u) {}
+ HateBinder() : i_hateValue(si_noHateValue), i_unit(NULL) {}
+ HateBinder(const HateBinder &obj) : i_hateValue(obj.i_hateValue), i_unit(obj.i_unit) {}
+
+ HateBinder& operator=(const HateBinder &obj)
+ {
+ i_hateValue = obj.i_hateValue;
+ i_unit = obj.i_unit;
+ return *this;
+ }
+};
+#endif
diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp
new file mode 100644
index 00000000000..281909b68de
--- /dev/null
+++ b/src/game/HomeMovementGenerator.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "HomeMovementGenerator.h"
+#include "Creature.h"
+#include "Traveller.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "DestinationHolderImp.h"
+#include "ObjectMgr.h"
+#include "WorldPacket.h"
+
+void
+HomeMovementGenerator<Creature>::Initialize(Creature & owner)
+{
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setTargetLocation(owner);
+}
+
+void
+HomeMovementGenerator<Creature>::Reset(Creature &)
+{
+}
+
+void
+HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+{
+ if( !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) )
+ return;
+
+ float x, y, z;
+ owner.GetRespawnCoord(x, y, z);
+
+ CreatureTraveller traveller(owner);
+
+ uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z);
+ modifyTravelTime(travel_time);
+ owner.clearUnitState(UNIT_STAT_ALL_STATE);
+}
+
+bool
+HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff)
+{
+ CreatureTraveller traveller( owner);
+ i_destinationHolder.UpdateTraveller(traveller, time_diff, false);
+
+ if (time_diff > i_travel_timer)
+ {
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ // restore orientation of not moving creature at returning to home
+ if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE)
+ {
+ if(CreatureData const* data = objmgr.GetCreatureData(owner.GetDBTableGUIDLow()))
+ {
+ owner.SetOrientation(data->orientation);
+ WorldPacket packet;
+ owner.BuildHeartBeatMsg(&packet);
+ owner.SendMessageToSet(&packet, false);
+ }
+ }
+ return false;
+ }
+
+ i_travel_timer -= time_diff;
+
+ return true;
+}
diff --git a/src/game/HomeMovementGenerator.h b/src/game/HomeMovementGenerator.h
new file mode 100644
index 00000000000..aa7dc27a1b1
--- /dev/null
+++ b/src/game/HomeMovementGenerator.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_HOMEMOVEMENTGENERATOR_H
+#define MANGOS_HOMEMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+class Creature;
+
+template < class T >
+class MANGOS_DLL_SPEC HomeMovementGenerator;
+
+template <>
+class MANGOS_DLL_SPEC HomeMovementGenerator<Creature>
+: public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> >
+{
+ public:
+
+ HomeMovementGenerator() {}
+ ~HomeMovementGenerator() {}
+
+ void Initialize(Creature &);
+ void Finalize(Creature &) {}
+ void Reset(Creature &);
+ bool Update(Creature &, const uint32 &);
+ void modifyTravelTime(uint32 travel_time) { i_travel_timer = travel_time; }
+ MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
+
+ bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; }
+ private:
+ void _setTargetLocation(Creature &);
+ DestinationHolder< Traveller<Creature> > i_destinationHolder;
+
+ uint32 i_travel_timer;
+};
+#endif
diff --git a/src/game/HostilRefManager.cpp b/src/game/HostilRefManager.cpp
new file mode 100644
index 00000000000..5ce8ec4d556
--- /dev/null
+++ b/src/game/HostilRefManager.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "HostilRefManager.h"
+#include "ThreatManager.h"
+#include "Unit.h"
+#include "Database/DBCStructure.h"
+#include "SpellMgr.h"
+
+HostilRefManager::~HostilRefManager()
+{
+ deleteReferences();
+}
+
+//=================================================
+// send threat to all my hateres for the pVictim
+// The pVictim is hated than by them as well
+// use for buffs and healing threat functionality
+
+void HostilRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry const *pThreatSpell, bool pSingleTarget)
+{
+ HostilReference* ref;
+
+ uint32 size = pSingleTarget ? 1 : getSize(); // if pSingleTarget do not devide threat
+ ref = getFirst();
+ while(ref != NULL)
+ {
+ float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell);
+ if(pVictim == getOwner())
+ ref->addThreat(float (threat) / size); // It is faster to modify the threat durectly if possible
+ else
+ ref->getSource()->addThreat(pVictim, float (threat) / size);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+
+void HostilRefManager::addThreatPercent(int32 pValue)
+{
+ HostilReference* ref;
+
+ ref = getFirst();
+ while(ref != NULL)
+ {
+ ref->addThreatPercent(pValue);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The online / offline status is given to the method. The calculation has to be done before
+
+void HostilRefManager::setOnlineOfflineState(bool pIsOnline)
+{
+ HostilReference* ref;
+
+ ref = getFirst();
+ while(ref != NULL)
+ {
+ ref->setOnlineOfflineState(pIsOnline);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The online / offline status is calculated and set
+
+void HostilRefManager::updateThreatTables()
+{
+ HostilReference* ref = getFirst();
+ while(ref)
+ {
+ ref->updateOnlineStatus();
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The references are not needed anymore
+// tell the source to remove them from the list and free the mem
+
+void HostilRefManager::deleteReferences()
+{
+ HostilReference* ref = getFirst();
+ while(ref)
+ {
+ HostilReference* nextRef = ref->next();
+ ref->removeReference();
+ delete ref;
+ ref = nextRef;
+ }
+}
+
+//=================================================
+// delete one reference, defined by Unit
+
+void HostilRefManager::deleteReference(Unit *pCreature)
+{
+ HostilReference* ref = getFirst();
+ while(ref)
+ {
+ HostilReference* nextRef = ref->next();
+ if(ref->getSource()->getOwner() == pCreature)
+ {
+ ref->removeReference();
+ delete ref;
+ break;
+ }
+ ref = nextRef;
+ }
+}
+
+//=================================================
+// set state for one reference, defined by Unit
+
+void HostilRefManager::setOnlineOfflineState(Unit *pCreature,bool pIsOnline)
+{
+ HostilReference* ref = getFirst();
+ while(ref)
+ {
+ HostilReference* nextRef = ref->next();
+ if(ref->getSource()->getOwner() == pCreature)
+ {
+ ref->setOnlineOfflineState(pIsOnline);
+ break;
+ }
+ ref = nextRef;
+ }
+}
+
+//=================================================
diff --git a/src/game/HostilRefManager.h b/src/game/HostilRefManager.h
new file mode 100644
index 00000000000..b4e4707e9fe
--- /dev/null
+++ b/src/game/HostilRefManager.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _HOSTILEREFMANAGER
+#define _HOSTILEREFMANAGER
+
+#include "Common.h"
+#include "Utilities/LinkedReference/RefManager.h"
+
+class Unit;
+class ThreatManager;
+class HostilReference;
+struct SpellEntry;
+
+//=================================================
+
+class HostilRefManager : public RefManager<Unit, ThreatManager>
+{
+ private:
+ Unit *iOwner;
+ public:
+ explicit HostilRefManager(Unit *pOwner) { iOwner = pOwner; }
+ ~HostilRefManager();
+
+ Unit* getOwner() { return iOwner; }
+
+ // send threat to all my hateres for the pVictim
+ // The pVictim is hated than by them as well
+ // use for buffs and healing threat functionality
+ void threatAssist(Unit *pVictim, float threat, SpellEntry const *threatSpell = 0, bool pSingleTarget=false);
+
+ void addThreatPercent(int32 pValue);
+
+ // The references are not needed anymore
+ // tell the source to remove them from the list and free the mem
+ void deleteReferences();
+
+ HostilReference* getFirst() { return ((HostilReference* ) RefManager<Unit, ThreatManager>::getFirst()); }
+
+ void updateThreatTables();
+
+ void setOnlineOfflineState(bool pIsOnline);
+
+ // set state for one reference, defined by Unit
+ void setOnlineOfflineState(Unit *pCreature,bool pIsOnline);
+
+ // delete one reference, defined by Unit
+ void deleteReference(Unit *pCreature);
+};
+//=================================================
+#endif
diff --git a/src/game/IdleMovementGenerator.cpp b/src/game/IdleMovementGenerator.cpp
new file mode 100644
index 00000000000..3932bd63f1f
--- /dev/null
+++ b/src/game/IdleMovementGenerator.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "IdleMovementGenerator.h"
+#include "Creature.h"
+
+IdleMovementGenerator si_idleMovement;
+
+void
+IdleMovementGenerator::Reset(Unit& /*owner*/)
+{
+}
+
+void
+DistractMovementGenerator::Initialize(Unit& owner)
+{
+ owner.addUnitState(UNIT_STAT_DISTRACTED);
+}
+
+void
+DistractMovementGenerator::Finalize(Unit& owner)
+{
+ owner.clearUnitState(UNIT_STAT_DISTRACTED);
+}
+
+bool
+DistractMovementGenerator::Update(Unit& owner, const uint32& time_diff)
+{
+ if(time_diff > m_timer)
+ return false;
+
+ m_timer -= time_diff;
+ return true;
+}
diff --git a/src/game/IdleMovementGenerator.h b/src/game/IdleMovementGenerator.h
new file mode 100644
index 00000000000..cd1c8a20ac7
--- /dev/null
+++ b/src/game/IdleMovementGenerator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_IDLEMOVEMENTGENERATOR_H
+#define MANGOS_IDLEMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+
+class MANGOS_DLL_SPEC IdleMovementGenerator : public MovementGenerator
+{
+ public:
+
+ void Initialize(Unit &) { }
+ void Finalize(Unit &) { }
+ void Reset(Unit &);
+ bool Update(Unit &, const uint32 &) { return true; }
+ MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
+};
+
+extern IdleMovementGenerator si_idleMovement;
+
+class MANGOS_DLL_SPEC DistractMovementGenerator : public MovementGenerator
+{
+ public:
+ explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
+
+ void Initialize(Unit& owner);
+ void Finalize(Unit& owner);
+ void Reset(Unit& owner) { Initialize(owner); }
+ bool Update(Unit& owner, const uint32& time_diff);
+ MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
+
+ private:
+ uint32 m_timer;
+};
+
+#endif
diff --git a/src/game/InstanceData.cpp b/src/game/InstanceData.cpp
new file mode 100644
index 00000000000..78c7bea2584
--- /dev/null
+++ b/src/game/InstanceData.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "InstanceData.h"
+#include "Database/DatabaseEnv.h"
+#include "Map.h"
+
+void InstanceData::SaveToDB()
+{
+ if(!Save()) return;
+ std::string data = Save();
+ CharacterDatabase.escape_string(data);
+ CharacterDatabase.PExecute("UPDATE instance SET data = '%s' WHERE id = '%d'", data.c_str(), instance->GetInstanceId());
+}
diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h
new file mode 100644
index 00000000000..518fa8661fd
--- /dev/null
+++ b/src/game/InstanceData.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_INSTANCE_DATA_H
+#define MANGOS_INSTANCE_DATA_H
+
+#include "Common.h"
+
+class Map;
+class Unit;
+class Player;
+class GameObject;
+class Creature;
+
+class MANGOS_DLL_SPEC InstanceData
+{
+ public:
+
+ explicit InstanceData(Map *map) : instance(map) {}
+ virtual ~InstanceData() {}
+
+ Map *instance;
+
+ //On creation, NOT load.
+ virtual void Initialize() {}
+
+ //On load
+ virtual void Load(const char* /*data*/) {}
+
+ //When save is needed, this function generates the data
+ virtual const char* Save() { return ""; }
+
+ void SaveToDB();
+
+ //Called every map update
+ virtual void Update(uint32 /*diff*/) {}
+
+ //Used by the map's CanEnter function.
+ //This is to prevent players from entering during boss encounters.
+ virtual bool IsEncounterInProgress() const { return false; };
+
+ //Called when a player successfully enters the instance.
+ virtual void OnPlayerEnter(Player *) {}
+
+ //Called when a gameobject is created
+ virtual void OnObjectCreate(GameObject *) {}
+
+ //called on creature creation
+ virtual void OnCreatureCreate(Creature * /*creature*/, uint32 /*creature_entry*/) {}
+};
+#endif
diff --git a/src/game/InstanceSaveMgr.cpp b/src/game/InstanceSaveMgr.cpp
new file mode 100644
index 00000000000..aedf7015b3a
--- /dev/null
+++ b/src/game/InstanceSaveMgr.cpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2005,2006,2007 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "InstanceSaveMgr.h"
+#include "Common.h"
+#include "Database/SQLStorage.h"
+
+#include "Player.h"
+#include "GridNotifiers.h"
+#include "WorldSession.h"
+#include "Log.h"
+#include "GridStates.h"
+#include "CellImpl.h"
+#include "Map.h"
+#include "MapManager.h"
+#include "MapInstanced.h"
+#include "InstanceSaveMgr.h"
+#include "Timer.h"
+#include "GridNotifiersImpl.h"
+#include "Config/ConfigEnv.h"
+#include "Transports.h"
+#include "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include "Group.h"
+#include "InstanceData.h"
+#include "ProgressBar.h"
+
+INSTANTIATE_SINGLETON_1( InstanceSaveManager );
+
+InstanceSaveManager::InstanceSaveManager() : lock_instLists(false)
+{
+}
+
+InstanceSaveManager::~InstanceSaveManager()
+{
+ // it is undefined whether this or objectmgr will be unloaded first
+ // so we must be prepared for both cases
+ lock_instLists = true;
+ for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
+ {
+ InstanceSave *save = itr->second;
+ for(InstanceSave::PlayerListType::iterator itr = save->m_playerList.begin(), next = itr; itr != save->m_playerList.end(); itr = next)
+ {
+ ++next;
+ (*itr)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
+ }
+ save->m_playerList.clear();
+ for(InstanceSave::GroupListType::iterator itr = save->m_groupList.begin(), next = itr; itr != save->m_groupList.end(); itr = next)
+ {
+ ++next;
+ (*itr)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
+ }
+ save->m_groupList.clear();
+ delete save;
+ }
+}
+
+/*
+- adding instance into manager
+- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
+*/
+InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, uint8 difficulty, time_t resetTime, bool canReset, bool load)
+{
+ InstanceSave *save = GetInstanceSave(instanceId);
+ if(save) return save;
+
+ const MapEntry* entry = sMapStore.LookupEntry(mapId);
+ if(!entry || instanceId == 0)
+ {
+ sLog.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d!", mapId, instanceId);
+ return NULL;
+ }
+
+ if(!resetTime)
+ {
+ // initialize reset time
+ // for normal instances if no creatures are killed the instance will reset in two hours
+ if(entry->map_type == MAP_RAID || difficulty == DIFFICULTY_HEROIC)
+ resetTime = GetResetTimeFor(mapId);
+ else
+ {
+ resetTime = time(NULL) + 2 * HOUR;
+ // normally this will be removed soon after in InstanceMap::Add, prevent error
+ ScheduleReset(true, resetTime, InstResetEvent(0, mapId, instanceId));
+ }
+ }
+
+ sLog.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId, instanceId);
+
+ save = new InstanceSave(mapId, instanceId, difficulty, resetTime, canReset);
+ if(!load) save->SaveToDB();
+
+ m_instanceSaveById[instanceId] = save;
+ return save;
+}
+
+InstanceSave *InstanceSaveManager::GetInstanceSave(uint32 InstanceId)
+{
+ InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId);
+ return itr != m_instanceSaveById.end() ? itr->second : NULL;
+}
+
+void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid)
+{
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid);
+ CharacterDatabase.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid);
+ CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid);
+ CharacterDatabase.CommitTransaction();
+ // respawn times should be deleted only when the map gets unloaded
+}
+
+void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId)
+{
+ InstanceSaveHashMap::iterator itr = m_instanceSaveById.find( InstanceId );
+ if(itr != m_instanceSaveById.end())
+ {
+ // save the resettime for normal instances only when they get unloaded
+ if(time_t resettime = itr->second->GetResetTimeForDB())
+ CharacterDatabase.PExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", (uint64)resettime, InstanceId);
+ delete itr->second;
+ m_instanceSaveById.erase(itr);
+ }
+}
+
+InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty,
+ time_t resetTime, bool canReset)
+: m_mapid(MapId), m_instanceid(InstanceId), m_resetTime(resetTime),
+ m_difficulty(difficulty), m_canReset(canReset)
+{
+}
+
+InstanceSave::~InstanceSave()
+{
+ // the players and groups must be unbound before deleting the save
+ assert(m_playerList.empty() && m_groupList.empty());
+}
+
+/*
+ Called from AddInstanceSave
+*/
+void InstanceSave::SaveToDB()
+{
+ // save instance data too
+ std::string data;
+
+ Map *map = MapManager::Instance().FindMap(m_instanceid, GetMapId());
+ if(map)
+ {
+ assert(map->IsDungeon());
+ InstanceData *iData = ((InstanceMap *)map)->GetInstanceData();
+ if(iData && iData->Save())
+ {
+ data = iData->Save();
+ CharacterDatabase.escape_string(data);
+ }
+ }
+
+ CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"I64FMTD"', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str());
+}
+
+time_t InstanceSave::GetResetTimeForDB()
+{
+ // only save the reset time for normal instances
+ const MapEntry *entry = sMapStore.LookupEntry(GetMapId());
+ if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DIFFICULTY_HEROIC)
+ return 0;
+ else
+ return GetResetTime();
+}
+
+// to cache or not to cache, that is the question
+InstanceTemplate const* InstanceSave::GetTemplate()
+{
+ return objmgr.GetInstanceTemplate(m_mapid);
+}
+
+MapEntry const* InstanceSave::GetMapEntry()
+{
+ return sMapStore.LookupEntry(m_mapid);
+}
+
+void InstanceSave::DeleteFromDB()
+{
+ InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId());
+}
+
+/* true if the instance save is still valid */
+bool InstanceSave::UnloadIfEmpty()
+{
+ if(m_playerList.empty() && m_groupList.empty())
+ {
+ if(!sInstanceSaveManager.lock_instLists)
+ sInstanceSaveManager.RemoveInstanceSave(GetInstanceId());
+ return false;
+ }
+ else
+ return true;
+}
+
+void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...)
+{
+ Tokens fieldTokens = StrSplit(fields, ", ");
+ assert(fieldTokens.size() != 0);
+
+ va_list ap;
+ char szQueryTail [MAX_QUERY_LEN];
+ va_start(ap, queryTail);
+ int res = vsnprintf( szQueryTail, MAX_QUERY_LEN, queryTail, ap );
+ va_end(ap);
+
+ QueryResult *result = db.PQuery("SELECT %s FROM %s %s", fields, table, szQueryTail);
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ std::ostringstream ss;
+ for(size_t i = 0; i < fieldTokens.size(); i++)
+ {
+ std::string fieldValue = fields[i].GetCppString();
+ db.escape_string(fieldValue);
+ ss << (i != 0 ? " AND " : "") << fieldTokens[i] << " = '" << fieldValue << "'";
+ }
+ db.DirectPExecute("DELETE FROM %s WHERE %s", table, ss.str().c_str());
+ } while (result->NextRow());
+ delete result;
+ }
+}
+
+void InstanceSaveManager::CleanupInstances()
+{
+ uint64 now = (uint64)time(NULL);
+
+ barGoLink bar(2);
+ bar.step();
+
+ // load reset times and clean expired instances
+ sInstanceSaveManager.LoadResetTimes();
+
+ // clean character/group - instance binds with invalid group/characters
+ _DelHelper(CharacterDatabase, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL");
+ _DelHelper(CharacterDatabase, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN characters ON group_instance.leaderGuid = characters.guid LEFT JOIN groups ON group_instance.leaderGuid = groups.leaderGuid WHERE characters.guid IS NULL OR groups.leaderGuid IS NULL");
+
+ // clean instances that do not have any players or groups bound to them
+ _DelHelper(CharacterDatabase, "id, map, difficulty", "instance", "LEFT JOIN character_instance ON character_instance.instance = id LEFT JOIN group_instance ON group_instance.instance = id WHERE character_instance.instance IS NULL AND group_instance.instance IS NULL");
+
+ // clean invalid instance references in other tables
+ _DelHelper(CharacterDatabase, "character_instance.guid, instance", "character_instance", "LEFT JOIN instance ON character_instance.instance = instance.id WHERE instance.id IS NULL");
+ _DelHelper(CharacterDatabase, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN instance ON group_instance.instance = instance.id WHERE instance.id IS NULL");
+
+ // creature_respawn and gameobject_respawn are in another database
+ // first, obtain total instance set
+ std::set< uint32 > InstanceSet;
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id FROM instance");
+ if( result )
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ InstanceSet.insert(fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ delete result;
+ }
+
+ // creature_respawn
+ result = WorldDatabase.PQuery("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
+ if( result )
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ if(InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
+ WorldDatabase.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ delete result;
+ }
+
+ // gameobject_respawn
+ result = WorldDatabase.PQuery("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
+ if( result )
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ if(InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
+ WorldDatabase.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ delete result;
+ }
+
+ bar.step();
+ sLog.outString();
+ sLog.outString( ">> Initialized %u instances", (uint32)InstanceSet.size());
+}
+
+void InstanceSaveManager::PackInstances()
+{
+ // this routine renumbers player instance associations in such a way so they start from 1 and go up
+ // TODO: this can be done a LOT more efficiently
+
+ // obtain set of all associations
+ std::set< uint32 > InstanceSet;
+
+ // all valid ids are in the instance table
+ // any associations to ids not in this table are assumed to be
+ // cleaned already in CleanupInstances
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id FROM instance");
+ if( result )
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ InstanceSet.insert(fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ delete result;
+ }
+
+ barGoLink bar( InstanceSet.size() + 1);
+ bar.step();
+
+ uint32 InstanceNumber = 1;
+ // we do assume std::set is sorted properly on integer value
+ for (std::set< uint32 >::iterator i = InstanceSet.begin(); i != InstanceSet.end(); i++)
+ {
+ if (*i != InstanceNumber)
+ {
+ // remap instance id
+ WorldDatabase.PExecute("UPDATE creature_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i);
+ WorldDatabase.PExecute("UPDATE gameobject_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i);
+ CharacterDatabase.PExecute("UPDATE corpse SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i);
+ CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i);
+ CharacterDatabase.PExecute("UPDATE instance SET id = '%u' WHERE id = '%u'", InstanceNumber, *i);
+ CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i);
+ }
+
+ ++InstanceNumber;
+ bar.step();
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber );
+}
+
+void InstanceSaveManager::LoadResetTimes()
+{
+ time_t now = time(NULL);
+ time_t today = (now / DAY) * DAY;
+
+ // NOTE: Use DirectPExecute for tables that will be queried later
+
+ // get the current reset times for normal instances (these may need to be updated)
+ // these are only kept in memory for InstanceSaves that are loaded later
+ // resettime = 0 in the DB for raid/heroic instances so those are skipped
+ typedef std::map<uint32, std::pair<uint32, uint64> > ResetTimeMapType;
+ ResetTimeMapType InstResetTime;
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id, map, resettime FROM instance WHERE resettime > 0");
+ if( result )
+ {
+ do
+ {
+ if(uint64 resettime = (*result)[2].GetUInt64())
+ {
+ uint32 id = (*result)[0].GetUInt32();
+ uint32 mapid = (*result)[1].GetUInt32();
+ InstResetTime[id] = std::pair<uint32, uint64>(mapid, resettime);
+ }
+ }
+ while (result->NextRow());
+ delete result;
+
+ // update reset time for normal instances with the max creature respawn time + X hours
+ result = WorldDatabase.PQuery("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
+ if( result )
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 instance = fields[1].GetUInt32();
+ uint64 resettime = fields[0].GetUInt64() + 2 * HOUR;
+ ResetTimeMapType::iterator itr = InstResetTime.find(instance);
+ if(itr != InstResetTime.end() && itr->second.second != resettime)
+ {
+ CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", resettime, instance);
+ itr->second.second = resettime;
+ }
+ }
+ while (result->NextRow());
+ delete result;
+ }
+
+ // schedule the reset times
+ for(ResetTimeMapType::iterator itr = InstResetTime.begin(); itr != InstResetTime.end(); ++itr)
+ if(itr->second.second > now)
+ ScheduleReset(true, itr->second.second, InstResetEvent(0, itr->second.first, itr->first));
+ }
+
+ // load the global respawn times for raid/heroic instances
+ uint32 diff = sWorld.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR;
+ m_resetTimeByMapId.resize(sMapStore.GetNumRows()+1);
+ result = CharacterDatabase.Query("SELECT mapid, resettime FROM instance_reset");
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 mapid = fields[0].GetUInt32();
+ if(!objmgr.GetInstanceTemplate(mapid))
+ {
+ sLog.outError("InstanceSaveManager::LoadResetTimes: invalid mapid %u in instance_reset!", mapid);
+ CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u'", mapid);
+ continue;
+ }
+
+ // update the reset time if the hour in the configs changes
+ uint64 oldresettime = fields[1].GetUInt64();
+ uint64 newresettime = (oldresettime / DAY) * DAY + diff;
+ if(oldresettime != newresettime)
+ CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%u'", newresettime, mapid);
+
+ m_resetTimeByMapId[mapid] = newresettime;
+ } while(result->NextRow());
+ delete result;
+ }
+
+ // clean expired instances, references to them will be deleted in CleanupInstances
+ // must be done before calculating new reset times
+ _DelHelper(CharacterDatabase, "id, map, difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map WHERE (instance.resettime < '"I64FMTD"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"I64FMTD"')", (uint64)now, (uint64)now);
+
+ // calculate new global reset times for expired instances and those that have never been reset yet
+ // add the global reset times to the priority queue
+ for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
+ {
+ InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(i);
+ if(!temp) continue;
+ // only raid/heroic maps have a global reset time
+ const MapEntry* entry = sMapStore.LookupEntry(temp->map);
+ if(!entry || !entry->HasResetTime())
+ continue;
+
+ uint32 period = temp->reset_delay * DAY;
+ assert(period != 0);
+ time_t t = m_resetTimeByMapId[temp->map];
+ if(!t)
+ {
+ // initialize the reset time
+ t = today + period + diff;
+ CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"I64FMTD"')", i, (uint64)t);
+ }
+
+ if(t < now)
+ {
+ // assume that expired instances have already been cleaned
+ // calculate the next reset time
+ t = (t / DAY) * DAY;
+ t += ((today - t) / period + 1) * period + diff;
+ CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%u'", (uint64)t, i);
+ }
+
+ m_resetTimeByMapId[temp->map] = t;
+
+ // schedule the global reset/warning
+ uint8 type = 1;
+ static int tim[4] = {3600, 900, 300, 60};
+ for(; type < 4; type++)
+ if(t - tim[type-1] > now) break;
+ ScheduleReset(true, t - tim[type-1], InstResetEvent(type, i));
+ }
+}
+
+void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
+{
+ if(add) m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event));
+ else
+ {
+ // find the event in the queue and remove it
+ ResetTimeQueue::iterator itr;
+ std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
+ range = m_resetTimeQueue.equal_range(time);
+ for(itr = range.first; itr != range.second; ++itr)
+ if(itr->second == event) { m_resetTimeQueue.erase(itr); return; }
+ // in case the reset time changed (should happen very rarely), we search the whole queue
+ if(itr == range.second)
+ {
+ for(itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
+ if(itr->second == event) { m_resetTimeQueue.erase(itr); return; }
+ if(itr == m_resetTimeQueue.end())
+ sLog.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId);
+ }
+ }
+}
+
+void InstanceSaveManager::Update()
+{
+ time_t now = time(NULL), t;
+ while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now)
+ {
+ InstResetEvent &event = m_resetTimeQueue.begin()->second;
+ if(event.type == 0)
+ {
+ // for individual normal instances, max creature respawn + X hours
+ _ResetInstance(event.mapid, event.instanceId);
+ m_resetTimeQueue.erase(m_resetTimeQueue.begin());
+ }
+ else
+ {
+ // global reset/warning for a certain map
+ time_t resetTime = GetResetTimeFor(event.mapid);
+ _ResetOrWarnAll(event.mapid, event.type != 4, resetTime - now);
+ if(event.type != 4)
+ {
+ // schedule the next warning/reset
+ event.type++;
+ static int tim[4] = {3600, 900, 300, 60};
+ ScheduleReset(true, resetTime - tim[event.type-1], event);
+ }
+ m_resetTimeQueue.erase(m_resetTimeQueue.begin());
+ }
+ }
+}
+
+void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
+{
+ // unbind all players bound to the instance
+ // do not allow UnbindInstance to automatically unload the InstanceSaves
+ lock_instLists = true;
+ InstanceSave::PlayerListType &pList = itr->second->m_playerList;
+ while(!pList.empty())
+ {
+ Player *player = *(pList.begin());
+ player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
+ }
+ InstanceSave::GroupListType &gList = itr->second->m_groupList;
+ while(!gList.empty())
+ {
+ Group *group = *(gList.begin());
+ group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
+ }
+ m_instanceSaveById.erase(itr++);
+ lock_instLists = false;
+}
+
+void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId)
+{
+ sLog.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId);
+ Map *map = (MapInstanced*)MapManager::Instance().GetBaseMap(mapid);
+ if(!map->Instanceable())
+ return;
+
+ InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId);
+ if(itr != m_instanceSaveById.end()) _ResetSave(itr);
+ DeleteInstanceFromDB(instanceId); // even if save not loaded
+
+ Map* iMap = ((MapInstanced*)map)->FindMap(instanceId);
+ if(iMap && iMap->IsDungeon()) ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
+ else objmgr.DeleteRespawnTimeForInstance(instanceId); // even if map is not loaded
+}
+
+void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLeft)
+{
+ // global reset for all instances of the given map
+ // note: this isn't fast but it's meant to be executed very rarely
+ Map *map = (MapInstanced*)MapManager::Instance().GetBaseMap(mapid);
+ if(!map->Instanceable())
+ return;
+ uint64 now = (uint64)time(NULL);
+
+ if(!warn)
+ {
+ // this is called one minute before the reset time
+ InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(mapid);
+ if(!temp || !temp->reset_delay)
+ {
+ sLog.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid);
+ return;
+ }
+
+ // remove all binds to instances of the given map
+ for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
+ {
+ if(itr->second->GetMapId() == mapid)
+ _ResetSave(itr);
+ else
+ ++itr;
+ }
+
+ // delete them from the DB, even if not loaded
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid);
+ CharacterDatabase.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid);
+ CharacterDatabase.PExecute("DELETE FROM instance WHERE map = '%u'", mapid);
+ CharacterDatabase.CommitTransaction();
+
+ // calculate the next reset time
+ uint32 diff = sWorld.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR;
+ uint32 period = temp->reset_delay * DAY;
+ uint64 next_reset = ((now + timeLeft + MINUTE) / DAY * DAY) + period + diff;
+ // update it in the DB
+ CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%d'", next_reset, mapid);
+ }
+
+ MapInstanced::InstancedMaps &instMaps = ((MapInstanced*)map)->GetInstancedMaps();
+ MapInstanced::InstancedMaps::iterator mitr;
+ for(mitr = instMaps.begin(); mitr != instMaps.end(); ++mitr)
+ {
+ Map *map = mitr->second;
+ if(!map->IsDungeon()) continue;
+ if(warn) ((InstanceMap*)map)->SendResetWarnings(timeLeft);
+ else ((InstanceMap*)map)->Reset(INSTANCE_RESET_GLOBAL);
+ }
+
+ // TODO: delete creature/gameobject respawn times even if the maps are not loaded
+}
+
+uint32 InstanceSaveManager::GetNumBoundPlayersTotal()
+{
+ uint32 ret = 0;
+ for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
+ ret += itr->second->GetPlayerCount();
+ return ret;
+}
+
+uint32 InstanceSaveManager::GetNumBoundGroupsTotal()
+{
+ uint32 ret = 0;
+ for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
+ ret += itr->second->GetGroupCount();
+ return ret;
+}
diff --git a/src/game/InstanceSaveMgr.h b/src/game/InstanceSaveMgr.h
new file mode 100644
index 00000000000..961e915eb11
--- /dev/null
+++ b/src/game/InstanceSaveMgr.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005,2006,2007 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __InstanceSaveMgr_H
+#define __InstanceSaveMgr_H
+
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "zthread/Mutex.h"
+#include <list>
+#include <map>
+#include "Utilities/HashMap.h"
+#include "Database/DatabaseEnv.h"
+
+struct InstanceTemplate;
+struct MapEntry;
+class Player;
+class Group;
+
+/*
+ Holds the information necessary for creating a new map for an existing instance
+ Is referenced in three cases:
+ - player-instance binds for solo players (not in group)
+ - player-instance binds for permanent heroic/raid saves
+ - group-instance binds (both solo and permanent) cache the player binds for the group leader
+*/
+class InstanceSave
+{
+ friend class InstanceSaveManager;
+ public:
+ /* Created either when:
+ - any new instance is being generated
+ - the first time a player bound to InstanceId logs in
+ - when a group bound to the instance is loaded */
+ InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, time_t resetTime, bool canReset);
+
+ /* Unloaded when m_playerList and m_groupList become empty
+ or when the instance is reset */
+ ~InstanceSave();
+
+ uint8 GetPlayerCount() { return m_playerList.size(); }
+ uint8 GetGroupCount() { return m_groupList.size(); }
+
+ /* A map corresponding to the InstanceId/MapId does not always exist.
+ InstanceSave objects may be created on player logon but the maps are
+ created and loaded only when a player actually enters the instance. */
+ uint32 GetInstanceId() { return m_instanceid; }
+ uint32 GetMapId() { return m_mapid; }
+
+ /* Saved when the instance is generated for the first time */
+ void SaveToDB();
+ /* When the instance is being reset (permanently deleted) */
+ void DeleteFromDB();
+
+ /* for normal instances this corresponds to max(creature respawn time) + X hours
+ for raid/heroic instances this caches the global respawn time for the map */
+ time_t GetResetTime() { return m_resetTime; }
+ void SetResetTime(time_t resetTime) { m_resetTime = resetTime; }
+ time_t GetResetTimeForDB();
+
+ InstanceTemplate const* GetTemplate();
+ MapEntry const* GetMapEntry();
+
+ /* online players bound to the instance (perm/solo)
+ does not include the members of the group unless they have permanent saves */
+ void AddPlayer(Player *player) { m_playerList.push_back(player); }
+ bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); }
+ /* all groups bound to the instance */
+ void AddGroup(Group *group) { m_groupList.push_back(group); }
+ bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); }
+
+ /* instances cannot be reset (except at the global reset time)
+ if there are players permanently bound to it
+ this is cached for the case when those players are offline */
+ bool CanReset() { return m_canReset; }
+ void SetCanReset(bool canReset) { m_canReset = canReset; }
+
+ /* currently it is possible to omit this information from this structure
+ but that would depend on a lot of things that can easily change in future */
+ uint8 GetDifficulty() { return m_difficulty; }
+
+ typedef std::list<Player*> PlayerListType;
+ typedef std::list<Group*> GroupListType;
+ private:
+ bool UnloadIfEmpty();
+ /* the only reason the instSave-object links are kept is because
+ the object-instSave links need to be broken at reset time
+ TODO: maybe it's enough to just store the number of players/groups */
+ PlayerListType m_playerList;
+ GroupListType m_groupList;
+ time_t m_resetTime;
+ uint32 m_instanceid;
+ uint16 m_mapid;
+ uint8 m_difficulty;
+ bool m_canReset;
+};
+
+class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSaveManager, MaNGOS::ClassLevelLockable<InstanceSaveManager, ZThread::Mutex> >
+{
+ friend class InstanceSave;
+ public:
+ InstanceSaveManager();
+ ~InstanceSaveManager();
+
+ typedef std::map<uint32 /*InstanceId*/, InstanceSave*> InstanceSaveMap;
+ typedef HM_NAMESPACE::hash_map<uint32 /*InstanceId*/, InstanceSave*> InstanceSaveHashMap;
+ typedef std::map<uint32 /*mapId*/, InstanceSaveMap> InstanceSaveMapMap;
+
+ /* resetTime is a global propery of each (raid/heroic) map
+ all instances of that map reset at the same time */
+ struct InstResetEvent
+ {
+ uint8 type;
+ uint16 mapid;
+ uint16 instanceId;
+ InstResetEvent(uint8 t = 0, uint16 m = 0, uint16 i = 0) : type(t), mapid(m), instanceId(i) {}
+ bool operator == (const InstResetEvent& e) { return e.instanceId == instanceId; }
+ };
+ typedef std::multimap<time_t /*resetTime*/, InstResetEvent> ResetTimeQueue;
+ typedef std::vector<time_t /*resetTime*/> ResetTimeVector;
+
+ void CleanupInstances();
+ void PackInstances();
+
+ void LoadResetTimes();
+ time_t GetResetTimeFor(uint32 mapid) { return m_resetTimeByMapId[mapid]; }
+ void ScheduleReset(bool add, time_t time, InstResetEvent event);
+
+ void Update();
+
+ InstanceSave* AddInstanceSave(uint32 mapId, uint32 instanceId, uint8 difficulty, time_t resetTime, bool canReset, bool load = false);
+ void RemoveInstanceSave(uint32 InstanceId);
+ static void DeleteInstanceFromDB(uint32 instanceid);
+
+ InstanceSave *GetInstanceSave(uint32 InstanceId);
+
+ /* statistics */
+ uint32 GetNumInstanceSaves() { return m_instanceSaveById.size(); }
+ uint32 GetNumBoundPlayersTotal();
+ uint32 GetNumBoundGroupsTotal();
+
+ private:
+ void _ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeleft);
+ void _ResetInstance(uint32 mapid, uint32 instanceId);
+ void _ResetSave(InstanceSaveHashMap::iterator &itr);
+ void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...);
+ // used during global instance resets
+ bool lock_instLists;
+ // fast lookup by instance id
+ InstanceSaveHashMap m_instanceSaveById;
+ // fast lookup for reset times
+ ResetTimeVector m_resetTimeByMapId;
+ ResetTimeQueue m_resetTimeQueue;
+};
+
+#define sInstanceSaveManager MaNGOS::Singleton<InstanceSaveManager>::Instance()
+#endif
diff --git a/src/game/Item.cpp b/src/game/Item.cpp
new file mode 100644
index 00000000000..b7e5d010773
--- /dev/null
+++ b/src/game/Item.cpp
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Item.h"
+#include "ObjectMgr.h"
+#include "WorldPacket.h"
+#include "Database/DatabaseEnv.h"
+#include "ItemEnchantmentMgr.h"
+
+void AddItemsSetItem(Player*player,Item *item)
+{
+ ItemPrototype const *proto = item->GetProto();
+ uint32 setid = proto->ItemSet;
+
+ ItemSetEntry const *set = sItemSetStore.LookupEntry(setid);
+
+ if(!set)
+ {
+ sLog.outErrorDb("Item set %u for item (id %u) not found, mods not applied.",setid,proto->ItemId);
+ return;
+ }
+
+ if( set->required_skill_id && player->GetSkillValue(set->required_skill_id) < set->required_skill_value )
+ return;
+
+ ItemSetEffect *eff = NULL;
+
+ for(size_t x = 0; x < player->ItemSetEff.size(); ++x)
+ {
+ if(player->ItemSetEff[x] && player->ItemSetEff[x]->setid == setid)
+ {
+ eff = player->ItemSetEff[x];
+ break;
+ }
+ }
+
+ if(!eff)
+ {
+ eff = new ItemSetEffect;
+ memset(eff,0,sizeof(ItemSetEffect));
+ eff->setid = setid;
+
+ size_t x = 0;
+ for(; x < player->ItemSetEff.size(); x++)
+ if(!player->ItemSetEff[x])
+ break;
+
+ if(x < player->ItemSetEff.size())
+ player->ItemSetEff[x]=eff;
+ else
+ player->ItemSetEff.push_back(eff);
+ }
+
+ ++eff->item_count;
+
+ for(uint32 x=0;x<8;x++)
+ {
+ if(!set->spells [x])
+ continue;
+ //not enough for spell
+ if(set->items_to_triggerspell[x] > eff->item_count)
+ continue;
+
+ uint32 z=0;
+ for(;z<8;z++)
+ if(eff->spells[z] && eff->spells[z]->Id==set->spells[x])
+ break;
+
+ if(z < 8)
+ continue;
+
+ //new spell
+ for(uint32 y=0;y<8;y++)
+ {
+ if(!eff->spells[y]) // free slot
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(set->spells[x]);
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %u in items set %u effects", set->spells[x],setid);
+ break;
+ }
+
+ // spell casted only if fit form requirement, in other case will casted at form change
+ player->ApplyEquipSpell(spellInfo,NULL,true);
+ eff->spells[y] = spellInfo;
+ break;
+ }
+ }
+ }
+}
+
+void RemoveItemsSetItem(Player*player,ItemPrototype const *proto)
+{
+ uint32 setid = proto->ItemSet;
+
+ ItemSetEntry const *set = sItemSetStore.LookupEntry(setid);
+
+ if(!set)
+ {
+ sLog.outErrorDb("Item set #%u for item #%u not found, mods not removed.",setid,proto->ItemId);
+ return;
+ }
+
+ ItemSetEffect *eff = NULL;
+ size_t setindex = 0;
+ for(;setindex < player->ItemSetEff.size(); setindex++)
+ {
+ if(player->ItemSetEff[setindex] && player->ItemSetEff[setindex]->setid == setid)
+ {
+ eff = player->ItemSetEff[setindex];
+ break;
+ }
+ }
+
+ // can be in case now enough skill requirement for set appling but set has been appliend when skill requirement not enough
+ if(!eff)
+ return;
+
+ --eff->item_count;
+
+ for(uint32 x=0;x<8;x++)
+ {
+ if(!set->spells[x])
+ continue;
+
+ // enough for spell
+ if(set->items_to_triggerspell[x] <= eff->item_count)
+ continue;
+
+ for(uint32 z=0;z<8;z++)
+ {
+ if(eff->spells[z] && eff->spells[z]->Id==set->spells[x])
+ {
+ // spell can be not active if not fit form requirement
+ player->ApplyEquipSpell(eff->spells[z],NULL,false);
+ eff->spells[z]=NULL;
+ break;
+ }
+ }
+ }
+
+ if(!eff->item_count) //all items of a set were removed
+ {
+ assert(eff == player->ItemSetEff[setindex]);
+ delete eff;
+ player->ItemSetEff[setindex] = NULL;
+ }
+}
+
+bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProto)
+{
+ if(!pProto || !pBagProto)
+ return false;
+
+ switch(pBagProto->Class)
+ {
+ case ITEM_CLASS_CONTAINER:
+ switch(pBagProto->SubClass)
+ {
+ case ITEM_SUBCLASS_CONTAINER:
+ return true;
+ case ITEM_SUBCLASS_SOUL_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_SHARDS))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_HERB_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_HERBS))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_ENCHANTING_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENCHANTING_SUPP))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_MINING_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_ENGINEERING_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENGINEERING_SUPP))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_GEM_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_GEMS))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_LEATHERWORKING_CONTAINER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_LEATHERWORKING_SUPP))
+ return false;
+ return true;
+ default:
+ return false;
+ }
+ case ITEM_CLASS_QUIVER:
+ switch(pBagProto->SubClass)
+ {
+ case ITEM_SUBCLASS_QUIVER:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_ARROWS))
+ return false;
+ return true;
+ case ITEM_SUBCLASS_AMMO_POUCH:
+ if(!(pProto->BagFamily & BAG_FAMILY_MASK_BULLETS))
+ return false;
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+Item::Item( )
+{
+ m_objectType |= TYPEMASK_ITEM;
+ m_objectTypeId = TYPEID_ITEM;
+ // 2.3.2 - 0x18
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID);
+
+ m_valuesCount = ITEM_END;
+ m_slot = 0;
+ uState = ITEM_NEW;
+ uQueuePos = -1;
+ m_container = NULL;
+ m_lootGenerated = false;
+ mb_in_trade = false;
+}
+
+bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner)
+{
+ Object::_Create( guidlow, 0, HIGHGUID_ITEM );
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, itemid);
+ SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);
+
+ SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0);
+ SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0);
+
+ ItemPrototype const *itemProto = objmgr.GetItemPrototype(itemid);
+ if(!itemProto)
+ return false;
+
+ SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
+ SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
+ SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
+
+ for(int i = 0; i < 5; ++i)
+ SetSpellCharges(i,itemProto->Spells[i].SpellCharges);
+
+ SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags);
+ SetUInt32Value(ITEM_FIELD_DURATION, abs(itemProto->Duration));
+
+ return true;
+}
+
+void Item::UpdateDuration(Player* owner, uint32 diff)
+{
+ if (!GetUInt32Value(ITEM_FIELD_DURATION))
+ return;
+
+ sLog.outDebug("Item::UpdateDuration Item (Entry: %u Duration %u Diff %u)",GetEntry(),GetUInt32Value(ITEM_FIELD_DURATION),diff);
+
+ if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff)
+ {
+ owner->DestroyItem(GetBagSlot(), GetSlot(), true);
+ return;
+ }
+
+ SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff);
+ SetState(ITEM_CHANGED); // save new time in database
+}
+
+void Item::SaveToDB()
+{
+ uint32 guid = GetGUIDLow();
+ switch (uState)
+ {
+ case ITEM_NEW:
+ {
+ CharacterDatabase.PExecute( "DELETE FROM item_instance WHERE guid = '%u'", guid );
+ std::ostringstream ss;
+ ss << "INSERT INTO item_instance (guid,owner_guid,data) VALUES (" << guid << "," << GUID_LOPART(GetOwnerGUID()) << ",'";
+ for(uint16 i = 0; i < m_valuesCount; i++ )
+ ss << GetUInt32Value(i) << " ";
+ ss << "' )";
+ CharacterDatabase.Execute( ss.str().c_str() );
+ } break;
+ case ITEM_CHANGED:
+ {
+ std::ostringstream ss;
+ ss << "UPDATE item_instance SET data = '";
+ for(uint16 i = 0; i < m_valuesCount; i++ )
+ ss << GetUInt32Value(i) << " ";
+ ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
+ CharacterDatabase.PExecute("UPDATE character_gifts SET guid = '%u' WHERE item_guid = '%u'", GUID_LOPART(GetOwnerGUID()),GetGUIDLow());
+ } break;
+ case ITEM_REMOVED:
+ {
+ if (GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID) > 0 )
+ CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID));
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid);
+ if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow());
+ delete this;
+ return;
+ }
+ case ITEM_UNCHANGED:
+ break;
+ }
+ SetState(ITEM_UNCHANGED);
+}
+
+bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
+{
+ // create item before any checks for store correct guid
+ // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
+ Object::_Create(guid, 0, HIGHGUID_ITEM);
+
+ bool delete_result = false;
+ if(!result)
+ {
+ result = CharacterDatabase.PQuery("SELECT data FROM item_instance WHERE guid = '%u'", guid);
+ delete_result = true;
+ }
+
+ if (!result)
+ {
+ sLog.outError("ERROR: Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ",guid,GUID_LOPART(owner_guid));
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+
+ if(!LoadValues(fields[0].GetString()))
+ {
+ sLog.outError("ERROR: Item #%d have broken data in `data` field. Can't be loaded.",guid);
+ if (delete_result) delete result;
+ return false;
+ }
+
+ bool need_save = false; // need explicit save data at load fixes
+
+ // overwrite possible wrong/corrupted guid
+ uint64 new_item_guid = MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM);
+ if(GetUInt64Value(OBJECT_FIELD_GUID) != new_item_guid)
+ {
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM));
+ need_save = true;
+ }
+
+ if (delete_result) delete result;
+
+ ItemPrototype const* proto = GetProto();
+ if(!proto)
+ return false;
+
+ // recalculate suffix factor
+ if(GetItemRandomPropertyId() < 0)
+ {
+ if(UpdateItemSuffixFactor())
+ need_save = true;
+ }
+
+ // Remove bind flag for items vs NO_BIND set
+ if (IsSoulBound() && proto->Bonding == NO_BIND)
+ {
+ ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED, false);
+ need_save = true;
+ }
+
+ // update duration if need, and remove if not need
+ if((proto->Duration==0) != (GetUInt32Value(ITEM_FIELD_DURATION)==0))
+ {
+ SetUInt32Value(ITEM_FIELD_DURATION,abs(proto->Duration));
+ need_save = true;
+ }
+
+ // set correct owner
+ if(owner_guid != 0 && GetOwnerGUID() != owner_guid)
+ {
+ SetOwnerGUID(owner_guid);
+ need_save = true;
+ }
+
+ if(need_save) // normal item changed state set not work at loading
+ {
+ std::ostringstream ss;
+ ss << "UPDATE item_instance SET data = '";
+ for(uint16 i = 0; i < m_valuesCount; i++ )
+ ss << GetUInt32Value(i) << " ";
+ ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+ }
+
+ return true;
+}
+
+void Item::DeleteFromDB()
+{
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow());
+}
+
+void Item::DeleteFromInventoryDB()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'",GetGUIDLow());
+}
+
+ItemPrototype const *Item::GetProto() const
+{
+ return objmgr.GetItemPrototype(GetUInt32Value(OBJECT_FIELD_ENTRY));
+}
+
+Player* Item::GetOwner()const
+{
+ return objmgr.GetPlayer(GetOwnerGUID());
+}
+
+uint32 Item::GetSkill()
+{
+ const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
+ {
+ SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES,
+ SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0,
+ SKILL_STAVES, 0, 0, SKILL_UNARMED, 0,
+ SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
+ SKILL_FISHING
+ };
+
+ const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] =
+ {
+ 0,SKILL_CLOTH,SKILL_LEATHER,SKILL_MAIL,SKILL_PLATE_MAIL,0,SKILL_SHIELD,0,0,0
+ };
+
+ ItemPrototype const* proto = GetProto();
+
+ switch (proto->Class)
+ {
+ case ITEM_CLASS_WEAPON:
+ if( proto->SubClass >= MAX_ITEM_SUBCLASS_WEAPON )
+ return 0;
+ else
+ return item_weapon_skills[proto->SubClass];
+
+ case ITEM_CLASS_ARMOR:
+ if( proto->SubClass >= MAX_ITEM_SUBCLASS_ARMOR )
+ return 0;
+ else
+ return item_armor_skills[proto->SubClass];
+
+ default:
+ return 0;
+ }
+}
+
+uint32 Item::GetSpell()
+{
+ ItemPrototype const* proto = GetProto();
+
+ switch (proto->Class)
+ {
+ case ITEM_CLASS_WEAPON:
+ switch (proto->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_AXE: return 196;
+ case ITEM_SUBCLASS_WEAPON_AXE2: return 197;
+ case ITEM_SUBCLASS_WEAPON_BOW: return 264;
+ case ITEM_SUBCLASS_WEAPON_GUN: return 266;
+ case ITEM_SUBCLASS_WEAPON_MACE: return 198;
+ case ITEM_SUBCLASS_WEAPON_MACE2: return 199;
+ case ITEM_SUBCLASS_WEAPON_POLEARM: return 200;
+ case ITEM_SUBCLASS_WEAPON_SWORD: return 201;
+ case ITEM_SUBCLASS_WEAPON_SWORD2: return 202;
+ case ITEM_SUBCLASS_WEAPON_STAFF: return 227;
+ case ITEM_SUBCLASS_WEAPON_DAGGER: return 1180;
+ case ITEM_SUBCLASS_WEAPON_THROWN: return 2567;
+ case ITEM_SUBCLASS_WEAPON_SPEAR: return 3386;
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:return 5011;
+ case ITEM_SUBCLASS_WEAPON_WAND: return 5009;
+ default: return 0;
+ }
+ case ITEM_CLASS_ARMOR:
+ switch(proto->SubClass)
+ {
+ case ITEM_SUBCLASS_ARMOR_CLOTH: return 9078;
+ case ITEM_SUBCLASS_ARMOR_LEATHER: return 9077;
+ case ITEM_SUBCLASS_ARMOR_MAIL: return 8737;
+ case ITEM_SUBCLASS_ARMOR_PLATE: return 750;
+ case ITEM_SUBCLASS_ARMOR_SHIELD: return 9116;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+int32 Item::GenerateItemRandomPropertyId(uint32 item_id)
+{
+ ItemPrototype const *itemProto = sItemStorage.LookupEntry<ItemPrototype>(item_id);
+
+ if(!itemProto)
+ return 0;
+
+ // item must have one from this field values not null if it can have random enchantments
+ if((!itemProto->RandomProperty) && (!itemProto->RandomSuffix))
+ return 0;
+
+ // item can have not null only one from field values
+ if((itemProto->RandomProperty) && (itemProto->RandomSuffix))
+ {
+ sLog.outErrorDb("Item template %u have RandomProperty==%u and RandomSuffix==%u, but must have one from field =0",itemProto->ItemId,itemProto->RandomProperty,itemProto->RandomSuffix);
+ return 0;
+ }
+
+ // RandomProperty case
+ if(itemProto->RandomProperty)
+ {
+ uint32 randomPropId = GetItemEnchantMod(itemProto->RandomProperty);
+ ItemRandomPropertiesEntry const *random_id = sItemRandomPropertiesStore.LookupEntry(randomPropId);
+ if(!random_id)
+ {
+ sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in 'ItemRandomProperties.dbc'",randomPropId);
+ return 0;
+ }
+
+ return random_id->ID;
+ }
+ // RandomSuffix case
+ else
+ {
+ uint32 randomPropId = GetItemEnchantMod(itemProto->RandomSuffix);
+ ItemRandomSuffixEntry const *random_id = sItemRandomSuffixStore.LookupEntry(randomPropId);
+ if(!random_id)
+ {
+ sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in sItemRandomSuffixStore.",randomPropId);
+ return 0;
+ }
+
+ return -int32(random_id->ID);
+ }
+}
+
+void Item::SetItemRandomProperties(int32 randomPropId)
+{
+ if(!randomPropId)
+ return;
+
+ if(randomPropId > 0)
+ {
+ ItemRandomPropertiesEntry const *item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropId);
+ if(item_rand)
+ {
+ if(GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != int32(item_rand->ID))
+ {
+ SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,item_rand->ID);
+ SetState(ITEM_CHANGED);
+ }
+ for(uint32 i = PROP_ENCHANTMENT_SLOT_2; i < PROP_ENCHANTMENT_SLOT_2 + 3; ++i)
+ SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_2],0,0);
+ }
+ }
+ else
+ {
+ ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropId);
+ if(item_rand)
+ {
+ if( GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != -int32(item_rand->ID) ||
+ !GetItemSuffixFactor())
+ {
+ SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,-int32(item_rand->ID));
+ UpdateItemSuffixFactor();
+ SetState(ITEM_CHANGED);
+ }
+
+ for(uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i)
+ SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0],0,0);
+ }
+ }
+}
+
+bool Item::UpdateItemSuffixFactor()
+{
+ uint32 suffixFactor = GenerateEnchSuffixFactor(GetEntry());
+ if(GetItemSuffixFactor()==suffixFactor)
+ return false;
+ SetUInt32Value(ITEM_FIELD_PROPERTY_SEED,suffixFactor);
+ return true;
+}
+
+void Item::SetState(ItemUpdateState state, Player *forplayer)
+{
+ if (uState == ITEM_NEW && state == ITEM_REMOVED)
+ {
+ // pretend the item never existed
+ RemoveFromUpdateQueueOf(forplayer);
+ delete this;
+ return;
+ }
+
+ if (state != ITEM_UNCHANGED)
+ {
+ // new items must stay in new state until saved
+ if (uState != ITEM_NEW) uState = state;
+ AddToUpdateQueueOf(forplayer);
+ }
+ else
+ {
+ // unset in queue
+ // the item must be removed from the queue manually
+ uQueuePos = -1;
+ uState = ITEM_UNCHANGED;
+ }
+}
+
+void Item::AddToUpdateQueueOf(Player *player)
+{
+ if (IsInUpdateQueue()) return;
+
+ if (!player)
+ {
+ player = GetOwner();
+ if (!player)
+ {
+ sLog.outError("Item::AddToUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID()));
+ return;
+ }
+ }
+
+ if (player->GetGUID() != GetOwnerGUID())
+ {
+ sLog.outError("Item::AddToUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow());
+ return;
+ }
+
+ if (player->m_itemUpdateQueueBlocked) return;
+
+ player->m_itemUpdateQueue.push_back(this);
+ uQueuePos = player->m_itemUpdateQueue.size()-1;
+}
+
+void Item::RemoveFromUpdateQueueOf(Player *player)
+{
+ if (!IsInUpdateQueue()) return;
+
+ if (!player)
+ {
+ player = GetOwner();
+ if (!player)
+ {
+ sLog.outError("Item::RemoveFromUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID()));
+ return;
+ }
+ }
+
+ if (player->GetGUID() != GetOwnerGUID())
+ {
+ sLog.outError("Item::RemoveFromUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow());
+ return;
+ }
+
+ if (player->m_itemUpdateQueueBlocked) return;
+
+ player->m_itemUpdateQueue[uQueuePos] = NULL;
+ uQueuePos = -1;
+}
+
+uint8 Item::GetBagSlot() const
+{
+ return m_container ? m_container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0);
+}
+
+bool Item::IsEquipped() const
+{
+ return !IsInBag() && m_slot < EQUIPMENT_SLOT_END;
+}
+
+bool Item::CanBeTraded() const
+{
+ if(IsSoulBound())
+ return false;
+ if(IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) )
+ return false;
+
+ if(Player* owner = GetOwner())
+ {
+ if(owner->CanUnequipItem(GetPos(),false) != EQUIP_ERR_OK )
+ return false;
+ if(owner->GetLootGUID()==GetGUID())
+ return false;
+ }
+
+ if (IsBoundByEnchant())
+ return false;
+
+ return true;
+}
+
+bool Item::IsBoundByEnchant() const
+{
+ // Check all enchants for soulbound
+ for(uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot)
+ {
+ uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ if(enchantEntry->slot & ENCHANTMENT_CAN_SOULBOUND)
+ return true;
+ }
+ return false;
+}
+
+bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const
+{
+ ItemPrototype const* proto = GetProto();
+
+ if (spellInfo->EquippedItemClass != -1) // -1 == any item class
+ {
+ if(spellInfo->EquippedItemClass != int32(proto->Class))
+ return false; // wrong item class
+
+ if(spellInfo->EquippedItemSubClassMask != 0) // 0 == any subclass
+ {
+ if((spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass)) == 0)
+ return false; // subclass not present in mask
+ }
+ }
+
+ if(spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type
+ {
+ if((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0)
+ return false; // inventory type not present in mask
+ }
+
+ return true;
+}
+
+void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges)
+{
+ // Better lost small time at check in comparison lost time at item save to DB.
+ if( GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET)==id &&
+ GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration &&
+ GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET)==charges )
+ return;
+
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id);
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
+ SetState(ITEM_CHANGED);
+}
+
+void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration)
+{
+ if(GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration)
+ return;
+
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
+ SetState(ITEM_CHANGED);
+}
+
+void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges)
+{
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
+ SetState(ITEM_CHANGED);
+}
+
+void Item::ClearEnchantment(EnchantmentSlot slot)
+{
+ if(!GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET))
+ return;
+
+ for(int x=0;x<3;x++)
+ SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + x,0);
+ SetState(ITEM_CHANGED);
+}
+
+bool Item::GemsFitSockets() const
+{
+ bool fits = true;
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint8 SocketColor = GetProto()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color;
+
+ uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ {
+ if(SocketColor) fits &= false;
+ continue;
+ }
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ {
+ if(SocketColor) fits &= false;
+ continue;
+ }
+
+ uint8 GemColor = 0;
+
+ uint32 gemid = enchantEntry->GemID;
+ if(gemid)
+ {
+ ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
+ if(gemProto)
+ {
+ GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
+ if(gemProperty)
+ GemColor = gemProperty->color;
+ }
+ }
+
+ fits &= (GemColor & SocketColor) ? true : false;
+ }
+ return fits;
+}
+
+uint8 Item::GetGemCountWithID(uint32 GemID) const
+{
+ uint8 count = 0;
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ if(GemID == enchantEntry->GemID)
+ ++count;
+ }
+ return count;
+}
+
+bool Item::IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const
+{
+ ItemPrototype const* proto = GetProto();
+ return proto && (proto->Map && proto->Map != cur_mapId || proto->Area && proto->Area != cur_zoneId );
+}
+
+// Though the client has the information in the item's data field,
+// we have to send SMSG_ITEM_TIME_UPDATE to display the remaining
+// time.
+void Item::SendTimeUpdate(Player* owner)
+{
+ if (!GetUInt32Value(ITEM_FIELD_DURATION))
+ return;
+
+ WorldPacket data(SMSG_ITEM_TIME_UPDATE, (8+4));
+ data << (uint64)GetGUID();
+ data << (uint32)GetUInt32Value(ITEM_FIELD_DURATION);
+ owner->GetSession()->SendPacket(&data);
+}
+
+Item* Item::CreateItem( uint32 item, uint32 count, Player const* player )
+{
+ if ( count < 1 )
+ return NULL; //don't create item at zero count
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ if ( count > pProto->Stackable )
+ count = pProto->Stackable;
+
+ assert(count !=0 && "pProto->Stackable==0 but checked at loading already");
+
+ Item *pItem = NewItemOrBag( pProto );
+ if( pItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), item, player) )
+ {
+ pItem->SetCount( count );
+ return pItem;
+ }
+ else
+ delete pItem;
+ }
+ return NULL;
+}
+
+Item* Item::CloneItem( uint32 count, Player const* player ) const
+{
+ Item* newItem = CreateItem( GetEntry(), count, player );
+ if(!newItem)
+ return NULL;
+
+ newItem->SetUInt32Value( ITEM_FIELD_CREATOR, GetUInt32Value( ITEM_FIELD_CREATOR ) );
+ newItem->SetUInt32Value( ITEM_FIELD_GIFTCREATOR, GetUInt32Value( ITEM_FIELD_GIFTCREATOR ) );
+ newItem->SetUInt32Value( ITEM_FIELD_FLAGS, GetUInt32Value( ITEM_FIELD_FLAGS ) );
+ newItem->SetUInt32Value( ITEM_FIELD_DURATION, GetUInt32Value( ITEM_FIELD_DURATION ) );
+ newItem->SetItemRandomProperties(GetItemRandomPropertyId());
+ return newItem;
+}
diff --git a/src/game/Item.h b/src/game/Item.h
new file mode 100644
index 00000000000..7379edd03f8
--- /dev/null
+++ b/src/game/Item.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_ITEM_H
+#define MANGOSSERVER_ITEM_H
+
+#include "Common.h"
+#include "Object.h"
+#include "LootMgr.h"
+#include "ItemPrototype.h"
+
+struct SpellEntry;
+class Bag;
+class QueryResult;
+
+struct ItemSetEffect
+{
+ uint32 setid;
+ uint32 item_count;
+ SpellEntry const *spells[8];
+};
+
+enum InventoryChangeFailure
+{
+ EQUIP_ERR_OK = 0,
+ EQUIP_ERR_CANT_EQUIP_LEVEL_I = 1,
+ EQUIP_ERR_ERR_CANT_EQUIP_SKILL = 2,
+ EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT = 3,
+ EQUIP_ERR_BAG_FULL = 4,
+ EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG = 5,
+ EQUIP_ERR_CANT_TRADE_EQUIP_BAGS = 6,
+ EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE = 7,
+ EQUIP_ERR_NO_REQUIRED_PROFICIENCY = 8,
+ EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE = 9,
+ EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM = 10,
+ EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM2 = 11,
+ EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE2 = 12,
+ EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED = 13,
+ EQUIP_ERR_CANT_DUAL_WIELD = 14,
+ EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG = 15,
+ EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2 = 16,
+ EQUIP_ERR_CANT_CARRY_MORE_OF_THIS = 17,
+ EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE3 = 18,
+ EQUIP_ERR_ITEM_CANT_STACK = 19,
+ EQUIP_ERR_ITEM_CANT_BE_EQUIPPED = 20,
+ EQUIP_ERR_ITEMS_CANT_BE_SWAPPED = 21,
+ EQUIP_ERR_SLOT_IS_EMPTY = 22,
+ EQUIP_ERR_ITEM_NOT_FOUND = 23,
+ EQUIP_ERR_CANT_DROP_SOULBOUND = 24,
+ EQUIP_ERR_OUT_OF_RANGE = 25,
+ EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT = 26,
+ EQUIP_ERR_COULDNT_SPLIT_ITEMS = 27,
+ EQUIP_ERR_MISSING_REAGENT = 28,
+ EQUIP_ERR_NOT_ENOUGH_MONEY = 29,
+ EQUIP_ERR_NOT_A_BAG = 30,
+ EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS = 31,
+ EQUIP_ERR_DONT_OWN_THAT_ITEM = 32,
+ EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER = 33,
+ EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT = 34,
+ EQUIP_ERR_TOO_FAR_AWAY_FROM_BANK = 35,
+ EQUIP_ERR_ITEM_LOCKED = 36,
+ EQUIP_ERR_YOU_ARE_STUNNED = 37,
+ EQUIP_ERR_YOU_ARE_DEAD = 38,
+ EQUIP_ERR_CANT_DO_RIGHT_NOW = 39,
+ EQUIP_ERR_INT_BAG_ERROR = 40,
+ EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER2 = 41,
+ EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH = 42,
+ EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED = 43,
+ EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED = 44,
+ EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED = 45,
+ EQUIP_ERR_BOUND_CANT_BE_WRAPPED = 46,
+ EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED = 47,
+ EQUIP_ERR_BAGS_CANT_BE_WRAPPED = 48,
+ EQUIP_ERR_ALREADY_LOOTED = 49,
+ EQUIP_ERR_INVENTORY_FULL = 50,
+ EQUIP_ERR_BANK_FULL = 51,
+ EQUIP_ERR_ITEM_IS_CURRENTLY_SOLD_OUT = 52,
+ EQUIP_ERR_BAG_FULL3 = 53,
+ EQUIP_ERR_ITEM_NOT_FOUND2 = 54,
+ EQUIP_ERR_ITEM_CANT_STACK2 = 55,
+ EQUIP_ERR_BAG_FULL4 = 56,
+ EQUIP_ERR_ITEM_SOLD_OUT = 57,
+ EQUIP_ERR_OBJECT_IS_BUSY = 58,
+ EQUIP_ERR_NONE = 59,
+ EQUIP_ERR_NOT_IN_COMBAT = 60,
+ EQUIP_ERR_NOT_WHILE_DISARMED = 61,
+ EQUIP_ERR_BAG_FULL6 = 62,
+ EQUIP_ERR_CANT_EQUIP_RANK = 63,
+ EQUIP_ERR_CANT_EQUIP_REPUTATION = 64,
+ EQUIP_ERR_TOO_MANY_SPECIAL_BAGS = 65,
+ EQUIP_ERR_LOOT_CANT_LOOT_THAT_NOW = 66,
+ EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE = 67,
+ EQUIP_ERR_VENDOR_MISSING_TURNINS = 68,
+ EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS = 69,
+ EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS = 70,
+ EQUIP_ERR_ITEM_MAX_COUNT_SOCKETED = 71,
+ EQUIP_ERR_MAIL_BOUND_ITEM = 72,
+ EQUIP_ERR_NO_SPLIT_WHILE_PROSPECTING = 73,
+ EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED = 75,
+ EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED = 76,
+ EQUIP_ERR_TOO_MUCH_GOLD = 77,
+ EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78,
+ EQUIP_ERR_CANNOT_TRADE_THAT = 79,
+ EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80
+ // probably exist more
+};
+
+enum BuyFailure
+{
+ BUY_ERR_CANT_FIND_ITEM = 0,
+ BUY_ERR_ITEM_ALREADY_SOLD = 1,
+ BUY_ERR_NOT_ENOUGHT_MONEY = 2,
+ BUY_ERR_SELLER_DONT_LIKE_YOU = 4,
+ BUY_ERR_DISTANCE_TOO_FAR = 5,
+ BUY_ERR_ITEM_SOLD_OUT = 7,
+ BUY_ERR_CANT_CARRY_MORE = 8,
+ BUY_ERR_RANK_REQUIRE = 11,
+ BUY_ERR_REPUTATION_REQUIRE = 12
+};
+
+enum SellFailure
+{
+ SELL_ERR_CANT_FIND_ITEM = 1,
+ SELL_ERR_CANT_SELL_ITEM = 2, // merchant doesn't like that item
+ SELL_ERR_CANT_FIND_VENDOR = 3, // merchant doesn't like you
+ SELL_ERR_YOU_DONT_OWN_THAT_ITEM = 4, // you don't own that item
+ SELL_ERR_UNK = 5, // nothing appears...
+ SELL_ERR_ONLY_EMPTY_BAG = 6 // can only do with empty bags
+};
+
+// -1 from client enchantment slot number
+enum EnchantmentSlot
+{
+ PERM_ENCHANTMENT_SLOT = 0,
+ TEMP_ENCHANTMENT_SLOT = 1,
+ SOCK_ENCHANTMENT_SLOT = 2,
+ SOCK_ENCHANTMENT_SLOT_2 = 3,
+ SOCK_ENCHANTMENT_SLOT_3 = 4,
+ BONUS_ENCHANTMENT_SLOT = 5,
+ MAX_INSPECTED_ENCHANTMENT_SLOT = 6,
+
+ PROP_ENCHANTMENT_SLOT_0 = 6, // used with RandomSuffix
+ PROP_ENCHANTMENT_SLOT_1 = 7, // used with RandomSuffix
+ PROP_ENCHANTMENT_SLOT_2 = 8, // used with RandomSuffix and RandomProperty
+ PROP_ENCHANTMENT_SLOT_3 = 9, // used with RandomProperty
+ PROP_ENCHANTMENT_SLOT_4 = 10, // used with RandomProperty
+ MAX_ENCHANTMENT_SLOT = 11
+};
+
+#define MAX_VISIBLE_ITEM_OFFSET 16 // 16 fields per visible item (creator(2) + enchantments(12) + properties(1) + pad(1))
+
+enum EnchantmentOffset
+{
+ ENCHANTMENT_ID_OFFSET = 0,
+ ENCHANTMENT_DURATION_OFFSET = 1,
+ ENCHANTMENT_CHARGES_OFFSET = 2
+};
+
+#define MAX_ENCHANTMENT_OFFSET 3
+
+enum EnchantmentSlotMask
+{
+ ENCHANTMENT_CAN_SOULBOUND = 0x01,
+ ENCHANTMENT_UNK1 = 0x02,
+ ENCHANTMENT_UNK2 = 0x04,
+ ENCHANTMENT_UNK3 = 0x08
+};
+
+enum ItemUpdateState
+{
+ ITEM_UNCHANGED = 0,
+ ITEM_CHANGED = 1,
+ ITEM_NEW = 2,
+ ITEM_REMOVED = 3
+};
+
+bool ItemCanGoIntoBag(ItemPrototype const *proto, ItemPrototype const *pBagProto);
+
+class MANGOS_DLL_SPEC Item : public Object
+{
+ public:
+ static Item* CreateItem( uint32 item, uint32 count, Player const* player = NULL );
+ Item* CloneItem( uint32 count, Player const* player = NULL ) const;
+
+ Item ( );
+
+ virtual bool Create( uint32 guidlow, uint32 itemid, Player const* owner);
+
+ ItemPrototype const* GetProto() const;
+
+ uint64 const& GetOwnerGUID() const { return GetUInt64Value(ITEM_FIELD_OWNER); }
+ void SetOwnerGUID(uint64 guid) { SetUInt64Value(ITEM_FIELD_OWNER, guid); }
+ Player* GetOwner()const;
+
+ void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); }
+ bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); }
+ bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; }
+ bool IsBoundByEnchant() const;
+ virtual void SaveToDB();
+ virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL);
+ virtual void DeleteFromDB();
+ void DeleteFromInventoryDB();
+
+ bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; }
+ bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; }
+ bool CanBeTraded() const;
+ void SetInTrade(bool b = true) { mb_in_trade = b; }
+ bool IsInTrade() const { return mb_in_trade; }
+
+ bool IsFitToSpellRequirements(SpellEntry const* spellInfo) const;
+ bool IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const;
+ bool GemsFitSockets() const;
+
+ uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); }
+ uint32 GetCount() const { return GetUInt32Value (ITEM_FIELD_STACK_COUNT); }
+ void SetCount(uint32 value) { SetUInt32Value (ITEM_FIELD_STACK_COUNT, value); }
+ uint32 GetMaxStackCount() const { return GetProto()->Stackable; }
+ uint8 GetGemCountWithID(uint32 GemID) const;
+
+ uint8 GetSlot() const {return m_slot;}
+ Bag *GetContainer() { return m_container; }
+ uint8 GetBagSlot() const;
+ void SetSlot(uint8 slot) {m_slot = slot;}
+ uint16 GetPos() const { return uint16(GetBagSlot()) << 8 | GetSlot(); }
+ void SetContainer(Bag *container) { m_container = container; }
+
+ bool IsInBag() const { return m_container != NULL; }
+ bool IsEquipped() const;
+
+ uint32 GetSkill();
+ uint32 GetSpell();
+
+ // RandomPropertyId (signed but stored as unsigned)
+ int32 GetItemRandomPropertyId() const { return GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID); }
+ uint32 GetItemSuffixFactor() const { return GetUInt32Value(ITEM_FIELD_PROPERTY_SEED); }
+ void SetItemRandomProperties(int32 randomPropId);
+ bool UpdateItemSuffixFactor();
+ static int32 GenerateItemRandomPropertyId(uint32 item_id);
+ void SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges);
+ void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration);
+ void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges);
+ void ClearEnchantment(EnchantmentSlot slot);
+ uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);}
+ uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);}
+ uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET);}
+
+ void SendTimeUpdate(Player* owner);
+ void UpdateDuration(Player* owner, uint32 diff);
+
+ // spell charges (signed but stored as unsigned)
+ int32 GetSpellCharges(uint8 index/*0..5*/ = 0) const { return GetInt32Value(ITEM_FIELD_SPELL_CHARGES + index); }
+ void SetSpellCharges(uint8 index/*0..5*/, int32 value) { SetInt32Value(ITEM_FIELD_SPELL_CHARGES + index,value); }
+
+ Loot loot;
+ bool m_lootGenerated;
+
+ // Update States
+ ItemUpdateState GetState() const { return uState; }
+ void SetState(ItemUpdateState state, Player *forplayer = NULL);
+ void AddToUpdateQueueOf(Player *player);
+ void RemoveFromUpdateQueueOf(Player *player);
+ bool IsInUpdateQueue() const { return uQueuePos != -1; }
+ uint16 GetQueuePos() const { return uQueuePos; }
+ void FSetState(ItemUpdateState state) // forced
+ {
+ uState = state;
+ }
+
+ bool hasQuest(uint32 quest_id) const
+ {
+ ItemPrototype const *itemProto = GetProto();
+ return itemProto && itemProto->StartQuest == quest_id;
+ }
+ bool hasInvolvedQuest(uint32 /*quest_id*/) const { return false; }
+
+ private:
+ uint8 m_slot;
+ Bag *m_container;
+ ItemUpdateState uState;
+ int16 uQueuePos;
+ bool mb_in_trade; // true if item is currently in trade-window
+};
+#endif
diff --git a/src/game/ItemEnchantmentMgr.cpp b/src/game/ItemEnchantmentMgr.cpp
new file mode 100644
index 00000000000..9fd2e961745
--- /dev/null
+++ b/src/game/ItemEnchantmentMgr.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 <stdlib.h>
+#include <functional>
+#include "ItemEnchantmentMgr.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include <list>
+#include <vector>
+#include "Util.h"
+
+struct EnchStoreItem
+{
+ uint32 ench;
+ float chance;
+
+ EnchStoreItem()
+ : ench(0), chance(0) {}
+
+ EnchStoreItem(uint32 _ench, float _chance)
+ : ench(_ench), chance(_chance) {}
+};
+
+typedef std::vector<EnchStoreItem> EnchStoreList;
+typedef HM_NAMESPACE::hash_map<uint32, EnchStoreList> EnchantmentStore;
+
+static EnchantmentStore RandomItemEnch;
+
+void LoadRandomEnchantmentsTable()
+{
+ RandomItemEnch.clear(); // for reload case
+
+ EnchantmentStore::iterator tab;
+ uint32 entry, ench;
+ float chance;
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT entry, ench, chance FROM item_enchantment_template");
+
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ entry = fields[0].GetUInt32();
+ ench = fields[1].GetUInt32();
+ chance = fields[2].GetFloat();
+
+ if (chance > 0.000001f && chance <= 100.0f)
+ RandomItemEnch[entry].push_back( EnchStoreItem(ench, chance) );
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Item Enchantment definitions", count );
+ }
+ else
+ {
+ sLog.outString();
+ sLog.outErrorDb( ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");
+ }
+}
+
+uint32 GetItemEnchantMod(uint32 entry)
+{
+ if (!entry) return 0;
+
+ EnchantmentStore::iterator tab = RandomItemEnch.find(entry);
+
+ if (tab == RandomItemEnch.end())
+ {
+ sLog.outErrorDb("Item RandomProperty / RandomSuffix id #%u used in `item_template` but it doesn't have records in `item_enchantment_template` table.",entry);
+ return 0;
+ }
+
+ double dRoll = rand_chance();
+ float fCount = 0;
+
+ for(EnchStoreList::iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter)
+ {
+ fCount += ench_iter->chance;
+
+ if (fCount > dRoll) return ench_iter->ench;
+ }
+
+ //we could get here only if sum of all enchantment chances is lower than 100%
+ dRoll = (irand(0, (int)floor(fCount * 100) + 1)) / 100;
+ fCount = 0;
+
+ for(EnchStoreList::iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter)
+ {
+ fCount += ench_iter->chance;
+
+ if (fCount > dRoll) return ench_iter->ench;
+ }
+
+ return 0;
+}
+
+uint32 GenerateEnchSuffixFactor(uint32 item_id)
+{
+ ItemPrototype const *itemProto = objmgr.GetItemPrototype(item_id);
+
+ if(!itemProto)
+ return 0;
+ if(!itemProto->RandomSuffix)
+ return 0;
+
+ RandomPropertiesPointsEntry const *randomProperty = sRandomPropertiesPointsStore.LookupEntry(itemProto->ItemLevel);
+ if(!randomProperty)
+ return 0;
+
+ uint32 suffixFactor;
+ switch(itemProto->InventoryType)
+ {
+ // Items of that type don`t have points
+ case INVTYPE_NON_EQUIP:
+ case INVTYPE_BAG:
+ case INVTYPE_TABARD:
+ case INVTYPE_AMMO:
+ case INVTYPE_QUIVER:
+ case INVTYPE_RELIC:
+ return 0;
+ // Select point coefficient
+ case INVTYPE_HEAD:
+ case INVTYPE_BODY:
+ case INVTYPE_CHEST:
+ case INVTYPE_LEGS:
+ case INVTYPE_2HWEAPON:
+ case INVTYPE_ROBE:
+ suffixFactor = 0;
+ break;
+ case INVTYPE_SHOULDERS:
+ case INVTYPE_WAIST:
+ case INVTYPE_FEET:
+ case INVTYPE_HANDS:
+ case INVTYPE_TRINKET:
+ suffixFactor = 1;
+ break;
+ case INVTYPE_NECK:
+ case INVTYPE_WRISTS:
+ case INVTYPE_FINGER:
+ case INVTYPE_SHIELD:
+ case INVTYPE_CLOAK:
+ case INVTYPE_HOLDABLE:
+ suffixFactor = 2;
+ break;
+ case INVTYPE_WEAPON:
+ case INVTYPE_WEAPONMAINHAND:
+ case INVTYPE_WEAPONOFFHAND:
+ suffixFactor = 3;
+ break;
+ case INVTYPE_RANGED:
+ case INVTYPE_THROWN:
+ case INVTYPE_RANGEDRIGHT:
+ suffixFactor = 4;
+ break;
+ default:
+ return 0;
+ }
+ // Select rare/epic modifier
+ switch (itemProto->Quality)
+ {
+ case ITEM_QUALITY_UNCOMMON:
+ return randomProperty->UncommonPropertiesPoints[suffixFactor];
+ case ITEM_QUALITY_RARE:
+ return randomProperty->RarePropertiesPoints[suffixFactor];
+ case ITEM_QUALITY_EPIC:
+ return randomProperty->EpicPropertiesPoints[suffixFactor];
+ case ITEM_QUALITY_LEGENDARY:
+ case ITEM_QUALITY_ARTIFACT:
+ return 0; // not have random properties
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/src/game/ItemEnchantmentMgr.h b/src/game/ItemEnchantmentMgr.h
new file mode 100644
index 00000000000..95c93cf39df
--- /dev/null
+++ b/src/game/ItemEnchantmentMgr.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _ITEM_ENCHANTMENT_MGR_H
+#define _ITEM_ENCHANTMENT_MGR_H
+
+#include "Common.h"
+
+void LoadRandomEnchantmentsTable();
+uint32 GetItemEnchantMod(uint32 entry);
+uint32 GenerateEnchSuffixFactor(uint32 item_id);
+#endif
diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp
new file mode 100644
index 00000000000..de687ad6a09
--- /dev/null
+++ b/src/game/ItemHandler.cpp
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Item.h"
+#include "UpdateData.h"
+#include "ObjectAccessor.h"
+
+void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
+ uint8 srcbag, srcslot, dstbag, dstslot, count;
+
+ recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ if(src==dst)
+ return;
+
+ if (count==0)
+ return; //check count - if zero it's fake packet
+
+ _player->SplitItem( src, dst, count );
+}
+
+void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM");
+ uint8 srcslot, dstslot;
+
+ recv_data >> srcslot >> dstslot;
+ //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(srcslot==dstslot)
+ return;
+
+ uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot );
+ uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot );
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+ uint64 itemguid;
+ uint8 dstslot;
+ recv_data >> itemguid >> dstslot;
+
+ // cheating attempt, client should never send opcode in that case
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
+ return;
+
+ Item* item = _player->GetItemByGuid(itemguid);
+ uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
+
+ if(!item || item->GetPos() == dstpos)
+ return;
+
+ _player->SwapItem(item->GetPos(), dstpos);
+}
+
+void WorldSession::HandleSwapItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_ITEM");
+ uint8 dstbag, dstslot, srcbag, srcslot;
+
+ recv_data >> dstbag >> dstslot >> srcbag >> srcslot ;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(src==dst)
+ return;
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM");
+ uint8 srcbag, srcslot;
+
+ recv_data >> srcbag >> srcslot;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pSrcItem )
+ return; // only at cheat
+
+ if(pSrcItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 dest;
+ uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 src = pSrcItem->GetPos();
+ if(dest==src) // prevent equip in same slot, only at cheat
+ return;
+
+ Item *pDstItem = _player->GetItemByPos( dest );
+ if( !pDstItem ) // empty slot, simple case
+ {
+ _player->RemoveItem( srcbag, srcslot, true );
+ _player->EquipItem( dest, pSrcItem, true );
+ _player->AutoUnequipOffhandIfNeed();
+ }
+ else // have currently equipped item, not simple case
+ {
+ uint8 dstbag = pDstItem->GetBagSlot();
+ uint8 dstslot = pDstItem->GetSlot();
+
+ msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, NULL );
+ return;
+ }
+
+ // check dest->src move possibility
+ ItemPosCountVec sSrc;
+ uint16 eSrc;
+ if( _player->IsInventoryPos( src ) )
+ {
+ msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsBankPos( src ) )
+ {
+ msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsEquipmentPos( src ) )
+ {
+ msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true);
+ if( msg == EQUIP_ERR_OK )
+ msg = _player->CanUnequipItem( eSrc, true);
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, pSrcItem );
+ return;
+ }
+
+ // now do moves, remove...
+ _player->RemoveItem(dstbag, dstslot, false);
+ _player->RemoveItem(srcbag, srcslot, false);
+
+ // add to dest
+ _player->EquipItem(dest, pSrcItem, true);
+
+ // add to src
+ if( _player->IsInventoryPos( src ) )
+ _player->StoreItem(sSrc, pDstItem, true);
+ else if( _player->IsBankPos( src ) )
+ _player->BankItem(sSrc, pDstItem, true);
+ else if( _player->IsEquipmentPos( src ) )
+ _player->EquipItem(eSrc, pDstItem, true);
+
+ _player->AutoUnequipOffhandIfNeed();
+ }
+}
+
+void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_DESTROYITEM");
+ uint8 bag, slot, count, data1, data2, data3;
+
+ recv_data >> bag >> slot >> count >> data1 >> data2 >> data3;
+ //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
+
+ uint16 pos = (bag << 8) | slot;
+
+ // prevent drop unequipable items (in combat, for example) and non-empty bags
+ if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
+ {
+ uint8 msg = _player->CanUnequipItem( pos, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL );
+ return;
+ }
+ }
+
+ Item *pItem = _player->GetItemByPos( bag, slot );
+ if(!pItem)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ if(count)
+ {
+ uint32 i_count = count;
+ _player->DestroyItemCount( pItem, i_count, true );
+ }
+ else
+ _player->DestroyItem( bag, slot, true );
+}
+
+// Only _static_ data send in this packet !!!
+void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE");
+ uint32 item;
+ recv_data >> item;
+
+ sLog.outDetail("STORAGE: Item Query = %u", item);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ std::string Name = pProto->Name1;
+ std::string Description = pProto->Description;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty())
+ Description = il->Description[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
+ data << pProto->ItemId;
+ data << pProto->Class;
+ data << pProto->SubClass;
+ data << uint32(-1); // new 2.0.3, not exist in wdb cache?
+ data << Name;
+ data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
+ data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
+ data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
+ data << pProto->DisplayInfoID;
+ data << pProto->Quality;
+ data << pProto->Flags;
+ data << pProto->BuyPrice;
+ data << pProto->SellPrice;
+ data << pProto->InventoryType;
+ data << pProto->AllowableClass;
+ data << pProto->AllowableRace;
+ data << pProto->ItemLevel;
+ data << pProto->RequiredLevel;
+ data << pProto->RequiredSkill;
+ data << pProto->RequiredSkillRank;
+ data << pProto->RequiredSpell;
+ data << pProto->RequiredHonorRank;
+ data << pProto->RequiredCityRank;
+ data << pProto->RequiredReputationFaction;
+ data << pProto->RequiredReputationRank;
+ data << pProto->MaxCount;
+ data << pProto->Stackable;
+ data << pProto->ContainerSlots;
+ for(int i = 0; i < 10; i++)
+ {
+ data << pProto->ItemStat[i].ItemStatType;
+ data << pProto->ItemStat[i].ItemStatValue;
+ }
+ for(int i = 0; i < 5; i++)
+ {
+ data << pProto->Damage[i].DamageMin;
+ data << pProto->Damage[i].DamageMax;
+ data << pProto->Damage[i].DamageType;
+ }
+ data << pProto->Armor;
+ data << pProto->HolyRes;
+ data << pProto->FireRes;
+ data << pProto->NatureRes;
+ data << pProto->FrostRes;
+ data << pProto->ShadowRes;
+ data << pProto->ArcaneRes;
+ data << pProto->Delay;
+ data << pProto->Ammo_type;
+
+ data << (float)pProto->RangedModRange;
+ for(int s = 0; s < 5; s++)
+ {
+ // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
+ // use `item_template` or if not set then only use spell cooldowns
+ SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId);
+ if(spell)
+ {
+ bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
+
+ data << pProto->Spells[s].SpellId;
+ data << pProto->Spells[s].SpellTrigger;
+ data << uint32(-abs(pProto->Spells[s].SpellCharges));
+
+ if(db_data)
+ {
+ data << uint32(pProto->Spells[s].SpellCooldown);
+ data << uint32(pProto->Spells[s].SpellCategory);
+ data << uint32(pProto->Spells[s].SpellCategoryCooldown);
+ }
+ else
+ {
+ data << uint32(spell->RecoveryTime);
+ data << uint32(spell->Category);
+ data << uint32(spell->CategoryRecoveryTime);
+ }
+ }
+ else
+ {
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(-1);
+ data << uint32(0);
+ data << uint32(-1);
+ }
+ }
+ data << pProto->Bonding;
+ data << Description;
+ data << pProto->PageText;
+ data << pProto->LanguageID;
+ data << pProto->PageMaterial;
+ data << pProto->StartQuest;
+ data << pProto->LockID;
+ data << pProto->Material;
+ data << pProto->Sheath;
+ data << pProto->RandomProperty;
+ data << pProto->RandomSuffix;
+ data << pProto->Block;
+ data << pProto->ItemSet;
+ data << pProto->MaxDurability;
+ data << pProto->Area;
+ data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
+ data << pProto->BagFamily;
+ data << pProto->TotemCategory;
+ for(int s = 0; s < 3; s++)
+ {
+ data << pProto->Socket[s].Color;
+ data << pProto->Socket[s].Content;
+ }
+ data << pProto->socketBonus;
+ data << pProto->GemProperties;
+ data << pProto->RequiredDisenchantSkill;
+ data << pProto->ArmorDamageModifier;
+ data << uint32(0); // added in 2.4.2.8209, duration (seconds)
+ SendPacket( &data );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item );
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4);
+ data << uint32(item | 0x80000000);
+ SendPacket( &data );
+ }
+}
+
+void WorldSession::HandleReadItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug( "WORLD: CMSG_READ_ITEM");
+
+ uint8 bag, slot;
+ recv_data >> bag >> slot;
+
+ //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
+ Item *pItem = _player->GetItemByPos( bag, slot );
+
+ if( pItem && pItem->GetProto()->PageText )
+ {
+ WorldPacket data;
+
+ uint8 msg = _player->CanUseItem( pItem );
+ if( msg == EQUIP_ERR_OK )
+ {
+ data.Initialize (SMSG_READ_ITEM_OK, 8);
+ sLog.outDetail("STORAGE: Item page sent");
+ }
+ else
+ {
+ data.Initialize( SMSG_READ_ITEM_FAILED, 8 );
+ sLog.outDetail("STORAGE: Unable to read item");
+ _player->SendEquipError( msg, pItem, NULL );
+ }
+ data << pItem->GetGUID();
+ SendPacket(&data);
+ }
+ else
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+}
+
+void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
+
+ uint32 itemid;
+ uint64 guid;
+
+ recv_data >> itemid >> guid;
+
+ sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u",
+ itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid));
+}
+
+void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
+ uint64 vendorguid, itemguid;
+ uint8 _count;
+
+ recv_data >> vendorguid >> itemguid >> _count;
+
+ // prevent possible overflow, as mangos uses uint32 for item count
+ uint32 count = _count;
+
+ if(!itemguid)
+ return;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemByGuid( itemguid );
+ if( pItem )
+ {
+ // prevent sell not owner item
+ if(_player->GetGUID()!=pItem->GetOwnerGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell non empty bag by drag-and-drop at vendor's item list
+ if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell currently looted item
+ if(_player->GetLootGUID()==pItem->GetGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // special case at auto sell (sell all)
+ if(count==0)
+ {
+ count = pItem->GetCount();
+ }
+ else
+ {
+ // prevent sell more items that exist in stack (possable only not from client)
+ if(count > pItem->GetCount())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if( pProto->SellPrice > 0 )
+ {
+ if(count < pItem->GetCount()) // need split items
+ {
+ Item *pNewItem = pItem->CloneItem( count, _player );
+ if (!pNewItem)
+ {
+ sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count );
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ pItem->SetCount( pItem->GetCount() - count );
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), count );
+ if( _player->IsInWorld() )
+ pItem->SendUpdateToPlayer( _player );
+ pItem->SetState(ITEM_CHANGED, _player);
+
+ _player->AddItemToBuyBackSlot( pNewItem );
+ if( _player->IsInWorld() )
+ pNewItem->SendUpdateToPlayer( _player );
+ }
+ else
+ {
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true);
+ pItem->RemoveFromUpdateQueueOf(_player);
+ _player->AddItemToBuyBackSlot( pItem );
+ }
+
+ _player->ModifyMoney( pProto->SellPrice * count );
+ }
+ else
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+ _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0);
+ return;
+}
+
+void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
+ uint64 vendorguid;
+ uint32 slot;
+
+ recv_data >> vendorguid >> slot;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemFromBuyBackSlot( slot );
+ if( pItem )
+ {
+ uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START );
+ if( _player->GetMoney() < price )
+ {
+ _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ _player->ModifyMoney( -(int32)price );
+ _player->RemoveItemFromBuyBackSlot( slot, false );
+ _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->StoreItem( dest, pItem, true );
+ }
+ else
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ else
+ _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
+}
+
+void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+8+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
+ uint64 vendorguid, bagguid;
+ uint32 item;
+ uint8 slot, count;
+
+ recv_data >> vendorguid >> item >> bagguid >> slot >> count;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot);
+}
+
+void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
+ uint64 vendorguid;
+ uint32 item;
+ uint8 count, unk1;
+
+ recv_data >> vendorguid >> item >> count >> unk1;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT);
+}
+
+void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" );
+
+ SendListInventory( guid );
+}
+
+void WorldSession::SendListInventory( uint64 vendorguid )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" );
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // Stop the npc if moving
+ pCreature->StopMoving();
+ // load vendor items if not yet
+ pCreature->LoadGoods();
+
+ uint8 numitems = pCreature->GetItemCount();
+ uint8 count = 0;
+ uint32 ptime = time(NULL);
+ uint32 diff;
+
+ WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
+ data << uint64(vendorguid);
+ data << uint8(numitems);
+
+ float discountMod = _player->GetReputationPriceDiscount(pCreature);
+
+ ItemPrototype const *pProto;
+ for(int i = 0; i < numitems; i++ )
+ {
+ CreatureItem* crItem = pCreature->GetItem(i);
+ if( crItem )
+ {
+ pProto = objmgr.GetItemPrototype(crItem->id);
+ if( pProto )
+ {
+ if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
+ continue;
+ ++count;
+ if( crItem->incrtime != 0 && (crItem->lastincr + crItem->incrtime <= ptime) )
+ {
+ diff = uint32((ptime - crItem->lastincr)/crItem->incrtime);
+ if( (crItem->count + diff * pProto->BuyCount) <= crItem->maxcount )
+ crItem->count += diff * pProto->BuyCount;
+ else
+ crItem->count = crItem->maxcount;
+ crItem->lastincr = ptime;
+ }
+ data << uint32(count);
+ data << uint32(crItem->id);
+ data << uint32(pProto->DisplayInfoID);
+ data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : crItem->count);
+
+ uint32 price = pProto->BuyPrice;
+
+ // reputation discount
+ price = uint32(floor(pProto->BuyPrice * discountMod));
+
+ data << uint32(price);
+ data << uint32(pProto->MaxDurability);
+ data << uint32(pProto->BuyCount);
+ data << uint32(crItem->ExtendedCost);
+ }
+ }
+ }
+
+ if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 )
+ return;
+
+ data.put<uint8>(8, count);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
+ uint8 srcbag, srcslot, dstbag;
+
+ recv_data >> srcbag >> srcslot >> dstbag;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ uint16 src = pItem->GetPos();
+
+ // check unequip potability for equipped items and bank bags
+ if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
+ {
+ uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src ));
+ if(msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ // no-op: placed in same slot
+ if(dest.size()==1 && dest[0].pos==src)
+ {
+ // just remove grey item state
+ _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true );
+ _player->StoreItem( dest, pItem, true );
+}
+
+void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT");
+
+ uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2);
+
+ // next slot
+ ++slot;
+
+ sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot);
+
+ BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
+
+ if(!slotEntry)
+ return;
+
+ uint32 price = slotEntry->price;
+
+ if (_player->GetMoney() < price)
+ return;
+
+ _player->SetByteValue(PLAYER_BYTES_2, 2, slot);
+ _player->ModifyMoney(-int32(price));
+}
+
+void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+}
+
+void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->StoreItem( dest, pItem, true );
+ }
+ else // moving from inventory to bank
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+ }
+}
+
+void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ if(!GetPlayer()->isAlive())
+ {
+ GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL );
+ return;
+ }
+
+ sLog.outDebug("WORLD: CMSG_SET_AMMO");
+ uint32 item;
+
+ recv_data >> item;
+
+ if(!item)
+ GetPlayer()->RemoveAmmo();
+ else
+ GetPlayer()->SetAmmo(item);
+}
+
+void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID)
+{
+ WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10
+ data << Target;
+ data << Caster;
+ data << ItemID;
+ data << SpellID;
+ data << uint8(0);
+ SendPacket(&data);
+}
+
+void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration)
+{
+ // last check 2.0.10
+ WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
+ data << uint64(Itemguid);
+ data << uint32(slot);
+ data << uint32(Duration);
+ data << uint64(Playerguid);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 itemid;
+ recv_data >> itemid;
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid);
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid );
+ if( pProto )
+ {
+ std::string Name;
+ Name = pProto->Name1;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10));
+ data << uint32(pProto->ItemId);
+ data << Name;
+ data << uint32(pProto->InventoryType);
+ SendPacket(&data);
+ return;
+ }
+ else
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid);
+}
+
+void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ sLog.outDebug("Received opcode CMSG_WRAP_ITEM");
+
+ uint8 gift_bag, gift_slot, item_bag, item_slot;
+ //recv_data.hexlike();
+
+ recv_data >> gift_bag >> gift_slot; // paper
+ recv_data >> item_bag >> item_slot; // item
+
+ sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
+
+ Item *gift = _player->GetItemByPos( gift_bag, gift_slot );
+ if(!gift)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ Item *item = _player->GetItemByPos( item_bag, item_slot );
+
+ if( !item )
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL );
+ return;
+ }
+
+ if(item==gift) // not possable with pacjket from real client
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsEquipped())
+ {
+ _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsBag())
+ {
+ _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsSoulBound())
+ {
+ _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetMaxStackCount() != 1)
+ {
+ _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ // maybe not correct check (it is better than nothing)
+ if(item->GetProto()->MaxCount>0)
+ {
+ _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS));
+ item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY));
+
+ switch (item->GetEntry())
+ {
+ case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break;
+ case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break;
+ case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break;
+ case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break;
+ case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break;
+ case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break;
+ }
+ item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
+ item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ item->SetState(ITEM_CHANGED, _player);
+
+ if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
+ {
+ // after save it will be impossible to remove the item from the queue
+ item->RemoveFromUpdateQueueOf(_player);
+ item->SaveToDB(); // item gave inventory record unchanged and can be save standalone
+ }
+ CharacterDatabase.CommitTransaction();
+
+ uint32 count = 1;
+ _player->DestroyItemCount(gift, count, true);
+}
+
+void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_SOCKET_GEMS");
+
+ CHECK_PACKET_SIZE(recv_data,8*4);
+
+ uint64 guids[4];
+ uint32 GemEnchants[3], OldEnchants[3];
+ Item *Gems[3];
+ bool SocketBonusActivated, SocketBonusToBeActivated;
+
+ for(int i = 0; i < 4; i++)
+ recv_data >> guids[i];
+
+ if(!guids[0])
+ return;
+
+ //cheat -> tried to socket same gem multiple times
+ if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3])))
+ return;
+
+ Item *itemTarget = _player->GetItemByGuid(guids[0]);
+ if(!itemTarget) //missing item to socket
+ return;
+
+ //this slot is excepted when applying / removing meta gem bonus
+ uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT;
+
+ for(int i = 0; i < 3; i++)
+ Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL;
+
+ GemPropertiesEntry const *GemProps[3];
+ for(int i = 0; i < 3; ++i) //get geminfo from dbc storage
+ {
+ GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
+ }
+
+ for(int i = 0; i < 3; ++i) //check for hack maybe
+ {
+ // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket
+ // tried to put meta gem in normal socket
+ if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color ||
+ itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META ||
+ itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) )
+ return;
+ }
+
+ for(int i = 0; i < 3; ++i) //get new and old enchantments
+ {
+ GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
+ OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
+ }
+
+ // check unique-equipped conditions
+ for(int i = 0; i < 3; ++i)
+ {
+ if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
+ {
+ // for equipped item check all equipment for duplicate equipped gems
+ if(itemTarget->IsEquipped())
+ {
+ if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry()))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL );
+ return;
+ }
+ }
+
+ // continue check for case when attempt add 2 similar unique equipped gems in one item.
+ for (int j = 0; j < 3; ++j)
+ {
+ if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ for (int j = 0; j < 3; ++j)
+ {
+ if (OldEnchants[j])
+ {
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]);
+ if(!enchantEntry)
+ continue;
+
+ if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
+ _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
+
+ //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
+
+ //remove ALL enchants
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false);
+
+ for(int i = 0; i < 3; ++i)
+ {
+ if(GemEnchants[i])
+ {
+ itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0);
+ if(Item* guidItem = _player->GetItemByGuid(guids[i + 1]))
+ _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
+ }
+ }
+
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true);
+
+ SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
+ if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
+ {
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false);
+ itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0);
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true);
+ //it is not displayed, client has an inbuilt system to determine if the bonus is activated
+ }
+
+ _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
+}
+
+void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
+
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 eslot;
+
+ recv_data >> eslot;
+
+ // apply only to equipped item
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot))
+ return;
+
+ Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
+
+ if(!item)
+ return;
+
+ if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+ return;
+
+ GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
+}
diff --git a/src/game/ItemPrototype.h b/src/game/ItemPrototype.h
new file mode 100644
index 00000000000..2325d229859
--- /dev/null
+++ b/src/game/ItemPrototype.h
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _ITEMPROTOTYPE_H
+#define _ITEMPROTOTYPE_H
+
+#include "Common.h"
+
+enum ItemModType
+{
+ ITEM_MOD_MANA = 0,
+ ITEM_MOD_HEALTH = 1,
+ ITEM_MOD_AGILITY = 3,
+ ITEM_MOD_STRENGTH = 4,
+ ITEM_MOD_INTELLECT = 5,
+ ITEM_MOD_SPIRIT = 6,
+ ITEM_MOD_STAMINA = 7,
+ ITEM_MOD_DEFENSE_SKILL_RATING = 12,
+ ITEM_MOD_DODGE_RATING = 13,
+ ITEM_MOD_PARRY_RATING = 14,
+ ITEM_MOD_BLOCK_RATING = 15,
+ ITEM_MOD_HIT_MELEE_RATING = 16,
+ ITEM_MOD_HIT_RANGED_RATING = 17,
+ ITEM_MOD_HIT_SPELL_RATING = 18,
+ ITEM_MOD_CRIT_MELEE_RATING = 19,
+ ITEM_MOD_CRIT_RANGED_RATING = 20,
+ ITEM_MOD_CRIT_SPELL_RATING = 21,
+ ITEM_MOD_HIT_TAKEN_MELEE_RATING = 22,
+ ITEM_MOD_HIT_TAKEN_RANGED_RATING = 23,
+ ITEM_MOD_HIT_TAKEN_SPELL_RATING = 24,
+ ITEM_MOD_CRIT_TAKEN_MELEE_RATING = 25,
+ ITEM_MOD_CRIT_TAKEN_RANGED_RATING = 26,
+ ITEM_MOD_CRIT_TAKEN_SPELL_RATING = 27,
+ ITEM_MOD_HASTE_MELEE_RATING = 28,
+ ITEM_MOD_HASTE_RANGED_RATING = 29,
+ ITEM_MOD_HASTE_SPELL_RATING = 30,
+ ITEM_MOD_HIT_RATING = 31,
+ ITEM_MOD_CRIT_RATING = 32,
+ ITEM_MOD_HIT_TAKEN_RATING = 33,
+ ITEM_MOD_CRIT_TAKEN_RATING = 34,
+ ITEM_MOD_RESILIENCE_RATING = 35,
+ ITEM_MOD_HASTE_RATING = 36,
+ ITEM_MOD_EXPERTISE_RATING = 37
+};
+
+#define MAX_ITEM_MOD 38
+
+enum ItemSpelltriggerType
+{
+ ITEM_SPELLTRIGGER_ON_USE = 0, // use after equip cooldown
+ ITEM_SPELLTRIGGER_ON_EQUIP = 1,
+ ITEM_SPELLTRIGGER_CHANCE_ON_HIT = 2,
+ ITEM_SPELLTRIGGER_SOULSTONE = 4,
+ ITEM_SPELLTRIGGER_ON_NO_DELAY_USE = 5, // no equip cooldown
+ ITEM_SPELLTRIGGER_LEARN_SPELL_ID = 6 // used in item_template.spell_2 with spell_id with SPELL_GENERIC_LEARN in spell_1
+};
+
+#define MAX_ITEM_SPELLTRIGGER 7
+
+enum ItemBondingType
+{
+ NO_BIND = 0,
+ BIND_WHEN_PICKED_UP = 1,
+ BIND_WHEN_EQUIPED = 2,
+ BIND_WHEN_USE = 3,
+ BIND_QUEST_ITEM = 4,
+ BIND_QUEST_ITEM1 = 5 // not used in game
+};
+
+#define MAX_BIND_TYPE 6
+
+// masks for ITEM_FIELD_FLAGS field
+enum ITEM_FLAGS
+{
+ ITEM_FLAGS_BINDED = 0x00000001,
+ ITEM_FLAGS_CONJURED = 0x00000002,
+ ITEM_FLAGS_OPENABLE = 0x00000004,
+ ITEM_FLAGS_WRAPPED = 0x00000008,
+ ITEM_FLAGS_WRAPPER = 0x00000200, // used or not used wrapper
+ ITEM_FLAGS_PARTY_LOOT = 0x00000800, // determines if item is party loot or not
+ ITEM_FLAGS_CHARTER = 0x00002000, // arena/guild charter
+ ITEM_FLAGS_UNIQUE_EQUIPPED = 0x00080000,
+ ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000,
+ ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip
+ ITEM_FLAGS_SPECIALUSE = 0x00800000 // last used flag in 2.3.0
+};
+
+enum BAG_FAMILY_MASK
+{
+ BAG_FAMILY_MASK_ARROWS = 0x00000001,
+ BAG_FAMILY_MASK_BULLETS = 0x00000002,
+ BAG_FAMILY_MASK_SHARDS = 0x00000004,
+ BAG_FAMILY_MASK_LEATHERWORKING_SUPP = 0x00000008,
+ BAG_FAMILY_MASK_UNUSED = 0x00000010, // not used currently
+ BAG_FAMILY_MASK_HERBS = 0x00000020,
+ BAG_FAMILY_MASK_ENCHANTING_SUPP = 0x00000040,
+ BAG_FAMILY_MASK_ENGINEERING_SUPP = 0x00000080,
+ BAG_FAMILY_MASK_KEYS = 0x00000100,
+ BAG_FAMILY_MASK_GEMS = 0x00000200,
+ BAG_FAMILY_MASK_MINING_SUPP = 0x00000400,
+ BAG_FAMILY_MASK_SOULBOUND_EQUIPMENT = 0x00000800,
+ BAG_FAMILY_MASK_VANITY_PETS = 0x00001000
+};
+
+/* TODO: Not entirely positive on need for this??
+enum SOCKET_CONTENT ();
+*/
+
+enum SocketColor
+{
+ SOCKET_COLOR_META = 1,
+ SOCKET_COLOR_RED = 2,
+ SOCKET_COLOR_YELLOW = 4,
+ SOCKET_COLOR_BLUE = 8
+};
+
+#define SOCKET_COLOR_ALL (SOCKET_COLOR_META | SOCKET_COLOR_RED | SOCKET_COLOR_YELLOW | SOCKET_COLOR_BLUE)
+
+enum InventoryType
+{
+ INVTYPE_NON_EQUIP = 0,
+ INVTYPE_HEAD = 1,
+ INVTYPE_NECK = 2,
+ INVTYPE_SHOULDERS = 3,
+ INVTYPE_BODY = 4,
+ INVTYPE_CHEST = 5,
+ INVTYPE_WAIST = 6,
+ INVTYPE_LEGS = 7,
+ INVTYPE_FEET = 8,
+ INVTYPE_WRISTS = 9,
+ INVTYPE_HANDS = 10,
+ INVTYPE_FINGER = 11,
+ INVTYPE_TRINKET = 12,
+ INVTYPE_WEAPON = 13,
+ INVTYPE_SHIELD = 14,
+ INVTYPE_RANGED = 15,
+ INVTYPE_CLOAK = 16,
+ INVTYPE_2HWEAPON = 17,
+ INVTYPE_BAG = 18,
+ INVTYPE_TABARD = 19,
+ INVTYPE_ROBE = 20,
+ INVTYPE_WEAPONMAINHAND = 21,
+ INVTYPE_WEAPONOFFHAND = 22,
+ INVTYPE_HOLDABLE = 23,
+ INVTYPE_AMMO = 24,
+ INVTYPE_THROWN = 25,
+ INVTYPE_RANGEDRIGHT = 26,
+ INVTYPE_QUIVER = 27,
+ INVTYPE_RELIC = 28
+};
+
+#define MAX_INVTYPE 29
+
+enum ItemClass
+{
+ ITEM_CLASS_CONSUMABLE = 0,
+ ITEM_CLASS_CONTAINER = 1,
+ ITEM_CLASS_WEAPON = 2,
+ ITEM_CLASS_GEM = 3,
+ ITEM_CLASS_ARMOR = 4,
+ ITEM_CLASS_REAGENT = 5,
+ ITEM_CLASS_PROJECTILE = 6,
+ ITEM_CLASS_TRADE_GOODS = 7,
+ ITEM_CLASS_GENERIC = 8,
+ ITEM_CLASS_RECIPE = 9,
+ ITEM_CLASS_MONEY = 10,
+ ITEM_CLASS_QUIVER = 11,
+ ITEM_CLASS_QUEST = 12,
+ ITEM_CLASS_KEY = 13,
+ ITEM_CLASS_PERMANENT = 14,
+ ITEM_CLASS_JUNK = 15
+};
+
+#define MAX_ITEM_CLASS 16
+
+enum ItemSubclassConsumable
+{
+ ITEM_SUBCLASS_CONSUMABLE = 0,
+ ITEM_SUBCLASS_POTION = 1,
+ ITEM_SUBCLASS_ELIXIR = 2,
+ ITEM_SUBCLASS_FLASK = 3,
+ ITEM_SUBCLASS_SCROLL = 4,
+ ITEM_SUBCLASS_FOOD = 5,
+ ITEM_SUBCLASS_ITEM_ENHANCEMENT = 6,
+ ITEM_SUBCLASS_BANDAGE = 7,
+ ITEM_SUBCLASS_CONSUMABLE_OTHER = 8
+};
+
+#define MAX_ITEM_SUBCLASS_CONSUMABLE 9
+
+enum ItemSubclassContainer
+{
+ ITEM_SUBCLASS_CONTAINER = 0,
+ ITEM_SUBCLASS_SOUL_CONTAINER = 1,
+ ITEM_SUBCLASS_HERB_CONTAINER = 2,
+ ITEM_SUBCLASS_ENCHANTING_CONTAINER = 3,
+ ITEM_SUBCLASS_ENGINEERING_CONTAINER = 4,
+ ITEM_SUBCLASS_GEM_CONTAINER = 5,
+ ITEM_SUBCLASS_MINING_CONTAINER = 6,
+ ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7
+};
+
+#define MAX_ITEM_SUBCLASS_CONTAINER 8
+
+enum ItemSubclassWeapon
+{
+ ITEM_SUBCLASS_WEAPON_AXE = 0,
+ ITEM_SUBCLASS_WEAPON_AXE2 = 1,
+ ITEM_SUBCLASS_WEAPON_BOW = 2,
+ ITEM_SUBCLASS_WEAPON_GUN = 3,
+ ITEM_SUBCLASS_WEAPON_MACE = 4,
+ ITEM_SUBCLASS_WEAPON_MACE2 = 5,
+ ITEM_SUBCLASS_WEAPON_POLEARM = 6,
+ ITEM_SUBCLASS_WEAPON_SWORD = 7,
+ ITEM_SUBCLASS_WEAPON_SWORD2 = 8,
+ ITEM_SUBCLASS_WEAPON_obsolete = 9,
+ ITEM_SUBCLASS_WEAPON_STAFF = 10,
+ ITEM_SUBCLASS_WEAPON_EXOTIC = 11,
+ ITEM_SUBCLASS_WEAPON_EXOTIC2 = 12,
+ ITEM_SUBCLASS_WEAPON_FIST = 13,
+ ITEM_SUBCLASS_WEAPON_MISC = 14,
+ ITEM_SUBCLASS_WEAPON_DAGGER = 15,
+ ITEM_SUBCLASS_WEAPON_THROWN = 16,
+ ITEM_SUBCLASS_WEAPON_SPEAR = 17,
+ ITEM_SUBCLASS_WEAPON_CROSSBOW = 18,
+ ITEM_SUBCLASS_WEAPON_WAND = 19,
+ ITEM_SUBCLASS_WEAPON_FISHING_POLE = 20
+};
+
+#define MAX_ITEM_SUBCLASS_WEAPON 21
+
+enum ItemSubclassGem
+{
+ ITEM_SUBCLASS_GEM_RED = 0,
+ ITEM_SUBCLASS_GEM_BLUE = 1,
+ ITEM_SUBCLASS_GEM_YELLOW = 2,
+ ITEM_SUBCLASS_GEM_PURPLE = 3,
+ ITEM_SUBCLASS_GEM_GREEN = 4,
+ ITEM_SUBCLASS_GEM_ORANGE = 5,
+ ITEM_SUBCLASS_GEM_META = 6,
+ ITEM_SUBCLASS_GEM_SIMPLE = 7,
+ ITEM_SUBCLASS_GEM_PRISMATIC = 8
+};
+
+#define MAX_ITEM_SUBCLASS_GEM 9
+
+enum ItemSubclassArmor
+{
+ ITEM_SUBCLASS_ARMOR_MISC = 0,
+ ITEM_SUBCLASS_ARMOR_CLOTH = 1,
+ ITEM_SUBCLASS_ARMOR_LEATHER = 2,
+ ITEM_SUBCLASS_ARMOR_MAIL = 3,
+ ITEM_SUBCLASS_ARMOR_PLATE = 4,
+ ITEM_SUBCLASS_ARMOR_BUCKLER = 5,
+ ITEM_SUBCLASS_ARMOR_SHIELD = 6,
+ ITEM_SUBCLASS_ARMOR_LIBRAM = 7,
+ ITEM_SUBCLASS_ARMOR_IDOL = 8,
+ ITEM_SUBCLASS_ARMOR_TOTEM = 9
+};
+
+#define MAX_ITEM_SUBCLASS_ARMOR 10
+
+enum ItemSubclassReagent
+{
+ ITEM_SUBCLASS_REAGENT = 0
+};
+
+#define MAX_ITEM_SUBCLASS_REAGENT 1
+
+enum ItemSubclassProjectile
+{
+ ITEM_SUBCLASS_WAND = 0, // ABS
+ ITEM_SUBCLASS_BOLT = 1, // ABS
+ ITEM_SUBCLASS_ARROW = 2,
+ ITEM_SUBCLASS_BULLET = 3,
+ ITEM_SUBCLASS_THROWN = 4 // ABS
+};
+
+#define MAX_ITEM_SUBCLASS_PROJECTILE 5
+
+enum ItemSubclassTradeGoods
+{
+ ITEM_SUBCLASS_TRADE_GOODS = 0,
+ ITEM_SUBCLASS_PARTS = 1,
+ ITEM_SUBCLASS_EXPLOSIVES = 2,
+ ITEM_SUBCLASS_DEVICES = 3,
+ ITEM_SUBCLASS_JEWELCRAFTING = 4,
+ ITEM_SUBCLASS_CLOTH = 5,
+ ITEM_SUBCLASS_LEATHER = 6,
+ ITEM_SUBCLASS_METAL_STONE = 7,
+ ITEM_SUBCLASS_MEAT = 8,
+ ITEM_SUBCLASS_HERB = 9,
+ ITEM_SUBCLASS_ELEMENTAL = 10,
+ ITEM_SUBCLASS_TRADE_GOODS_OTHER = 11,
+ ITEM_SUBCLASS_ENCHANTING = 12,
+ ITEM_SUBCLASS_MATERIAL = 13 // Added in 2.4.2
+};
+
+#define MAX_ITEM_SUBCLASS_TRADE_GOODS 14
+
+enum ItemSubclassGeneric
+{
+ ITEM_SUBCLASS_GENERIC = 0
+};
+
+#define MAX_ITEM_SUBCLASS_GENERIC 1
+
+enum ItemSubclassRecipe
+{
+ ITEM_SUBCLASS_BOOK = 0,
+ ITEM_SUBCLASS_LEATHERWORKING_PATTERN = 1,
+ ITEM_SUBCLASS_TAILORING_PATTERN = 2,
+ ITEM_SUBCLASS_ENGINEERING_SCHEMATIC = 3,
+ ITEM_SUBCLASS_BLACKSMITHING = 4,
+ ITEM_SUBCLASS_COOKING_RECIPE = 5,
+ ITEM_SUBCLASS_ALCHEMY_RECIPE = 6,
+ ITEM_SUBCLASS_FIRST_AID_MANUAL = 7,
+ ITEM_SUBCLASS_ENCHANTING_FORMULA = 8,
+ ITEM_SUBCLASS_FISHING_MANUAL = 9,
+ ITEM_SUBCLASS_JEWELCRAFTING_RECIPE = 10
+};
+
+#define MAX_ITEM_SUBCLASS_RECIPE 11
+
+enum ItemSubclassMoney
+{
+ ITEM_SUBCLASS_MONEY = 0
+};
+
+#define MAX_ITEM_SUBCLASS_MONEY 1
+
+enum ItemSubclassQuiver
+{
+ ITEM_SUBCLASS_QUIVER0 = 0, // ABS
+ ITEM_SUBCLASS_QUIVER1 = 1, // ABS
+ ITEM_SUBCLASS_QUIVER = 2,
+ ITEM_SUBCLASS_AMMO_POUCH = 3
+};
+
+#define MAX_ITEM_SUBCLASS_QUIVER 4
+
+enum ItemSubclassQuest
+{
+ ITEM_SUBCLASS_QUEST = 0
+};
+
+#define MAX_ITEM_SUBCLASS_QUEST 1
+
+enum ItemSubclassKey
+{
+ ITEM_SUBCLASS_KEY = 0,
+ ITEM_SUBCLASS_LOCKPICK = 1
+};
+
+#define MAX_ITEM_SUBCLASS_KEY 2
+
+enum ItemSubclassPermanent
+{
+ ITEM_SUBCLASS_PERMANENT = 0
+};
+
+#define MAX_ITEM_SUBCLASS_PERMANENT 1
+
+enum ItemSubclassJunk
+{
+ ITEM_SUBCLASS_JUNK = 0,
+ ITEM_SUBCLASS_JUNK_REAGENT = 1,
+ ITEM_SUBCLASS_JUNK_PET = 2,
+ ITEM_SUBCLASS_JUNK_HOLIDAY = 3,
+ ITEM_SUBCLASS_JUNK_OTHER = 4,
+ ITEM_SUBCLASS_JUNK_MOUNT = 5
+};
+
+#define MAX_ITEM_SUBCLASS_JUNK 6
+
+const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] =
+{
+ MAX_ITEM_SUBCLASS_CONSUMABLE,
+ MAX_ITEM_SUBCLASS_CONTAINER,
+ MAX_ITEM_SUBCLASS_WEAPON,
+ MAX_ITEM_SUBCLASS_GEM,
+ MAX_ITEM_SUBCLASS_ARMOR,
+ MAX_ITEM_SUBCLASS_REAGENT,
+ MAX_ITEM_SUBCLASS_PROJECTILE,
+ MAX_ITEM_SUBCLASS_TRADE_GOODS,
+ MAX_ITEM_SUBCLASS_GENERIC,
+ MAX_ITEM_SUBCLASS_RECIPE,
+ MAX_ITEM_SUBCLASS_MONEY,
+ MAX_ITEM_SUBCLASS_QUIVER,
+ MAX_ITEM_SUBCLASS_QUEST,
+ MAX_ITEM_SUBCLASS_KEY,
+ MAX_ITEM_SUBCLASS_PERMANENT,
+ MAX_ITEM_SUBCLASS_JUNK
+};
+
+inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemSubClass)
+{
+ switch(ItemClass)
+ {
+ case ITEM_CLASS_WEAPON: return ItemSubClass;
+ case ITEM_CLASS_ARMOR: return ItemSubClass + 21;
+ }
+ return 0;
+}
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+struct _Damage
+{
+ float DamageMin;
+ float DamageMax;
+ uint32 DamageType; // id from Resistances.dbc
+
+};
+
+struct _ItemStat
+{
+ uint32 ItemStatType;
+ int32 ItemStatValue;
+
+};
+struct _Spell
+{
+ uint32 SpellId; // id from Spell.dbc
+ uint32 SpellTrigger;
+ int32 SpellCharges;
+ float SpellPPMRate;
+ int32 SpellCooldown;
+ uint32 SpellCategory; // id from SpellCategory.dbc
+ int32 SpellCategoryCooldown;
+
+};
+
+struct _Socket
+{
+ uint32 Color;
+ uint32 Content;
+};
+
+struct ItemPrototype
+{
+ uint32 ItemId;
+ uint32 Class; // id from ItemClass.dbc
+ uint32 SubClass; // id from ItemSubClass.dbc
+ uint32 Unk0;
+ char* Name1;
+ uint32 DisplayInfoID; // id from ItemDisplayInfo.dbc
+ uint32 Quality;
+ uint32 Flags;
+ uint32 BuyCount;
+ uint32 BuyPrice;
+ uint32 SellPrice;
+ uint32 InventoryType;
+ uint32 AllowableClass;
+ uint32 AllowableRace;
+ uint32 ItemLevel;
+ uint32 RequiredLevel;
+ uint32 RequiredSkill; // id from SkillLine.dbc
+ uint32 RequiredSkillRank;
+ uint32 RequiredSpell; // id from Spell.dbc
+ uint32 RequiredHonorRank;
+ uint32 RequiredCityRank;
+ uint32 RequiredReputationFaction; // id from Faction.dbc
+ uint32 RequiredReputationRank;
+ uint32 MaxCount;
+ uint32 Stackable;
+ uint32 ContainerSlots;
+ _ItemStat ItemStat[10];
+ _Damage Damage[5];
+ uint32 Armor;
+ uint32 HolyRes;
+ uint32 FireRes;
+ uint32 NatureRes;
+ uint32 FrostRes;
+ uint32 ShadowRes;
+ uint32 ArcaneRes;
+ uint32 Delay;
+ uint32 Ammo_type;
+ float RangedModRange;
+
+ _Spell Spells[5];
+ uint32 Bonding;
+ char* Description;
+ uint32 PageText;
+ uint32 LanguageID;
+ uint32 PageMaterial;
+ uint32 StartQuest; // id from QuestCache.wdb
+ uint32 LockID;
+ uint32 Material; // id from Material.dbc
+ uint32 Sheath;
+ uint32 RandomProperty; // id from ItemRandomProperties.dbc
+ uint32 RandomSuffix; // id from ItemRandomSuffix.dbc
+ uint32 Block;
+ uint32 ItemSet; // id from ItemSet.dbc
+ uint32 MaxDurability;
+ uint32 Area; // id from AreaTable.dbc
+ uint32 Map; // id from Map.dbc
+ uint32 BagFamily; // id from ItemBagFamily.dbc
+ uint32 TotemCategory; // id from TotemCategory.dbc
+ _Socket Socket[3];
+ uint32 socketBonus; // id from SpellItemEnchantment.dbc
+ uint32 GemProperties; // id from GemProperties.dbc
+ uint32 RequiredDisenchantSkill;
+ float ArmorDamageModifier;
+ char* ScriptName;
+ uint32 DisenchantID;
+ uint32 FoodType;
+ uint32 MinMoneyLoot;
+ uint32 MaxMoneyLoot;
+ int32 Duration; // negative = realtime, positive = ingame time
+
+ // helpers
+ bool CanChangeEquipStateInCombat() const
+ {
+ switch(InventoryType)
+ {
+ case INVTYPE_RELIC:
+ case INVTYPE_SHIELD:
+ return true;
+ }
+
+ switch(Class)
+ {
+ case ITEM_CLASS_WEAPON:
+ case ITEM_CLASS_PROJECTILE:
+ return true;
+ }
+
+ return false;
+ }
+};
+
+struct ItemLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> Description;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+#endif
diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp
new file mode 100644
index 00000000000..df87b51de89
--- /dev/null
+++ b/src/game/LFGHandler.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "WorldSession.h"
+#include "Log.h"
+#include "Database/DatabaseEnv.h"
+#include "Player.h"
+#include "WorldPacket.h"
+#include "ObjectMgr.h"
+#include "World.h"
+
+static void AttemptJoin(Player* _player)
+{
+ // skip not can autojoin cases and player group case
+ if(!_player->m_lookingForGroup.canAutoJoin() || _player->GetGroup())
+ return;
+
+ //TODO: Guard Player Map
+ HashMapHolder<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter)
+ {
+ Player *plr = iter->second;
+
+ // skip enemies and self
+ if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam())
+ continue;
+
+ // skip not auto add, not group leader cases
+ if(!plr->GetSession()->LookingForGroup_auto_add || plr->GetGroup() && plr->GetGroup()->GetLeaderGUID()!=plr->GetGUID())
+ continue;
+
+ // skip non auto-join or empty slots, or non compatible slots
+ if(!plr->m_lookingForGroup.more.canAutoJoin() || !_player->m_lookingForGroup.HaveInSlot(plr->m_lookingForGroup.more))
+ continue;
+
+ // attempt create group, or skip
+ if(!plr->GetGroup())
+ {
+ Group* group = new Group;
+ if(!group->Create(plr->GetGUID(), plr->GetName()))
+ {
+ delete group;
+ continue;
+ }
+
+ objmgr.AddGroup(group);
+ }
+
+ // stop at success join
+ if(plr->GetGroup()->AddMember(_player->GetGUID(), _player->GetName()))
+ {
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER )
+ _player->LeaveLFGChannel();
+ break;
+ }
+ // full
+ else
+ {
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER )
+ plr->LeaveLFGChannel();
+ }
+ }
+}
+
+static void AttemptAddMore(Player* _player)
+{
+ // skip not group leader case
+ if(_player->GetGroup() && _player->GetGroup()->GetLeaderGUID()!=_player->GetGUID())
+ return;
+
+ if(!_player->m_lookingForGroup.more.canAutoJoin())
+ return;
+
+ //TODO: Guard Player map
+ HashMapHolder<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter)
+ {
+ Player *plr = iter->second;
+
+ // skip enemies and self
+ if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam())
+ continue;
+
+ // skip not auto join or in group
+ if(!plr->GetSession()->LookingForGroup_auto_join || plr->GetGroup() )
+ continue;
+
+ if(!plr->m_lookingForGroup.HaveInSlot(_player->m_lookingForGroup.more))
+ continue;
+
+ // attempt create group if need, or stop attempts
+ if(!_player->GetGroup())
+ {
+ Group* group = new Group;
+ if(!group->Create(_player->GetGUID(), _player->GetName()))
+ {
+ delete group;
+ return; // cann't create group (??)
+ }
+
+ objmgr.AddGroup(group);
+ }
+
+ // stop at join fail (full)
+ if(!_player->GetGroup()->AddMember(plr->GetGUID(), plr->GetName()) )
+ {
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER )
+ _player->LeaveLFGChannel();
+
+ break;
+ }
+
+ // joined
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER )
+ plr->LeaveLFGChannel();
+
+ // and group full
+ if(_player->GetGroup()->IsFull() )
+ {
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER )
+ _player->LeaveLFGChannel();
+
+ break;
+ }
+ }
+}
+
+void WorldSession::HandleLfgAutoJoinOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("CMSG_SET_LFG_AUTO_JOIN");
+ LookingForGroup_auto_join = true;
+
+ if(!_player) // needed because STATUS_AUTHED
+ return;
+
+ AttemptJoin(_player);
+}
+
+void WorldSession::HandleLfgCancelAutoJoinOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("CMSG_UNSET_LFG_AUTO_JOIN");
+ LookingForGroup_auto_join = false;
+}
+
+void WorldSession::HandleLfmAutoAddMembersOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("CMSG_SET_LFM_AUTOADD");
+ LookingForGroup_auto_add = true;
+
+ if(!_player) // needed because STATUS_AUTHED
+ return;
+
+ AttemptAddMore(_player);
+}
+
+void WorldSession::HandleLfmCancelAutoAddmembersOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("CMSG_UNSET_LFM_AUTOADD");
+ LookingForGroup_auto_add = false;
+}
+
+void WorldSession::HandleLfgClearOpcode( WorldPacket & /*recv_data */ )
+{
+ sLog.outDebug("CMSG_LOOKING_FOR_GROUP_CLEAR");
+
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ _player->m_lookingForGroup.slots[i].Clear();
+
+ if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER )
+ _player->LeaveLFGChannel();
+}
+
+void WorldSession::HandleLfmSetNoneOpcode( WorldPacket & /*recv_data */)
+{
+ sLog.outDebug("CMSG_SET_LOOKING_FOR_NONE");
+
+ _player->m_lookingForGroup.more.Clear();
+}
+
+void WorldSession::HandleLfmSetOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ sLog.outDebug("CMSG_SET_LOOKING_FOR_MORE");
+ //recv_data.hexlike();
+ uint32 temp, entry, type;
+
+ recv_data >> temp;
+
+ entry = ( temp & 0xFFFF);
+ type = ( (temp >> 24) & 0xFFFF);
+
+ _player->m_lookingForGroup.more.Set(entry,type);
+ sLog.outDebug("LFM set: temp %u, zone %u, type %u", temp, entry, type);
+
+ if(LookingForGroup_auto_add)
+ AttemptAddMore(_player);
+
+ SendLfgResult(type, entry, 1);
+}
+
+void WorldSession::HandleLfgSetCommentOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug("CMSG_SET_COMMENTARY");
+ //recv_data.hexlike();
+
+ std::string comment;
+ recv_data >> comment;
+ sLog.outDebug("LFG comment %s", comment.c_str());
+
+ _player->m_lookingForGroup.comment = comment;
+}
+
+void WorldSession::HandleLookingForGroup(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+4);
+
+ sLog.outDebug("MSG_LOOKING_FOR_GROUP");
+ //recv_data.hexlike();
+ uint32 type, entry, unk;
+
+ recv_data >> type >> entry >> unk;
+ sLog.outDebug("MSG_LOOKING_FOR_GROUP: type %u, entry %u, unk %u", type, entry, unk);
+
+ if(LookingForGroup_auto_add)
+ AttemptAddMore(_player);
+
+ if(LookingForGroup_auto_join)
+ AttemptJoin(_player);
+
+ SendLfgResult(type, entry, 0);
+}
+
+void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
+{
+ uint32 number = 0;
+
+ // start preper packet;
+ WorldPacket data(MSG_LOOKING_FOR_GROUP);
+ data << uint32(type); // type
+ data << uint32(entry); // entry from LFGDungeons.dbc
+ data << uint32(0); // count, placeholder
+ data << uint32(0); // count again, strange, placeholder
+
+ //TODO: Guard Player map
+ HashMapHolder<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter)
+ {
+ Player *plr = iter->second;
+
+ if(!plr || plr->GetTeam() != _player->GetTeam())
+ continue;
+
+ if(!plr->m_lookingForGroup.HaveInSlot(entry,type))
+ continue;
+
+ ++number;
+
+ data.append(plr->GetPackGUID()); // packed guid
+ data << plr->getLevel(); // level
+ data << plr->GetZoneId(); // current zone
+ data << lfg_type; // 0x00 - LFG, 0x01 - LFM
+
+ for(uint8 j = 0; j < MAX_LOOKING_FOR_GROUP_SLOT; ++j)
+ {
+ data << uint32( plr->m_lookingForGroup.slots[j].entry | (plr->m_lookingForGroup.slots[j].type << 24) );
+ }
+ data << plr->m_lookingForGroup.comment;
+
+ Group *group = plr->GetGroup();
+ if(group)
+ {
+ data << group->GetMembersCount()-1; // count of group members without group leader
+ for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *member = itr->getSource();
+ if(member && member->GetGUID() != plr->GetGUID())
+ {
+ data.append(member->GetPackGUID()); // packed guid
+ data << member->getLevel(); // player level
+ }
+ }
+ }
+ else
+ {
+ data << uint32(0x00);
+ }
+ }
+
+ // fill count placeholders
+ data.put<uint32>(4+4, number);
+ data.put<uint32>(4+4+4,number);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleSetLfgOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4);
+
+ sLog.outDebug("CMSG_SET_LOOKING_FOR_GROUP");
+ //recv_data.hexlike();
+ uint32 slot, temp, entry, type;
+
+ recv_data >> slot >> temp;
+
+ entry = ( temp & 0xFFFF);
+ type = ( (temp >> 24) & 0xFFFF);
+
+ if(slot >= MAX_LOOKING_FOR_GROUP_SLOT)
+ return;
+
+ _player->m_lookingForGroup.slots[slot].Set(entry,type);
+ sLog.outDebug("LFG set: looknumber %u, temp %X, type %u, entry %u", slot, temp, type, entry);
+
+ if(LookingForGroup_auto_join)
+ AttemptJoin(_player);
+
+ SendLfgResult(type, entry, 0);
+}
diff --git a/src/game/Language.h b/src/game/Language.h
new file mode 100644
index 00000000000..bb36bffcee6
--- /dev/null
+++ b/src/game/Language.h
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __MANGOS_LANGUAGE_H
+#define __MANGOS_LANGUAGE_H
+
+enum MangosStrings
+{
+ // for chat commands
+ LANG_SELECT_CHAR_OR_CREATURE = 1,
+ LANG_SELECT_CREATURE = 2,
+
+ // level 0 chat
+ LANG_SYSTEMMESSAGE = 3,
+ LANG_EVENTMESSAGE = 4,
+ LANG_NO_HELP_CMD = 5,
+ LANG_NO_CMD = 6,
+ LANG_NO_SUBCMD = 7,
+ LANG_SUBCMDS_LIST = 8,
+ LANG_AVIABLE_CMD = 9,
+ LANG_CMD_SYNTAX = 10,
+ LANG_ACCOUNT_LEVEL = 11,
+ LANG_CONNECTED_USERS = 12,
+ LANG_UPTIME = 13,
+ LANG_PLAYER_SAVED = 14,
+ LANG_PLAYERS_SAVED = 15,
+ LANG_GMS_ON_SRV = 16,
+ LANG_GMS_NOT_LOGGED = 17,
+ LANG_YOU_IN_FLIGHT = 18,
+ //LANG_YOU_IN_BATTLEGROUND = 19, not used
+ //LANG_TARGET_IN_FLIGHT = 20, not used
+ LANG_CHAR_IN_FLIGHT = 21,
+ LANG_CHAR_NON_MOUNTED = 22,
+ LANG_YOU_IN_COMBAT = 23,
+ LANG_YOU_USED_IT_RECENTLY = 24,
+ LANG_COMMAND_NOTCHANGEPASSWORD = 25,
+ LANG_COMMAND_PASSWORD = 26,
+ LANG_COMMAND_WRONGOLDPASSWORD = 27,
+ LANG_COMMAND_ACCLOCKLOCKED = 28,
+ LANG_COMMAND_ACCLOCKUNLOCKED = 29,
+ LANG_SPELL_RANK = 30,
+ LANG_KNOWN = 31,
+ LANG_LEARN = 32,
+ LANG_PASSIVE = 33,
+ LANG_TALENT = 34,
+ LANG_ACTIVE = 35,
+ LANG_COMPLETE = 36,
+ LANG_OFFLINE = 37,
+ LANG_ON = 38,
+ LANG_OFF = 39,
+ LANG_YOU_ARE = 40,
+ LANG_VISIBLE = 41,
+ LANG_INVISIBLE = 42,
+ LANG_DONE = 43,
+ LANG_YOU = 44,
+ LANG_UNKNOWN = 45,
+ LANG_ERROR = 46,
+ LANG_NON_EXIST_CHARACTER = 47,
+ LANG_FRIEND_IGNORE_UNKNOWN = 48,
+ LANG_LEVEL_MINREQUIRED = 49,
+ LANG_LEVEL_MINREQUIRED_AND_ITEM = 50,
+ LANG_NPC_TAINER_HELLO = 51,
+ // Room for more level 0
+
+ // level 1 chat
+ LANG_GLOBAL_NOTIFY = 100,
+ LANG_MAP_POSITION = 101,
+ LANG_IS_TELEPORTED = 102,
+ LANG_CANNOT_SUMMON_TO_INST = 103,
+ LANG_CANNOT_GO_TO_INST_PARTY = 104,
+ LANG_CANNOT_GO_TO_INST_GM = 105,
+ LANG_CANNOT_GO_INST_INST = 106,
+ LANG_CANNOT_SUMMON_INST_INST = 107,
+
+ LANG_SUMMONING = 108,
+ LANG_SUMMONED_BY = 109,
+ LANG_TELEPORTING_TO = 110,
+ LANG_TELEPORTED_TO_BY = 111,
+ LANG_NO_PLAYER = 112,
+ LANG_APPEARING_AT = 113,
+ LANG_APPEARING_TO = 114,
+
+ LANG_BAD_VALUE = 115,
+ LANG_NO_CHAR_SELECTED = 116,
+ LANG_NOT_IN_GROUP = 117,
+
+ LANG_YOU_CHANGE_HP = 118,
+ LANG_YOURS_HP_CHANGED = 119,
+ LANG_YOU_CHANGE_MANA = 120,
+ LANG_YOURS_MANA_CHANGED = 121,
+ LANG_YOU_CHANGE_ENERGY = 122,
+ LANG_YOURS_ENERGY_CHANGED = 123,
+
+ LANG_CURRENT_ENERGY = 124, //log
+ LANG_YOU_CHANGE_RAGE = 125,
+ LANG_YOURS_RAGE_CHANGED = 126,
+ LANG_YOU_CHANGE_LVL = 127,
+ LANG_CURRENT_FACTION = 128,
+ LANG_WRONG_FACTION = 129,
+ LANG_YOU_CHANGE_FACTION = 130,
+ LANG_YOU_CHANGE_SPELLFLATID = 131,
+ LANG_YOURS_SPELLFLATID_CHANGED = 132,
+ LANG_YOU_GIVE_TAXIS = 133,
+ LANG_YOU_REMOVE_TAXIS = 134,
+ LANG_YOURS_TAXIS_ADDED = 135,
+ LANG_YOURS_TAXIS_REMOVED = 136,
+
+ LANG_YOU_CHANGE_ASPEED = 137,
+ LANG_YOURS_ASPEED_CHANGED = 138,
+ LANG_YOU_CHANGE_SPEED = 139,
+ LANG_YOURS_SPEED_CHANGED = 140,
+ LANG_YOU_CHANGE_SWIM_SPEED = 141,
+ LANG_YOURS_SWIM_SPEED_CHANGED = 142,
+ LANG_YOU_CHANGE_BACK_SPEED = 143,
+ LANG_YOURS_BACK_SPEED_CHANGED = 144,
+ LANG_YOU_CHANGE_FLY_SPEED = 145,
+ LANG_YOURS_FLY_SPEED_CHANGED = 146,
+
+ LANG_YOU_CHANGE_SIZE = 147,
+ LANG_YOURS_SIZE_CHANGED = 148,
+ LANG_NO_MOUNT = 149,
+ LANG_YOU_GIVE_MOUNT = 150,
+ LANG_MOUNT_GIVED = 151,
+
+ LANG_CURRENT_MONEY = 152,
+ LANG_YOU_TAKE_ALL_MONEY = 153,
+ LANG_YOURS_ALL_MONEY_GONE = 154,
+ LANG_YOU_TAKE_MONEY = 155,
+ LANG_YOURS_MONEY_TAKEN = 156,
+ LANG_YOU_GIVE_MONEY = 157,
+ LANG_YOURS_MONEY_GIVEN = 158,
+ LANG_YOU_HEAR_SOUND = 159,
+
+ LANG_NEW_MONEY = 160, // Log
+
+ LANG_REMOVE_BIT = 161,
+ LANG_SET_BIT = 162,
+ LANG_COMMAND_TELE_TABLEEMPTY = 163,
+ LANG_COMMAND_TELE_NOTFOUND = 164,
+ LANG_COMMAND_TELE_PARAMETER = 165,
+ LANG_COMMAND_TELE_NOREQUEST = 166,
+ LANG_COMMAND_TELE_NOLOCATION = 167,
+ LANG_COMMAND_TELE_LOCATION = 168,
+
+ LANG_MAIL_SENT = 169,
+ LANG_SOUND_NOT_EXIST = 170,
+ // Room for more level 1
+
+ // level 2 chat
+ LANG_NO_SELECTION = 200,
+ LANG_OBJECT_GUID = 201,
+ LANG_TOO_LONG_NAME = 202,
+ LANG_CHARS_ONLY = 203,
+ LANG_TOO_LONG_SUBNAME = 204,
+ LANG_NOT_IMPLEMENTED = 205,
+
+ LANG_ITEM_ADDED_TO_LIST = 206,
+ LANG_ITEM_NOT_FOUND = 207,
+ LANG_ITEM_DELETED_FROM_LIST = 208,
+ LANG_ITEM_NOT_IN_LIST = 209,
+ LANG_ITEM_ALREADY_IN_LIST = 210,
+
+ LANG_RESET_SPELLS_ONLINE = 211,
+ LANG_RESET_SPELLS_OFFLINE = 212,
+ LANG_RESET_TALENTS_ONLINE = 213,
+ LANG_RESET_TALENTS_OFFLINE = 214,
+ LANG_RESET_SPELLS = 215,
+ LANG_RESET_TALENTS = 216,
+
+ LANG_RESETALL_UNKNOWN_CASE = 217,
+ LANG_RESETALL_SPELLS = 218,
+ LANG_RESETALL_TALENTS = 219,
+
+ LANG_WAYPOINT_NOTFOUND = 220,
+ LANG_WAYPOINT_NOTFOUNDLAST = 221,
+ LANG_WAYPOINT_NOTFOUNDSEARCH = 222,
+ LANG_WAYPOINT_NOTFOUNDDBPROBLEM = 223,
+ LANG_WAYPOINT_CREATSELECTED = 224,
+ LANG_WAYPOINT_CREATNOTFOUND = 225,
+ LANG_WAYPOINT_VP_SELECT = 226,
+ LANG_WAYPOINT_VP_NOTFOUND = 227,
+ LANG_WAYPOINT_VP_NOTCREATED = 228,
+ LANG_WAYPOINT_VP_ALLREMOVED = 229,
+ LANG_WAYPOINT_NOTCREATED = 230,
+ LANG_WAYPOINT_NOGUID = 231,
+ LANG_WAYPOINT_NOWAYPOINTGIVEN = 232,
+ LANG_WAYPOINT_ARGUMENTREQ = 233,
+ LANG_WAYPOINT_ADDED = 234,
+ LANG_WAYPOINT_ADDED_NO = 235,
+ LANG_WAYPOINT_CHANGED = 236,
+ LANG_WAYPOINT_CHANGED_NO = 237,
+ LANG_WAYPOINT_EXPORTED = 238,
+ LANG_WAYPOINT_NOTHINGTOEXPORT = 239,
+ LANG_WAYPOINT_IMPORTED = 240,
+ LANG_WAYPOINT_REMOVED = 241,
+ LANG_WAYPOINT_NOTREMOVED = 242,
+ LANG_WAYPOINT_TOOFAR1 = 243,
+ LANG_WAYPOINT_TOOFAR2 = 244,
+ LANG_WAYPOINT_TOOFAR3 = 245,
+ LANG_WAYPOINT_INFO_TITLE = 246,
+ LANG_WAYPOINT_INFO_WAITTIME = 247,
+ LANG_WAYPOINT_INFO_MODEL = 248,
+ LANG_WAYPOINT_INFO_EMOTE = 249,
+ LANG_WAYPOINT_INFO_SPELL = 250,
+ LANG_WAYPOINT_INFO_TEXT = 251,
+ LANG_WAYPOINT_INFO_AISCRIPT = 252,
+
+ LANG_RENAME_PLAYER = 253,
+ LANG_RENAME_PLAYER_GUID = 254,
+
+ LANG_WAYPOINT_WPCREATNOTFOUND = 255,
+ LANG_WAYPOINT_NPCNOTFOUND = 256,
+
+ LANG_MOVE_TYPE_SET = 257,
+ LANG_MOVE_TYPE_SET_NODEL = 258,
+ LANG_USE_BOL = 259,
+ LANG_VALUE_SAVED = 260,
+ LANG_VALUE_SAVED_REJOIN = 261,
+
+ LANG_COMMAND_GOAREATRNOTFOUND = 262,
+ LANG_INVALID_TARGET_COORD = 263,
+ LANG_INVALID_ZONE_COORD = 264,
+ LANG_INVALID_ZONE_MAP = 265,
+ LANG_COMMAND_TARGETOBJNOTFOUND = 266,
+ LANG_COMMAND_GOOBJNOTFOUND = 267,
+ LANG_COMMAND_GOCREATNOTFOUND = 268,
+ LANG_COMMAND_GOCREATMULTIPLE = 269,
+ LANG_COMMAND_DELCREATMESSAGE = 270,
+ LANG_COMMAND_CREATUREMOVED = 271,
+ LANG_COMMAND_CREATUREATSAMEMAP = 272,
+ LANG_COMMAND_OBJNOTFOUND = 273,
+ LANG_COMMAND_DELOBJREFERCREATURE = 274,
+ LANG_COMMAND_DELOBJMESSAGE = 275,
+ LANG_COMMAND_TURNOBJMESSAGE = 276,
+ LANG_COMMAND_MOVEOBJMESSAGE = 277,
+ LANG_COMMAND_VENDORSELECTION = 278,
+ LANG_COMMAND_NEEDITEMSEND = 279,
+ LANG_COMMAND_ADDVENDORITEMITEMS = 280,
+ LANG_COMMAND_KICKSELF = 281,
+ LANG_COMMAND_KICKMESSAGE = 282,
+ LANG_COMMAND_KICKNOTFOUNDPLAYER = 283,
+ LANG_COMMAND_WHISPERACCEPTING = 284,
+ LANG_COMMAND_WHISPERON = 285,
+ LANG_COMMAND_WHISPEROFF = 286,
+ LANG_COMMAND_CREATGUIDNOTFOUND = 287,
+ LANG_COMMAND_TICKETCOUNT = 288,
+ LANG_COMMAND_TICKETNEW = 289,
+ LANG_COMMAND_TICKETVIEW = 290,
+ LANG_COMMAND_TICKETON = 291,
+ LANG_COMMAND_TICKETOFF = 292,
+ LANG_COMMAND_TICKENOTEXIST = 293,
+ LANG_COMMAND_ALLTICKETDELETED = 294,
+ LANG_COMMAND_TICKETPLAYERDEL = 295,
+ LANG_COMMAND_TICKETDEL = 296,
+ LANG_COMMAND_SPAWNDIST = 297,
+ LANG_COMMAND_SPAWNTIME = 298,
+ LANG_COMMAND_MODIFY_HONOR = 299,
+
+ LANG_YOUR_CHAT_DISABLED = 300,
+ LANG_YOU_DISABLE_CHAT = 301,
+ LANG_CHAT_ALREADY_ENABLED = 302,
+ LANG_YOUR_CHAT_ENABLED = 303,
+ LANG_YOU_ENABLE_CHAT = 304,
+
+ LANG_COMMAND_MODIFY_REP = 305,
+ LANG_COMMAND_MODIFY_ARENA = 306,
+ LANG_COMMAND_FACTION_NOTFOUND = 307,
+ LANG_COMMAND_FACTION_UNKNOWN = 308,
+ LANG_COMMAND_FACTION_INVPARAM = 309,
+ LANG_COMMAND_FACTION_DELTA = 310,
+ LANG_FACTION_LIST = 311,
+ LANG_FACTION_VISIBLE = 312,
+ LANG_FACTION_ATWAR = 313,
+ LANG_FACTION_PEACE_FORCED = 314,
+ LANG_FACTION_HIDDEN = 315,
+ LANG_FACTION_INVISIBLE_FORCED = 316,
+ LANG_FACTION_INACTIVE = 317,
+ LANG_REP_HATED = 318,
+ LANG_REP_HOSTILE = 319,
+ LANG_REP_UNFRIENDLY = 320,
+ LANG_REP_NEUTRAL = 321,
+ LANG_REP_FRIENDLY = 322,
+ LANG_REP_HONORED = 323,
+ LANG_REP_REVERED = 324,
+ LANG_REP_EXALTED = 325,
+ LANG_COMMAND_FACTION_NOREP_ERROR = 326,
+ LANG_FACTION_NOREPUTATION = 327,
+
+ // Room for more level 2
+
+ // level 3 chat
+ LANG_SCRIPTS_RELOADED = 400,
+ LANG_YOU_CHANGE_SECURITY = 401,
+ LANG_YOURS_SECURITY_CHANGED = 402,
+ LANG_YOURS_SECURITY_IS_LOW = 403,
+ LANG_CREATURE_MOVE_DISABLED = 404,
+ LANG_CREATURE_MOVE_ENABLED = 405,
+ LANG_NO_WEATHER = 406,
+ LANG_WEATHER_DISABLED = 407,
+
+ LANG_BAN_YOUBANNED = 408,
+ LANG_BAN_YOUPERMBANNED = 409,
+ LANG_BAN_NOTFOUND = 410,
+
+ LANG_UNBAN_UNBANNED = 411,
+ LANG_UNBAN_ERROR = 412,
+
+ LANG_BANINFO_NOACCOUNT = 413,
+ LANG_BANINFO_NOCHARACTER = 414,
+ LANG_BANINFO_NOIP = 415,
+ LANG_BANINFO_NOACCOUNTBAN = 416,
+ LANG_BANINFO_BANHISTORY = 417,
+ LANG_BANINFO_HISTORYENTRY = 418,
+ LANG_BANINFO_INFINITE = 419,
+ LANG_BANINFO_NEVER = 420,
+ LANG_BANINFO_YES = 421,
+ LANG_BANINFO_NO = 422,
+ LANG_BANINFO_IPENTRY = 423,
+
+ LANG_BANLIST_NOIP = 424,
+ LANG_BANLIST_NOACCOUNT = 425,
+ LANG_BANLIST_NOCHARACTER = 426,
+ LANG_BANLIST_MATCHINGIP = 427,
+ LANG_BANLIST_MATCHINGACCOUNT = 428,
+
+ LANG_COMMAND_LEARN_MANY_SPELLS = 429,
+ LANG_COMMAND_LEARN_CLASS_SPELLS = 430,
+ LANG_COMMAND_LEARN_CLASS_TALENTS = 431,
+ LANG_COMMAND_LEARN_ALL_LANG = 432,
+ LANG_COMMAND_LEARN_ALL_CRAFT = 433,
+ LANG_COMMAND_COULDNOTFIND = 434,
+ LANG_COMMAND_ITEMIDINVALID = 435,
+ LANG_COMMAND_NOITEMFOUND = 436,
+ LANG_COMMAND_LISTOBJINVALIDID = 437,
+ LANG_COMMAND_LISTITEMMESSAGE = 438,
+ LANG_COMMAND_LISTOBJMESSAGE = 439,
+ LANG_COMMAND_INVALIDCREATUREID = 440,
+ LANG_COMMAND_LISTCREATUREMESSAGE = 441,
+ LANG_COMMAND_NOAREAFOUND = 442,
+ LANG_COMMAND_NOITEMSETFOUND = 443,
+ LANG_COMMAND_NOSKILLFOUND = 444,
+ LANG_COMMAND_NOSPELLFOUND = 445,
+ LANG_COMMAND_NOQUESTFOUND = 446,
+ LANG_COMMAND_NOCREATUREFOUND = 447,
+ LANG_COMMAND_NOGAMEOBJECTFOUND = 448,
+ LANG_COMMAND_GRAVEYARDNOEXIST = 449,
+ LANG_COMMAND_GRAVEYARDALRLINKED = 450,
+ LANG_COMMAND_GRAVEYARDLINKED = 451,
+ LANG_COMMAND_GRAVEYARDWRONGZONE = 452,
+ LANG_COMMAND_GRAVEYARDWRONGTEAM = 453,
+ LANG_COMMAND_GRAVEYARDERROR = 454,
+ LANG_COMMAND_GRAVEYARD_NOTEAM = 455,
+ LANG_COMMAND_GRAVEYARD_ANY = 456,
+ LANG_COMMAND_GRAVEYARD_ALLIANCE = 457,
+ LANG_COMMAND_GRAVEYARD_HORDE = 458,
+ LANG_COMMAND_GRAVEYARDNEAREST = 459,
+ LANG_COMMAND_ZONENOGRAVEYARDS = 460,
+ LANG_COMMAND_ZONENOGRAFACTION = 461,
+ LANG_COMMAND_TP_ALREADYEXIST = 462,
+ LANG_COMMAND_TP_ADDED = 463,
+ LANG_COMMAND_TP_ADDEDERR = 464,
+ LANG_COMMAND_TP_DELETED = 465,
+ LANG_COMMAND_TP_DELETEERR = 466,
+
+ LANG_COMMAND_TARGET_LISTAURAS = 467,
+ LANG_COMMAND_TARGET_AURADETAIL = 468,
+ LANG_COMMAND_TARGET_LISTAURATYPE = 469,
+ LANG_COMMAND_TARGET_AURASIMPLE = 470,
+
+ LANG_COMMAND_QUEST_NOTFOUND = 471,
+ LANG_COMMAND_QUEST_STARTFROMITEM = 472,
+ LANG_COMMAND_QUEST_REMOVED = 473,
+ LANG_COMMAND_QUEST_REWARDED = 474,
+ LANG_COMMAND_QUEST_COMPLETE = 475,
+ LANG_COMMAND_QUEST_ACTIVE = 476,
+
+ LANG_COMMAND_FLYMODE_STATUS = 477,
+
+ LANG_COMMAND_OPCODESENT = 478,
+
+ LANG_COMMAND_IMPORT_SUCCESS = 479,
+ LANG_COMMAND_IMPORT_FAILED = 480,
+ LANG_COMMAND_EXPORT_SUCCESS = 481,
+ LANG_COMMAND_EXPORT_FAILED = 482,
+
+ LANG_COMMAND_SPELL_BROKEN = 483,
+
+ LANG_SET_SKILL = 484,
+ LANG_SET_SKILL_ERROR = 485,
+
+ LANG_INVALID_SKILL_ID = 486,
+ LANG_LEARNING_GM_SKILLS = 487,
+ LANG_YOU_KNOWN_SPELL = 488,
+ LANG_TARGET_KNOWN_SPELL = 489,
+ LANG_UNKNOWN_SPELL = 490,
+ LANG_FORGET_SPELL = 491,
+ LANG_REMOVEALL_COOLDOWN = 492,
+ LANG_REMOVE_COOLDOWN = 493,
+
+ LANG_ADDITEM = 494, //log
+ LANG_ADDITEMSET = 495, //log
+ LANG_REMOVEITEM = 496,
+ LANG_ITEM_CANNOT_CREATE = 497,
+ LANG_INSERT_GUILD_NAME = 498,
+ LANG_PLAYER_NOT_FOUND = 499,
+ LANG_PLAYER_IN_GUILD = 500,
+ LANG_GUILD_NOT_CREATED = 501,
+ LANG_NO_ITEMS_FROM_ITEMSET_FOUND = 502,
+
+ LANG_DISTANCE = 503,
+
+ LANG_ITEM_SLOT = 504,
+ LANG_ITEM_SLOT_NOT_EXIST = 505,
+ LANG_ITEM_ADDED_TO_SLOT = 506,
+ LANG_ITEM_SAVE_FAILED = 507,
+ LANG_ITEMLIST_SLOT = 508,
+ LANG_ITEMLIST_MAIL = 509,
+ LANG_ITEMLIST_AUCTION = 510,
+
+ LANG_WRONG_LINK_TYPE = 511,
+ LANG_ITEM_LIST = 512,
+ LANG_QUEST_LIST = 513,
+ LANG_CREATURE_ENTRY_LIST = 514,
+ LANG_CREATURE_LIST = 515,
+ LANG_GO_ENTRY_LIST = 516,
+ LANG_GO_LIST = 517,
+ LANG_ITEMSET_LIST = 518,
+ LANG_TELE_LIST = 519,
+ LANG_SPELL_LIST = 520,
+ LANG_SKILL_LIST = 521,
+
+ LANG_GAMEOBJECT_NOT_EXIST = 522,
+
+ LANG_GAMEOBJECT_CURRENT = 523, //log
+ LANG_GAMEOBJECT_DETAIL = 524,
+ LANG_GAMEOBJECT_ADD = 525,
+
+ LANG_MOVEGENS_LIST = 526,
+ LANG_MOVEGENS_IDLE = 527,
+ LANG_MOVEGENS_RANDOM = 528,
+ LANG_MOVEGENS_WAYPOINT = 529,
+ LANG_MOVEGENS_ANIMAL_RANDOM = 530,
+ LANG_MOVEGENS_CONFUSED = 531,
+ LANG_MOVEGENS_TARGETED_PLAYER = 532,
+ LANG_MOVEGENS_TARGETED_CREATURE = 533,
+ LANG_MOVEGENS_TARGETED_NULL = 534,
+ LANG_MOVEGENS_HOME_CREATURE = 535,
+ LANG_MOVEGENS_HOME_PLAYER = 536,
+ LANG_MOVEGENS_FLIGHT = 537,
+ LANG_MOVEGENS_UNKNOWN = 538,
+
+ LANG_NPCINFO_CHAR = 539,
+ LANG_NPCINFO_LEVEL = 540,
+ LANG_NPCINFO_HEALTH = 541,
+ LANG_NPCINFO_FLAGS = 542,
+ LANG_NPCINFO_LOOT = 543,
+ LANG_NPCINFO_POSITION = 544,
+ LANG_NPCINFO_VENDOR = 545,
+ LANG_NPCINFO_TRAINER = 546,
+ LANG_NPCINFO_DUNGEON_ID = 547,
+
+ LANG_PINFO_ACCOUNT = 548,
+ LANG_PINFO_LEVEL = 549,
+ LANG_PINFO_NO_REP = 550,
+
+ LANG_YOU_SET_EXPLORE_ALL = 551,
+ LANG_YOU_SET_EXPLORE_NOTHING = 552,
+ LANG_YOURS_EXPLORE_SET_ALL = 553,
+ LANG_YOURS_EXPLORE_SET_NOTHING = 554,
+
+ LANG_HOVER_ENABLED = 555,
+ LANG_HOVER_DISABLED = 556,
+ LANG_YOURS_LEVEL_UP = 557,
+ LANG_YOURS_LEVEL_DOWN = 558,
+ LANG_YOURS_LEVEL_PROGRESS_RESET = 559,
+ LANG_EXPLORE_AREA = 560,
+ LANG_UNEXPLORE_AREA = 561,
+
+ LANG_UPDATE = 562,
+ LANG_UPDATE_CHANGE = 563,
+ LANG_TOO_BIG_INDEX = 564,
+ LANG_SET_UINT = 565, //log
+ LANG_SET_UINT_FIELD = 566,
+ LANG_SET_FLOAT = 567, //log
+ LANG_SET_FLOAT_FIELD = 568,
+ LANG_GET_UINT = 569, //log
+ LANG_GET_UINT_FIELD = 570,
+ LANG_GET_FLOAT = 571, //log
+ LANG_GET_FLOAT_FIELD = 572,
+ LANG_SET_32BIT = 573, //log
+ LANG_SET_32BIT_FIELD = 574,
+ LANG_CHANGE_32BIT = 575, //log
+ LANG_CHANGE_32BIT_FIELD = 576,
+
+ LANG_INVISIBLE_INVISIBLE = 577,
+ LANG_INVISIBLE_VISIBLE = 578,
+ LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579,
+
+ LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST = 580,
+ LANG_COMMAND_NEAROBJMESSAGE = 581,
+ LANG_COMMAND_RAWPAWNTIMES = 582,
+
+ LANG_EVENT_ENTRY_LIST = 583,
+ LANG_NOEVENTFOUND = 584,
+ LANG_EVENT_NOT_EXIST = 585,
+ LANG_EVENT_INFO = 586,
+ LANG_EVENT_ALREADY_ACTIVE = 587,
+ LANG_EVENT_NOT_ACTIVE = 588,
+
+ LANG_MOVEGENS_POINT = 589,
+ LANG_MOVEGENS_FEAR = 590,
+ LANG_MOVEGENS_DISTRACT = 591,
+
+ LANG_COMMAND_LEARN_ALL_RECIPES = 592,
+
+ // Battleground
+ LANG_BG_A_WINS = 600,
+ LANG_BG_H_WINS = 601,
+ LANG_BG_WS_ONE_MINUTE = 602,
+ LANG_BG_WS_HALF_MINUTE = 603,
+ LANG_BG_WS_BEGIN = 604,
+
+ LANG_BG_WS_CAPTURED_HF = 605,
+ LANG_BG_WS_CAPTURED_AF = 606,
+ LANG_BG_WS_DROPPED_HF = 607,
+ LANG_BG_WS_DROPPED_AF = 608,
+ LANG_BG_WS_RETURNED_AF = 609,
+ LANG_BG_WS_RETURNED_HF = 610,
+ LANG_BG_WS_PICKEDUP_HF = 611,
+ LANG_BG_WS_PICKEDUP_AF = 612,
+ LANG_BG_WS_F_PLACED = 613,
+ LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED = 614,
+ LANG_BG_WS_HORDE_FLAG_RESPAWNED = 615,
+
+ LANG_BG_EY_ONE_MINUTE = 636,
+ LANG_BG_EY_HALF_MINUTE = 637,
+ LANG_BG_EY_BEGIN = 638,
+
+ LANG_BG_AB_ALLY = 650,
+ LANG_BG_AB_HORDE = 651,
+ LANG_BG_AB_NODE_STABLES = 652,
+ LANG_BG_AB_NODE_BLACKSMITH = 653,
+ LANG_BG_AB_NODE_FARM = 654,
+ LANG_BG_AB_NODE_LUMBER_MILL = 655,
+ LANG_BG_AB_NODE_GOLD_MINE = 656,
+ LANG_BG_AB_NODE_TAKEN = 657,
+ LANG_BG_AB_NODE_DEFENDED = 658,
+ LANG_BG_AB_NODE_ASSAULTED = 659,
+ LANG_BG_AB_NODE_CLAIMED = 660,
+ LANG_BG_AB_ONEMINTOSTART = 661,
+ LANG_BG_AB_HALFMINTOSTART = 662,
+ LANG_BG_AB_STARTED = 663,
+ LANG_BG_AB_A_NEAR_VICTORY = 664,
+ LANG_BG_AB_H_NEAR_VICTORY = 665,
+ LANG_BG_MARK_BY_MAIL = 666,
+
+ LANG_BG_EY_HAS_TAKEN_A_M_TOWER = 667,
+ LANG_BG_EY_HAS_TAKEN_H_M_TOWER = 668,
+ LANG_BG_EY_HAS_TAKEN_A_D_RUINS = 669,
+ LANG_BG_EY_HAS_TAKEN_H_D_RUINS = 670,
+ LANG_BG_EY_HAS_TAKEN_A_B_TOWER = 671,
+ LANG_BG_EY_HAS_TAKEN_H_B_TOWER = 672,
+ LANG_BG_EY_HAS_TAKEN_A_F_RUINS = 673,
+ LANG_BG_EY_HAS_TAKEN_H_F_RUINS = 674,
+ LANG_BG_EY_HAS_LOST_A_M_TOWER = 675,
+ LANG_BG_EY_HAS_LOST_H_M_TOWER = 676,
+ LANG_BG_EY_HAS_LOST_A_D_RUINS = 677,
+ LANG_BG_EY_HAS_LOST_H_D_RUINS = 678,
+ LANG_BG_EY_HAS_LOST_A_B_TOWER = 679,
+ LANG_BG_EY_HAS_LOST_H_B_TOWER = 680,
+ LANG_BG_EY_HAS_LOST_A_F_RUINS = 681,
+ LANG_BG_EY_HAS_LOST_H_F_RUINS = 682,
+ LANG_BG_EY_HAS_TAKEN_FLAG = 683,
+ LANG_BG_EY_CAPTURED_FLAG_A = 684,
+ LANG_BG_EY_CAPTURED_FLAG_H = 685,
+ LANG_BG_EY_DROPPED_FLAG = 686,
+ LANG_BG_EY_RESETED_FLAG = 687,
+
+ LANG_ARENA_ONE_TOOLOW = 700,
+ LANG_ARENA_ONE_MINUTE = 701,
+ LANG_ARENA_THIRTY_SECONDS = 702,
+ LANG_ARENA_FIFTEEN_SECONDS = 703,
+ LANG_ARENA_BEGUN = 704,
+
+ LANG_WAIT_BEFORE_SPEAKING = 705,
+ LANG_NOT_EQUIPPED_ITEM = 706,
+ LANG_PLAYER_DND = 707,
+ LANG_PLAYER_AFK = 708,
+ LANG_PLAYER_DND_DEFAULT = 709,
+ LANG_PLAYER_AFK_DEFAULT = 710,
+
+ LANG_BG_QUEUE_ANNOUNCE_SELF = 711,
+ LANG_BG_QUEUE_ANNOUNCE_WORLD = 712,
+};
+#endif
+
+/* NOT USED VALUES
+// alliance ranks
+#define LANG_ALI_PRIVATE "Private "
+#define LANG_ALI_CORPORAL "Corporal "
+#define LANG_ALI_SERGEANT "Sergeant "
+#define LANG_ALI_MASTER_SERGEANT "Master Sergeant "
+#define LANG_ALI_SERGEANT_MAJOR "Sergeant Major "
+#define LANG_ALI_KNIGHT "Knight "
+#define LANG_ALI_KNIGHT_LIEUTENANT "Knight-Lieutenant "
+#define LANG_ALI_KNIGHT_CAPTAIN "Knight-Captain "
+#define LANG_ALI_KNIGHT_CHAMPION "Knight-Champion "
+#define LANG_ALI_LIEUTENANT_COMMANDER "Lieutenant Commander "
+#define LANG_ALI_COMMANDER "Commander "
+#define LANG_ALI_MARSHAL "Marshal "
+#define LANG_ALI_FIELD_MARSHAL "Field Marshal "
+#define LANG_ALI_GRAND_MARSHAL "Grand Marshal "
+#define LANG_ALI_GAME_MASTER "Game Master "
+
+// horde ranks
+#define LANG_HRD_SCOUT "Scout "
+#define LANG_HRD_GRUNT "Grunt "
+#define LANG_HRD_SERGEANT "Sergeant "
+#define LANG_HRD_SENIOR_SERGEANT "Senior Sergeant "
+#define LANG_HRD_FIRST_SERGEANT "First Sergeant "
+#define LANG_HRD_STONE_GUARD "Stone Guard "
+#define LANG_HRD_BLOOD_GUARD "Blood Guard "
+#define LANG_HRD_LEGIONNARE "Legionnaire "
+#define LANG_HRD_CENTURION "Centurion "
+#define LANG_HRD_CHAMPION "Champion "
+#define LANG_HRD_LIEUTENANT_GENERAL "Lieutenant General "
+#define LANG_HRD_GENERAL "General "
+#define LANG_HRD_WARLORD "Warlord "
+#define LANG_HRD_HIGH_WARLORD "High Warlord "
+#define LANG_HRD_GAME_MASTER "Game Master "
+
+#define LANG_NO_RANK "No rank "
+#define LANG_RANK "%s (Rank %u)"
+#define LANG_HONOR_TODAY "Today: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r]"
+#define LANG_HONOR_YESTERDAY "Yesterday: [Kills: |c0000ff00%u|r] [Honor: %u]"
+#define LANG_HONOR_THIS_WEEK "This week: [Kills: |c0000ff00%u|r] [Honor: %u]"
+#define LANG_HONOR_LAST_WEEK "Last week: [Kills: |c0000ff00%u|r] [Honor: %u] [Standing: %u]"
+#define LANG_HONOR_LIFE "Lifetime: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r] [Highest rank %u: %s]"
+
+// level 2
+#define LANG_ADD_OBJ "AddObject at Chat.cpp" //log
+#define LANG_DEMORPHED "Demorphed %s" //log
+
+// level 3
+#define LANG_SPAWNING_SPIRIT_HEAL "Spawning spirit healers\n"
+#define LANG_NO_SPIRIT_HEAL_DB "No spirit healers in database, exiting."
+
+#define LANG_ADD_OBJ_LV3 "AddObject at Level3.cpp line 1176"
+
+*/
diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp
new file mode 100644
index 00000000000..5130b158360
--- /dev/null
+++ b/src/game/Level0.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Language.h"
+#include "AccountMgr.h"
+#include "SystemConfig.h"
+#include "Util.h"
+
+bool ChatHandler::HandleHelpCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* cmd = strtok((char*)args, " ");
+ if(!cmd)
+ return false;
+
+ if(!ShowHelpForCommand(getCommandTable(), cmd))
+ SendSysMessage(LANG_NO_HELP_CMD);
+
+ return true;
+}
+
+bool ChatHandler::HandleCommandsCommand(const char* args)
+{
+ ShowHelpForCommand(getCommandTable(), "");
+ return true;
+}
+
+bool ChatHandler::HandleAcctCommand(const char* /*args*/)
+{
+ uint32 gmlevel = m_session->GetSecurity();
+ PSendSysMessage(LANG_ACCOUNT_LEVEL, gmlevel);
+ return true;
+}
+
+bool ChatHandler::HandleStartCommand(const char* /*args*/)
+{
+ Player *chr = m_session->GetPlayer();
+
+ if(chr->isInFlight())
+ {
+ SendSysMessage(LANG_YOU_IN_FLIGHT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInCombat())
+ {
+ SendSysMessage(LANG_YOU_IN_COMBAT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // cast spell Stuck
+ chr->CastSpell(chr,7355,false);
+ return true;
+}
+
+bool ChatHandler::HandleInfoCommand(const char* /*args*/)
+{
+ uint32 activeClientsNum = sWorld.GetActiveSessionCount();
+ uint32 queuedClientsNum = sWorld.GetQueuedSessionCount();
+ uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount();
+ uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount();
+ std::string str = secsToTimeString(sWorld.GetUptime());
+
+ PSendSysMessage(_FULLVERSION);
+ PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum);
+ PSendSysMessage(LANG_UPTIME, str.c_str());
+
+ return true;
+}
+
+bool ChatHandler::HandleDismountCommand(const char* /*args*/)
+{
+ //If player is not mounted, so go out :)
+ if (!m_session->GetPlayer( )->IsMounted())
+ {
+ SendSysMessage(LANG_CHAR_NON_MOUNTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(m_session->GetPlayer( )->isInFlight())
+ {
+ SendSysMessage(LANG_YOU_IN_FLIGHT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ m_session->GetPlayer()->Unmount();
+ m_session->GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ return true;
+}
+
+bool ChatHandler::HandleSaveCommand(const char* /*args*/)
+{
+ Player *player=m_session->GetPlayer();
+
+ // save GM account without delay and output message (testing, etc)
+ if(m_session->GetSecurity())
+ {
+ player->SaveToDB();
+ SendSysMessage(LANG_PLAYER_SAVED);
+ return true;
+ }
+
+ // save or plan save after 20 sec (logout delay) if current next save time more this value and _not_ output any messages to prevent cheat planning
+ uint32 save_interval = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
+ if(save_interval==0 || save_interval > 20*1000 && player->GetSaveTimer() <= save_interval - 20*1000)
+ player->SaveToDB();
+
+ return true;
+}
+
+bool ChatHandler::HandleGMListCommand(const char* /*args*/)
+{
+ bool first = true;
+
+ HashMapHolder<Player>::MapType &m = HashMapHolder<Player>::GetContainer();
+ HashMapHolder<Player>::MapType::iterator itr = m.begin();
+ for(; itr != m.end(); ++itr)
+ {
+ if( itr->second->GetSession()->GetSecurity() && (itr->second->isGameMaster() || sWorld.getConfig(CONFIG_GM_IN_GM_LIST) ) &&
+ itr->second->IsVisibleGloballyFor(m_session->GetPlayer()) )
+ {
+ if(first)
+ {
+ SendSysMessage(LANG_GMS_ON_SRV);
+ first = false;
+ }
+
+ SendSysMessage(itr->second->GetName());
+ }
+ }
+
+ if(first)
+ SendSysMessage(LANG_GMS_NOT_LOGGED);
+
+ return true;
+}
+
+bool ChatHandler::HandlePasswordCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char *old_pass = strtok ((char*)args, " ");
+ char *new_pass = strtok (NULL, " ");
+ char *new_pass_c = strtok (NULL, " ");
+
+ if( !old_pass || !new_pass || !new_pass_c )
+ return false;
+
+ std::string password_old = old_pass;
+ std::string password_new = new_pass;
+ std::string password_new_c = new_pass_c;
+
+ if(!accmgr.CheckPassword(m_session->GetAccountId(), password_old) || password_new != password_new_c)
+ {
+ SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ AccountOpResult result = accmgr.ChangePassword(m_session->GetAccountId(), password_new);
+
+ switch(result)
+ {
+ case AOR_OK:
+ SendSysMessage(LANG_COMMAND_PASSWORD);
+ break;
+ default:
+ SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleLockAccountCommand(const char* args)
+{
+ if (!*args)
+ {
+ SendSysMessage(LANG_USE_BOL);
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+ if (argstr == "on")
+ {
+ 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());
+ PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ return true;
+}
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
new file mode 100644
index 00000000000..21c1eec9f16
--- /dev/null
+++ b/src/game/Level1.cpp
@@ -0,0 +1,2351 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "Log.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Language.h"
+#include "CellImpl.h"
+#include "InstanceSaveMgr.h"
+#include "Util.h"
+#ifdef _DEBUG_VMAPS
+#include "VMapFactory.h"
+#endif
+
+bool ChatHandler::HandleSayCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->Say(args, LANG_UNIVERSAL, 0);
+
+ return true;
+}
+
+bool ChatHandler::HandleYellCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->Yell(args, LANG_UNIVERSAL, 0);
+
+ return true;
+}
+
+//show text emote by creature in chat
+bool ChatHandler::HandleTextEmoteCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->TextEmote(args, 0);
+
+ return true;
+}
+
+// make npc whisper to player
+bool ChatHandler::HandleNpcWhisperCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* receiver_str = strtok((char*)args, " ");
+ char* text = strtok(NULL, "");
+
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature || !receiver_str || !text)
+ {
+ return false;
+ }
+
+ uint64 receiver_guid= atol(receiver_str);
+
+ pCreature->Whisper(text,receiver_guid);
+
+ return true;
+}
+
+// global announce
+bool ChatHandler::HandleAnnounceCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ sWorld.SendWorldText(LANG_SYSTEMMESSAGE,args);
+ return true;
+}
+
+//notification player at the screen
+bool ChatHandler::HandleNotifyCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string str = GetMangosString(LANG_GLOBAL_NOTIFY);
+ str += args;
+
+ WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
+ data << str;
+ sWorld.SendGlobalMessage(&data);
+
+ return true;
+}
+
+//Enable\Dissable GM Mode
+bool ChatHandler::HandleGMmodeCommand(const char* args)
+{
+ if(!*args)
+ {
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string argstr = (char*)args;
+
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetGameMaster(true);
+ m_session->SendNotification("GM mode is ON");
+ #ifdef _DEBUG_VMAPS
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ vMapManager->processCommand("stoplog");
+ #endif
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ m_session->GetPlayer()->SetGameMaster(false);
+ m_session->SendNotification("GM mode is OFF");
+ #ifdef _DEBUG_VMAPS
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ vMapManager->processCommand("startlog");
+ #endif
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+//Enable\Dissable Invisible mode
+bool ChatHandler::HandleVisibleCommand(const char* args)
+{
+ if (!*args)
+ {
+ PSendSysMessage(LANG_YOU_ARE, m_session->GetPlayer()->isGMVisible() ? GetMangosString(LANG_VISIBLE) : GetMangosString(LANG_INVISIBLE));
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetGMVisible(true);
+ m_session->SendNotification(GetMangosString(LANG_INVISIBLE_VISIBLE));
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ m_session->SendNotification(GetMangosString(LANG_INVISIBLE_INVISIBLE));
+ m_session->GetPlayer()->SetGMVisible(false);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+bool ChatHandler::HandleGPSCommand(const char* args)
+{
+ WorldObject *obj = NULL;
+ if (*args)
+ {
+ std::string name = args;
+ if(normalizePlayerName(name))
+ obj = objmgr.GetPlayer(name.c_str());
+
+ if(!obj)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ obj = getSelectedUnit();
+
+ if(!obj)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ CellPair cell_val = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+ Cell cell(cell_val);
+
+ uint32 zone_id = obj->GetZoneId();
+ uint32 area_id = obj->GetAreaId();
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId());
+ AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id);
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id);
+
+ float zone_x = obj->GetPositionX();
+ float zone_y = obj->GetPositionY();
+
+ Map2ZoneCoordinates(zone_x,zone_y,zone_id);
+
+ Map const *map = MapManager::Instance().GetMap(obj->GetMapId(), obj);
+ float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
+ float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
+
+ GridPair p = MaNGOS::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY());
+
+ int gx=63-p.x_coord;
+ int gy=63-p.y_coord;
+
+ uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0;
+ uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0;
+
+ PSendSysMessage(LANG_MAP_POSITION,
+ obj->GetMapId(), (mapEntry ? mapEntry->name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ zone_id, (zoneEntry ? zoneEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ area_id, (areaEntry ? areaEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
+ cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
+ zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
+
+ sLog.outDebug("Player %s GPS call for %s '%s' (%s: %u):",
+ m_session->GetPlayer()->GetName(),
+ (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(),
+ (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry()) );
+ sLog.outDebug(GetMangosString(LANG_MAP_POSITION),
+ obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
+ cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
+ zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
+
+ return true;
+}
+
+//Summon Player
+bool ChatHandler::HandleNamegoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+ if(chr->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* pMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
+
+ if(pMap->Instanceable())
+ {
+ Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
+ if( cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId() )
+ {
+ // cannot summon from instance to instance
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // we are in instance, and can summon only player in our group with us as lead
+ if ( !m_session->GetPlayer()->GetGroup() || !chr->GetGroup() ||
+ (chr->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
+ (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) )
+ // the last check is a bit excessive, but let it be, just in case
+ {
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ PSendSysMessage(LANG_SUMMONING, chr->GetName(),"");
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ chr->SaveRecallPosition();
+
+ // before GM
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,chr->GetObjectSize());
+ chr->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,chr->GetOrientation());
+ }
+ else if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
+ {
+ PSendSysMessage(LANG_SUMMONING, name.c_str(),GetMangosString(LANG_OFFLINE));
+
+ // in point where GM stay
+ Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(),
+ m_session->GetPlayer()->GetPositionX(),
+ m_session->GetPlayer()->GetPositionY(),
+ m_session->GetPlayer()->GetPositionZ(),
+ m_session->GetPlayer()->GetOrientation(),
+ m_session->GetPlayer()->GetZoneId(),
+ guid);
+ }
+ else
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ }
+
+ return true;
+}
+
+//Teleport to Player
+bool ChatHandler::HandleGonameCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+ Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
+ if(cMap->Instanceable())
+ {
+ Map* pMap = MapManager::Instance().GetMap(_player->GetMapId(),_player);
+
+ // 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)
+ // 2) we are not bound to any group and have GM mode on
+ if (_player->GetGroup())
+ {
+ // we are in group, we can go only if we are in the player group
+ if (_player->GetGroup() != chr->GetGroup())
+ {
+ PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ // we are not in group, let's verify our GM mode
+ if (!_player->isGameMaster())
+ {
+ PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ // if the player or the player's group is bound to another instance
+ // the player will not be bound to another one
+ InstancePlayerBind *pBind = _player->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty());
+ if(!pBind)
+ {
+ Group *group = _player->GetGroup();
+ InstanceGroupBind *gBind = group ? group->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty()) : NULL;
+ if(!gBind)
+ {
+ // if no bind exists, create a solo bind
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(chr->GetInstanceId());
+ if(save) _player->BindToInstance(save, !save->CanReset());
+ }
+ }
+
+ _player->SetDifficulty(chr->GetDifficulty());
+ }
+
+ PSendSysMessage(LANG_APPEARING_AT, chr->GetName());
+
+ if (_player->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_APPEARING_TO, _player->GetName());
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ // to point to see at target with same orientation
+ float x,y,z;
+ chr->GetContactPoint(m_session->GetPlayer(),x,y,z);
+
+ _player->TeleportTo(chr->GetMapId(), x, y, z, _player->GetAngle( chr ), TELE_TO_GM_MODE);
+
+ return true;
+ }
+
+ if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
+ {
+ PSendSysMessage(LANG_APPEARING_AT, name.c_str());
+
+ // to point where player stay (if loaded)
+ float x,y,z,o;
+ uint32 map;
+ bool in_flight;
+ if(Player::LoadPositionFromDB(map,x,y,z,o,in_flight,guid))
+ {
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(map, x, y, z,_player->GetOrientation());
+ return true;
+ }
+ }
+
+ PSendSysMessage(LANG_NO_PLAYER, args);
+
+ SetSentErrorMessage(true);
+ return false;
+}
+
+// Teleport player to last position
+bool ChatHandler::HandleRecallCommand(const char* args)
+{
+ Player* chr = NULL;
+
+ if(!*args)
+ {
+ chr = getSelectedPlayer();
+ if(!chr)
+ chr = m_session->GetPlayer();
+ }
+ else
+ {
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr = objmgr.GetPlayer(name.c_str());
+
+ if(!chr)
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ if(chr->IsBeingTeleported())
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+
+ chr->TeleportTo(chr->m_recallMap, chr->m_recallX, chr->m_recallY, chr->m_recallZ, chr->m_recallO);
+ return true;
+}
+
+//Edit Player KnownTitles
+bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ uint64 titles = 0;
+
+ sscanf((char*)args, I64FMTD, &titles);
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 titles2 = titles;
+
+ for(int i=1; i < sCharTitlesStore.GetNumRows(); ++i)
+ if(CharTitlesEntry const* tEntry = sCharTitlesStore.LookupEntry(i))
+ titles2 &= ~(uint64(1) << tEntry->bit_index);
+
+ titles &= ~titles2; // remove not existed titles
+
+ chr->SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES, titles);
+ SendSysMessage(LANG_DONE);
+
+ return true;
+}
+
+//Edit Player HP
+bool ChatHandler::HandleModifyHPCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pHp = strtok((char*)args, " ");
+ // if (!pHp)
+ // return false;
+
+ // char* pHpMax = strtok(NULL, " ");
+ // if (!pHpMax)
+ // return false;
+
+ // int32 hpm = atoi(pHpMax);
+ // int32 hp = atoi(pHp);
+
+ int32 hp = atoi((char*)args);
+ int32 hpm = atoi((char*)args);
+
+ if (hp <= 0 || hpm <= 0 || hpm < hp)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_HP, chr->GetName(), hp, hpm);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, m_session->GetPlayer()->GetName(), hp, hpm);
+
+ chr->SetMaxHealth( hpm );
+ chr->SetHealth( hp );
+
+ return true;
+}
+
+//Edit Player Mana
+bool ChatHandler::HandleModifyManaCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+ int32 mana = atoi((char*)args);
+ int32 manam = atoi((char*)args);
+
+ if (mana <= 0 || manam <= 0 || manam < mana)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_MANA, chr->GetName(), mana, manam);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, m_session->GetPlayer()->GetName(), mana, manam);
+
+ chr->SetMaxPower(POWER_MANA,manam );
+ chr->SetPower(POWER_MANA, mana );
+
+ return true;
+}
+
+//Edit Player Energy
+bool ChatHandler::HandleModifyEnergyCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+
+ int32 energy = atoi((char*)args)*10;
+ int32 energym = atoi((char*)args)*10;
+
+ if (energy <= 0 || energym <= 0 || energym < energy)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_ENERGY, chr->GetName(), energy/10, energym/10);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, m_session->GetPlayer()->GetName(), energy/10, energym/10);
+
+ chr->SetMaxPower(POWER_ENERGY,energym );
+ chr->SetPower(POWER_ENERGY, energy );
+
+ sLog.outDetail(GetMangosString(LANG_CURRENT_ENERGY),chr->GetMaxPower(POWER_ENERGY));
+
+ return true;
+}
+
+//Edit Player Rage
+bool ChatHandler::HandleModifyRageCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+
+ int32 rage = atoi((char*)args)*10;
+ int32 ragem = atoi((char*)args)*10;
+
+ if (rage <= 0 || ragem <= 0 || ragem < rage)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_RAGE, chr->GetName(), rage/10, ragem/10);
+ // Special case: I use GetMangosString here to get local of destination char ;)
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_RAGE_CHANGED), m_session->GetPlayer()->GetName(), rage/10, ragem/10);
+
+ chr->SetMaxPower(POWER_RAGE,ragem );
+ chr->SetPower(POWER_RAGE, rage );
+
+ return true;
+}
+
+//Edit Player Faction
+bool ChatHandler::HandleModifyFactionCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* pfactionid = extractKeyFromLink((char*)args,"Hfaction");
+
+ Creature* chr = getSelectedCreature();
+ if(!chr)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!pfactionid)
+ {
+ if(chr)
+ {
+ uint32 factionid = chr->getFaction();
+ uint32 flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
+ uint32 npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
+ uint32 dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
+ PSendSysMessage(LANG_CURRENT_FACTION,chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
+ }
+ return true;
+ }
+
+ if( !chr )
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 factionid = atoi(pfactionid);
+ uint32 flag;
+
+ char *pflag = strtok(NULL, " ");
+ if (!pflag)
+ flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
+ else
+ flag = atoi(pflag);
+
+ char* pnpcflag = strtok(NULL, " ");
+
+ uint32 npcflag;
+ if(!pnpcflag)
+ npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
+ else
+ npcflag = atoi(pnpcflag);
+
+ char* pdyflag = strtok(NULL, " ");
+
+ uint32 dyflag;
+ if(!pdyflag)
+ dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
+ else
+ dyflag = atoi(pdyflag);
+
+ if(!sFactionTemplateStore.LookupEntry(factionid))
+ {
+ PSendSysMessage(LANG_WRONG_FACTION, factionid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_FACTION, chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
+
+ //sprintf((char*)buf,"%s changed your Faction to %i.", m_session->GetPlayer()->GetName(), factionid);
+ //FillSystemMessageData(&data, m_session, buf);
+
+ //chr->GetSession()->SendPacket(&data);
+
+ chr->setFaction(factionid);
+ chr->SetUInt32Value(UNIT_FIELD_FLAGS,flag);
+ chr->SetUInt32Value(UNIT_NPC_FLAGS,npcflag);
+ chr->SetUInt32Value(UNIT_DYNAMIC_FLAGS,dyflag);
+
+ return true;
+}
+
+//Edit Player Spell
+bool ChatHandler::HandleModifySpellCommand(const char* args)
+{
+ if(!*args) return false;
+ char* pspellflatid = strtok((char*)args, " ");
+ if (!pspellflatid)
+ return false;
+
+ char* pop = strtok(NULL, " ");
+ if (!pop)
+ return false;
+
+ char* pval = strtok(NULL, " ");
+ if (!pval)
+ return false;
+
+ uint16 mark;
+
+ char* pmark = strtok(NULL, " ");
+
+ uint8 spellflatid = atoi(pspellflatid);
+ uint8 op = atoi(pop);
+ uint16 val = atoi(pval);
+ if(!pmark)
+ mark = 65535;
+ else
+ mark = atoi(pmark);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, m_session->GetPlayer()->GetName(), spellflatid, val, mark);
+
+ WorldPacket data(SMSG_SET_FLAT_SPELL_MODIFIER, (1+1+2+2));
+ data << uint8(spellflatid);
+ data << uint8(op);
+ data << uint16(val);
+ data << uint16(mark);
+ chr->GetSession()->SendPacket(&data);
+
+ return true;
+}
+
+//Edit Player TP
+bool ChatHandler::HandleModifyTalentCommand (const char* args)
+{
+ if (!*args)
+ return false;
+
+ int tp = atoi((char*)args);
+ if (tp>0)
+ {
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ player->SetFreeTalentPoints(tp);
+ return true;
+ }
+ return false;
+}
+
+//Enable On\OFF all taxi paths
+bool ChatHandler::HandleTaxiCheatCommand(const char* args)
+{
+ if (!*args)
+ {
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string argstr = (char*)args;
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ chr=m_session->GetPlayer();
+ }
+
+ if (argstr == "on")
+ {
+ chr->SetTaxiCheater(true);
+ PSendSysMessage(LANG_YOU_GIVE_TAXIS, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ // to send localized data to target
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_ADDED), m_session->GetPlayer()->GetName());
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ chr->SetTaxiCheater(false);
+ PSendSysMessage(LANG_YOU_REMOVE_TAXIS, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_REMOVED), m_session->GetPlayer()->GetName());
+
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+//Edit Player Aspeed
+bool ChatHandler::HandleModifyASpeedCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float ASpeed = (float)atof((char*)args);
+
+ if (ASpeed > 10 || ASpeed < 0.1)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ASPEED_CHANGED), m_session->GetPlayer()->GetName(), ASpeed);
+
+ chr->SetSpeed(MOVE_WALK, ASpeed,true);
+ chr->SetSpeed(MOVE_RUN, ASpeed,true);
+ chr->SetSpeed(MOVE_SWIM, ASpeed,true);
+ //chr->SetSpeed(MOVE_TURN, ASpeed,true);
+ chr->SetSpeed(MOVE_FLY, ASpeed,true);
+ return true;
+}
+
+//Edit Player Speed
+bool ChatHandler::HandleModifySpeedCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Speed = (float)atof((char*)args);
+
+ if (Speed > 10 || Speed < 0.1)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Speed);
+
+ chr->SetSpeed(MOVE_RUN,Speed,true);
+
+ return true;
+}
+
+//Edit Player Swim Speed
+bool ChatHandler::HandleModifySwimCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Swim = (float)atof((char*)args);
+
+ if (Swim > 10.0f || Swim < 0.01f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SWIM_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Swim);
+
+ chr->SetSpeed(MOVE_SWIM,Swim,true);
+
+ return true;
+}
+
+//Edit Player Walk Speed
+bool ChatHandler::HandleModifyBWalkCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float BSpeed = (float)atof((char*)args);
+
+ if (BSpeed > 10.0f || BSpeed < 0.1f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_BACK_SPEED_CHANGED), m_session->GetPlayer()->GetName(), BSpeed);
+
+ chr->SetSpeed(MOVE_WALKBACK,BSpeed,true);
+
+ return true;
+}
+
+//Edit Player Fly
+bool ChatHandler::HandleModifyFlyCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float FSpeed = (float)atof((char*)args);
+
+ if (FSpeed > 10.0f || FSpeed < 0.1f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_FLY_SPEED_CHANGED), m_session->GetPlayer()->GetName(), FSpeed);
+
+ chr->SetSpeed(MOVE_FLY,FSpeed,true);
+
+ return true;
+}
+
+//Edit Player Scale
+bool ChatHandler::HandleModifyScaleCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Scale = (float)atof((char*)args);
+ if (Scale > 3.0f || Scale <= 0.0f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SIZE_CHANGED), m_session->GetPlayer()->GetName(), Scale);
+
+ chr->SetFloatValue(OBJECT_FIELD_SCALE_X, Scale);
+
+ return true;
+}
+
+//Enable Player mount
+bool ChatHandler::HandleModifyMountCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ uint16 mId = 1147;
+ float speed = (float)15;
+ uint32 num = 0;
+
+ num = atoi((char*)args);
+ switch(num)
+ {
+ case 1:
+ mId=14340;
+ break;
+ case 2:
+ mId=4806;
+ break;
+ case 3:
+ mId=6471;
+ break;
+ case 4:
+ mId=12345;
+ break;
+ case 5:
+ mId=6472;
+ break;
+ case 6:
+ mId=6473;
+ break;
+ case 7:
+ mId=10670;
+ break;
+ case 8:
+ mId=10719;
+ break;
+ case 9:
+ mId=10671;
+ break;
+ case 10:
+ mId=10672;
+ break;
+ case 11:
+ mId=10720;
+ break;
+ case 12:
+ mId=14349;
+ break;
+ case 13:
+ mId=11641;
+ break;
+ case 14:
+ mId=12244;
+ break;
+ case 15:
+ mId=12242;
+ break;
+ case 16:
+ mId=14578;
+ break;
+ case 17:
+ mId=14579;
+ break;
+ case 18:
+ mId=14349;
+ break;
+ case 19:
+ mId=12245;
+ break;
+ case 20:
+ mId=14335;
+ break;
+ case 21:
+ mId=207;
+ break;
+ case 22:
+ mId=2328;
+ break;
+ case 23:
+ mId=2327;
+ break;
+ case 24:
+ mId=2326;
+ break;
+ case 25:
+ mId=14573;
+ break;
+ case 26:
+ mId=14574;
+ break;
+ case 27:
+ mId=14575;
+ break;
+ case 28:
+ mId=604;
+ break;
+ case 29:
+ mId=1166;
+ break;
+ case 30:
+ mId=2402;
+ break;
+ case 31:
+ mId=2410;
+ break;
+ case 32:
+ mId=2409;
+ break;
+ case 33:
+ mId=2408;
+ break;
+ case 34:
+ mId=2405;
+ break;
+ case 35:
+ mId=14337;
+ break;
+ case 36:
+ mId=6569;
+ break;
+ case 37:
+ mId=10661;
+ break;
+ case 38:
+ mId=10666;
+ break;
+ case 39:
+ mId=9473;
+ break;
+ case 40:
+ mId=9476;
+ break;
+ case 41:
+ mId=9474;
+ break;
+ case 42:
+ mId=14374;
+ break;
+ case 43:
+ mId=14376;
+ break;
+ case 44:
+ mId=14377;
+ break;
+ case 45:
+ mId=2404;
+ break;
+ case 46:
+ mId=2784;
+ break;
+ case 47:
+ mId=2787;
+ break;
+ case 48:
+ mId=2785;
+ break;
+ case 49:
+ mId=2736;
+ break;
+ case 50:
+ mId=2786;
+ break;
+ case 51:
+ mId=14347;
+ break;
+ case 52:
+ mId=14346;
+ break;
+ case 53:
+ mId=14576;
+ break;
+ case 54:
+ mId=9695;
+ break;
+ case 55:
+ mId=9991;
+ break;
+ case 56:
+ mId=6448;
+ break;
+ case 57:
+ mId=6444;
+ break;
+ case 58:
+ mId=6080;
+ break;
+ case 59:
+ mId=6447;
+ break;
+ case 60:
+ mId=4805;
+ break;
+ case 61:
+ mId=9714;
+ break;
+ case 62:
+ mId=6448;
+ break;
+ case 63:
+ mId=6442;
+ break;
+ case 64:
+ mId=14632;
+ break;
+ case 65:
+ mId=14332;
+ break;
+ case 66:
+ mId=14331;
+ break;
+ case 67:
+ mId=8469;
+ break;
+ case 68:
+ mId=2830;
+ break;
+ case 69:
+ mId=2346;
+ break;
+ default:
+ SendSysMessage(LANG_NO_MOUNT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_GIVE_MOUNT, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_MOUNT_GIVED), m_session->GetPlayer()->GetName());
+
+ chr->SetUInt32Value( UNIT_FIELD_FLAGS , 0x001000 );
+ chr->Mount(mId);
+
+ WorldPacket data( SMSG_FORCE_RUN_SPEED_CHANGE, (8+4+1+4) );
+ data.append(chr->GetPackGUID());
+ data << (uint32)0;
+ data << (uint8)0; //new 2.1.0
+ data << float(speed);
+ chr->SendMessageToSet( &data, true );
+
+ data.Initialize( SMSG_FORCE_SWIM_SPEED_CHANGE, (8+4+4) );
+ data.append(chr->GetPackGUID());
+ data << (uint32)0;
+ data << float(speed);
+ chr->SendMessageToSet( &data, true );
+
+ return true;
+}
+
+//Edit Player money
+bool ChatHandler::HandleModifyMoneyCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 addmoney = atoi((char*)args);
+
+ uint32 moneyuser = chr->GetMoney();
+
+ if(addmoney < 0)
+ {
+ int32 newmoney = moneyuser + addmoney;
+
+ sLog.outDetail(GetMangosString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney);
+ if(newmoney <= 0 )
+ {
+ PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ALL_MONEY_GONE), m_session->GetPlayer()->GetName());
+
+ chr->SetMoney(0);
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_TAKEN), m_session->GetPlayer()->GetName(), abs(addmoney));
+ chr->SetMoney( newmoney );
+ }
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_GIVEN), m_session->GetPlayer()->GetName(), addmoney);
+ chr->ModifyMoney( addmoney );
+ }
+
+ sLog.outDetail(GetMangosString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney() );
+
+ return true;
+}
+
+//Edit Player field
+bool ChatHandler::HandleModifyBitCommand(const char* args)
+{
+ if( !*args )
+ return false;
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* pField = strtok((char*)args, " ");
+ if (!pField)
+ return false;
+
+ char* pBit = strtok(NULL, " ");
+ if (!pBit)
+ return false;
+
+ uint16 field = atoi(pField);
+ uint32 bit = atoi(pBit);
+
+ if (field < 1 || field >= PLAYER_END)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (bit < 1 || bit > 32)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if ( chr->HasFlag( field, (1<<(bit-1)) ) )
+ {
+ chr->RemoveFlag( field, (1<<(bit-1)) );
+ PSendSysMessage(LANG_REMOVE_BIT, bit, field);
+ }
+ else
+ {
+ chr->SetFlag( field, (1<<(bit-1)) );
+ PSendSysMessage(LANG_SET_BIT, bit, field);
+ }
+
+ return true;
+}
+
+//Teleport by game_tele entry
+bool ChatHandler::HandleModifyHonorCommand (const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 amount = (uint32)atoi(args);
+
+ target->ModifyHonorPoints(amount);
+
+ PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, target->GetName(), target->GetHonorPoints());
+
+ return true;
+}
+
+bool ChatHandler::HandleTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* cId = extractKeyFromLink((char*)args,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r
+ if(!cId)
+ return false;
+
+ std::string name = cId;
+ WorldDatabase.escape_string(name);
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",name.c_str());
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+ delete result;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,x,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, ort);
+ return true;
+}
+
+bool ChatHandler::HandleLookupAreaCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ // Search in AreaTable.dbc
+ for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag)
+ {
+ AreaTableEntry const *areaEntry = sAreaStore.LookupEntry(areaflag);
+ if(areaEntry)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = areaEntry->area_name[loc];
+ if(name.empty())
+ continue;
+
+ if(!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = areaEntry->area_name[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send area in "id - [name]" format
+ std::ostringstream ss;
+ ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name << " " << localeNames[loc]<< "]|h|r";
+
+ SendSysMessage(ss.str().c_str());
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOAREAFOUND);
+ return true;
+}
+
+//Find tele in game_tele order by name
+bool ChatHandler::HandleLookupTeleCommand(const char * args)
+{
+ if(!*args)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_PARAMETER);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ char const* str = strtok((char*)args, " ");
+ if(!str)
+ return false;
+
+ std::string namepart = str;
+ WorldDatabase.escape_string(namepart);
+ QueryResult *result = WorldDatabase.PQuery("SELECT name FROM game_tele WHERE name "_LIKE_" '""%%%s%%""'",namepart.c_str());
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOREQUEST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ std::string reply;
+ for (uint64 i=0; i < result->GetRowCount(); i++)
+ {
+ Field *fields = result->Fetch();
+ reply += " |cffffffff|Htele:";
+ reply += fields[0].GetCppString();
+ reply += "|h[";
+ reply += fields[0].GetCppString();
+ reply += "]|h|r\n";
+ result->NextRow();
+ }
+ delete result;
+
+ if(reply.empty())
+ SendSysMessage(LANG_COMMAND_TELE_NOLOCATION);
+ else
+ {
+ reply = GetMangosString(LANG_COMMAND_TELE_LOCATION) + reply;
+ SendSysMessage(reply.c_str());
+ }
+ return true;
+}
+
+//Enable\Dissable accept whispers (for GM)
+bool ChatHandler::HandleWhispersCommand(const char* args)
+{
+ if(!*args)
+ {
+ PSendSysMessage(LANG_COMMAND_WHISPERACCEPTING, m_session->GetPlayer()->isAcceptWhispers() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+ // whisper on
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetAcceptWhispers(true);
+ SendSysMessage(LANG_COMMAND_WHISPERON);
+ return true;
+ }
+
+ // whisper off
+ if (argstr == "off")
+ {
+ m_session->GetPlayer()->SetAcceptWhispers(false);
+ SendSysMessage(LANG_COMMAND_WHISPEROFF);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+//Play sound
+bool ChatHandler::HandlePlaySoundCommand(const char* args)
+{
+ // USAGE: .debug playsound #soundid
+ // #soundid - ID decimal number from SoundEntries.dbc (1st column)
+ // this file have about 5000 sounds.
+ // In this realization only caller can hear this sound.
+ if( *args )
+ {
+ uint32 dwSoundId = atoi((char*)args);
+
+ if( !sSoundEntriesStore.LookupEntry(dwSoundId) )
+ {
+ PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8);
+ data << uint32(dwSoundId) << m_session->GetPlayer()->GetGUID();
+ m_session->SendPacket(&data);
+
+ PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId);
+ return true;
+ }
+
+ return false;
+}
+
+//Save all players in the world
+bool ChatHandler::HandleSaveAllCommand(const char* /*args*/)
+{
+ ObjectAccessor::Instance().SaveAllPlayers();
+ SendSysMessage(LANG_PLAYERS_SAVED);
+ return true;
+}
+
+//Send mail by command
+bool ChatHandler::HandleSendMailCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* pName = strtok((char*)args, " ");
+ char* msgSubject = strtok(NULL, " ");
+ char* msgText = strtok(NULL, "");
+
+ if (!msgText)
+ return false;
+
+ // pName, msgSubject, msgText isn't NUL after prev. check
+
+ std::string name = pName;
+ std::string subject = msgSubject;
+ std::string text = msgText;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name);
+
+ if(!receiver_guid)
+ return false;
+
+ uint32 mailId = objmgr.GenerateMailID();
+ uint32 sender_guidlo = m_session->GetPlayer()->GetGUIDLow();
+ uint32 messagetype = MAIL_NORMAL;
+ uint32 stationery = MAIL_STATIONERY_GM;
+ uint32 itemTextId = 0;
+ if (!text.empty())
+ {
+ itemTextId = objmgr.CreateItemText( text );
+ }
+
+ Player *receiver = objmgr.GetPlayer(receiver_guid);
+
+ WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_NONE);
+
+ PSendSysMessage(LANG_MAIL_SENT, name.c_str());
+ return true;
+}
+
+// teleport player to given game_tele.entry
+bool ChatHandler::HandleNameTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ char* pName = strtok((char*)args, " ");
+
+ if(!pName)
+ return false;
+
+ char* tail = strtok(NULL, "");
+ if(!tail)
+ return false;
+
+ char* cId = extractKeyFromLink((char*)tail,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r
+ if(!cId)
+ return false;
+
+ std::string location = cId;
+
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldDatabase.escape_string(location);
+ QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",location.c_str());
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+ delete result;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,x,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+
+ if(chr->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_TELEPORTING_TO, chr->GetName(),"", location.c_str());
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ chr->SaveRecallPosition();
+
+ chr->TeleportTo(mapid,x,y,z,chr->GetOrientation());
+ }
+ else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()))
+ {
+ PSendSysMessage(LANG_TELEPORTING_TO, name.c_str(), GetMangosString(LANG_OFFLINE), location.c_str());
+ Player::SavePositionInDB(mapid,x,y,z,ort,MapManager::Instance().GetZoneId(mapid,x,y),guid);
+ }
+ else
+ PSendSysMessage(LANG_NO_PLAYER, name.c_str());
+
+ return true;
+}
+
+//Teleport group to given game_tele.entry
+bool ChatHandler::HandleGroupTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ Player *player = getSelectedPlayer();
+ if (!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* cId = extractKeyFromLink((char*)args,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r
+ if(!cId)
+ return false;
+
+ std::string location = cId;
+
+ WorldDatabase.escape_string(location);
+ QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",location.c_str());
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+ delete result;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Group *grp = player->GetGroup();
+
+ if(!grp)
+ {
+ PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+
+ if(!pl || !pl->GetSession() )
+ continue;
+
+ if(pl->IsBeingTeleported())
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
+ continue;
+ }
+
+ PSendSysMessage(LANG_TELEPORTING_TO, pl->GetName(),"", location.c_str());
+
+ if (m_session->GetPlayer() != pl && m_session->GetPlayer()->IsVisibleGloballyFor(pl))
+ ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(pl->isInFlight())
+ {
+ pl->GetMotionMaster()->MovementExpired();
+ pl->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ pl->SaveRecallPosition();
+
+ pl->TeleportTo(mapid, x, y, z, ort);
+ }
+
+ return true;
+}
+
+//Summon group of player
+bool ChatHandler::HandleGroupgoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *player = objmgr.GetPlayer(name.c_str());
+ if (!player)
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Group *grp = player->GetGroup();
+
+ if(!grp)
+ {
+ PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* gmMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
+ bool to_instance = gmMap->Instanceable();
+
+ // we are in instance, and can summon only player in our group with us as lead
+ if ( to_instance && (
+ !m_session->GetPlayer()->GetGroup() || (grp->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
+ (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ) )
+ // the last check is a bit excessive, but let it be, just in case
+ {
+ SendSysMessage(LANG_CANNOT_SUMMON_TO_INST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+
+ if(!pl || pl==m_session->GetPlayer() || !pl->GetSession() )
+ continue;
+
+ if(pl->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (to_instance)
+ {
+ Map* plMap = MapManager::Instance().GetMap(pl->GetMapId(),pl);
+
+ if ( plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId() )
+ {
+ // cannot summon from instance to instance
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,pl->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ PSendSysMessage(LANG_SUMMONING, pl->GetName(),"");
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(pl))
+ ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(pl->isInFlight())
+ {
+ pl->GetMotionMaster()->MovementExpired();
+ pl->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ pl->SaveRecallPosition();
+
+ // before GM
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,pl->GetObjectSize());
+ pl->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,pl->GetOrientation());
+ }
+
+ return true;
+}
+
+//teleport at coordinates
+bool ChatHandler::HandleGoXYCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else mapid = _player->GetMapId();
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ Map const *map = MapManager::Instance().GetBaseMap(mapid);
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport at coordinates, including Z
+bool ChatHandler::HandleGoXYZCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pz = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py || !pz)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ float z = (float)atof(pz);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else
+ mapid = _player->GetMapId();
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport at coordinates
+bool ChatHandler::HandleGoZoneXYCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* tail = strtok(NULL,"");
+
+ char* cAreaId = extractKeyFromLink(tail,"Harea"); // string or [name] Shift-click form |color|Harea:area_id|h[name]|h|r
+
+ if (!px || !py)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ uint32 areaid = cAreaId ? (uint32)atoi(cAreaId) : _player->GetZoneId();
+
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaid);
+
+ if( x<0 || x>100 || y<0 || y>100 || !areaEntry )
+ {
+ PSendSysMessage(LANG_INVALID_ZONE_COORD,x,y,areaid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // update to parent zone if exist (client map show only zones without parents)
+ AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry;
+
+ Map const *map = MapManager::Instance().GetBaseMap(zoneEntry->mapid);
+
+ if(map->Instanceable())
+ {
+ PSendSysMessage(LANG_INVALID_ZONE_MAP,areaEntry->ID,areaEntry->area_name[m_session->GetSessionDbcLocale()],map->GetId(),map->GetMapName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Zone2MapCoordinates(x,y,zoneEntry->ID);
+
+ if(!MapManager::IsValidMapCoord(zoneEntry->mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,zoneEntry->mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+ _player->TeleportTo(zoneEntry->mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport to grid
+bool ChatHandler::HandleGoGridCommand(const char* args)
+{
+ if(!*args) return false;
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ float grid_x = (float)atof(px);
+ float grid_y = (float)atof(py);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else mapid = _player->GetMapId();
+
+ // center of grid
+ float x = (grid_x-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
+ float y = (grid_y-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ Map const *map = MapManager::Instance().GetBaseMap(mapid);
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+bool ChatHandler::HandleDrunkCommand(const char* args)
+{
+ if(!*args) return false;
+
+ uint32 drunklevel = (uint32)atoi(args);
+ if(drunklevel > 100)
+ drunklevel = 100;
+
+ uint16 drunkMod = drunklevel * 0xFFFF / 100;
+
+ m_session->GetPlayer()->SetDrunkValue(drunkMod);
+
+ return true;
+}
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp
new file mode 100644
index 00000000000..63ad2a7a0b9
--- /dev/null
+++ b/src/game/Level2.cpp
@@ -0,0 +1,3948 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Item.h"
+#include "GameObject.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "Language.h"
+#include "World.h"
+#include "GameEvent.h"
+#include "SpellMgr.h"
+#include "WaypointManager.h"
+#include "Util.h"
+#include <cctype>
+#include <iostream>
+#include <fstream>
+#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
+};
+
+//mute player for some times
+bool ChatHandler::HandleMuteCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char *charname = strtok((char*)args, " ");
+ if (!charname)
+ return false;
+
+ std::string cname = charname;
+
+ char *timetonotspeak = strtok(NULL, " ");
+ if(!timetonotspeak)
+ return false;
+
+ uint32 notspeaktime = (uint32) atoi(timetonotspeak);
+
+ if(!normalizePlayerName(cname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
+ if(!guid)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(guid);
+
+ // check security
+ uint32 account_id = 0;
+ uint32 security = 0;
+
+ if (chr)
+ {
+ account_id = chr->GetSession()->GetAccountId();
+ security = chr->GetSession()->GetSecurity();
+ }
+ else
+ {
+ account_id = objmgr.GetPlayerAccountIdByGUID(guid);
+ security = objmgr.GetSecurityByAccount(account_id);
+ }
+
+ if(security >= m_session->GetSecurity())
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ time_t mutetime = time(NULL) + notspeaktime*60;
+
+ if (chr)
+ chr->GetSession()->m_muteTime = mutetime;
+
+ loginDatabase.PExecute("UPDATE account SET mutetime = " I64FMTD " WHERE id = '%u'",uint64(mutetime), account_id );
+
+ if(chr)
+ ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime);
+
+ PSendSysMessage(LANG_YOU_DISABLE_CHAT, cname.c_str(), notspeaktime);
+
+ return true;
+}
+
+//unmute player
+bool ChatHandler::HandleUnmuteCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char *charname = strtok((char*)args, " ");
+ if (!charname)
+ return false;
+
+ std::string cname = charname;
+
+ if(!normalizePlayerName(cname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
+ if(!guid)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(guid);
+
+ // check security
+ uint32 account_id = 0;
+ uint32 security = 0;
+
+ if (chr)
+ {
+ account_id = chr->GetSession()->GetAccountId();
+ security = chr->GetSession()->GetSecurity();
+ }
+ else
+ {
+ account_id = objmgr.GetPlayerAccountIdByGUID(guid);
+ security = objmgr.GetSecurityByAccount(account_id);
+ }
+
+ if(security >= m_session->GetSecurity())
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (chr)
+ {
+ if(chr->CanSpeak())
+ {
+ SendSysMessage(LANG_CHAT_ALREADY_ENABLED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr->GetSession()->m_muteTime = 0;
+ }
+
+ loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id );
+
+ if(chr)
+ ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_ENABLED);
+
+ PSendSysMessage(LANG_YOU_ENABLE_CHAT, cname.c_str());
+ return true;
+}
+
+bool ChatHandler::HandleTargetObjectCommand(const char* args)
+{
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+ GameEvent::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList();
+ if(*args)
+ {
+ int32 id = atoi((char*)args);
+ if(id)
+ result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id);
+ else
+ {
+ std::string name = args;
+ WorldDatabase.escape_string(name);
+ result = WorldDatabase.PQuery(
+ "SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ "
+ "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" '""%%%s%%""' ORDER BY order_ ASC LIMIT 1",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str());
+ }
+ }
+ else
+ {
+ std::ostringstream eventFilter;
+ eventFilter << " AND (event IS NULL ";
+ bool initString = true;
+
+ for (GameEvent::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
+ {
+ if (initString)
+ {
+ eventFilter << "OR event IN (" <<*itr;
+ initString =false;
+ }
+ else
+ eventFilter << "," << *itr;
+ }
+
+ if (!initString)
+ eventFilter << "))";
+ else
+ eventFilter << ")";
+
+ result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, "
+ "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject "
+ "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 1",
+ m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str());
+ }
+
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND);
+ return true;
+ }
+
+ Field *fields = result->Fetch();
+ uint32 lowguid = fields[0].GetUInt32();
+ uint32 id = fields[1].GetUInt32();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
+ float o = fields[5].GetFloat();
+ int mapid = fields[6].GetUInt16();
+ delete result;
+
+ GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id);
+
+ if (!goI)
+ {
+ PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
+ return false;
+ }
+
+ GameObject* target = ObjectAccessor::GetGameObject(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT));
+
+ PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o);
+
+ if(target)
+ {
+ int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
+ if(curRespawnDelay < 0)
+ curRespawnDelay = 0;
+
+ std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
+ std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
+
+ PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
+ }
+ return true;
+}
+
+//teleport to gameobject
+bool ChatHandler::HandleGoObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ int32 guid = atoi(cId);
+ if(!guid)
+ return false;
+
+ float x, y, z, ort;
+ int mapid;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(guid))
+ {
+ x = go_data->posX;
+ y = go_data->posY;
+ z = go_data->posZ;
+ ort = go_data->orientation;
+ mapid = go_data->mapid;
+ }
+ else
+ {
+ SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, ort);
+ return true;
+}
+
+bool ChatHandler::HandleGoTriggerCommand(const char* args)
+{
+ Player* _player = m_session->GetPlayer();
+
+ if (!*args)
+ return false;
+
+ char *atId = strtok((char*)args, " ");
+ if (!atId)
+ return false;
+
+ int32 i_atId = atoi(atId);
+
+ if(!i_atId)
+ return false;
+
+ AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(i_atId);
+ if (!at)
+ {
+ PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND,i_atId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(at->mapid,at->x,at->y,at->z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,at->x,at->y,at->mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(at->mapid, at->x, at->y, at->z, _player->GetOrientation());
+ return true;
+}
+
+bool ChatHandler::HandleGoGraveyardCommand(const char* args)
+{
+ Player* _player = m_session->GetPlayer();
+
+ if (!*args)
+ return false;
+
+ char *gyId = strtok((char*)args, " ");
+ if (!gyId)
+ return false;
+
+ int32 i_gyId = atoi(gyId);
+
+ if(!i_gyId)
+ return false;
+
+ WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(i_gyId);
+ if (!gy)
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST,i_gyId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(gy->map_id,gy->x,gy->y,gy->z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,gy->x,gy->y,gy->map_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(gy->map_id, gy->x, gy->y, gy->z, _player->GetOrientation());
+ return true;
+}
+
+/** \brief Teleport the GM to the specified creature
+ *
+ * .gocreature <GUID> --> TP using creature.guid
+ * .gocreature azuregos --> TP player to the mob with this name
+ * Warning: If there is more than one mob with this name
+ * you will be teleported to the first one that is found.
+ * .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry
+ * Warning: If there is more than one mob with this "id"
+ * you will be teleported to the first one that is found.
+ */
+//teleport to creature
+bool ChatHandler::HandleGoCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ Player* _player = m_session->GetPlayer();
+
+ // "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* pParam1 = extractKeyFromLink((char*)args,"Hcreature");
+ if (!pParam1)
+ return false;
+
+ std::ostringstream whereClause;
+
+ // User wants to teleport to the NPC's template entry
+ if( strcmp(pParam1, "id") == 0 )
+ {
+ //sLog.outError("DEBUG: ID found");
+
+ // Get the "creature_template.entry"
+ // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* tail = strtok(NULL,"");
+ if(!tail)
+ return false;
+ char* cId = extractKeyFromLink(tail,"Hcreature_entry");
+ if(!cId)
+ return false;
+
+ int32 tEntry = atoi(cId);
+ //sLog.outError("DEBUG: ID value: %d", tEntry);
+ if(!tEntry)
+ return false;
+
+ whereClause << "WHERE id = '" << tEntry << "'";
+ }
+ else
+ {
+ //sLog.outError("DEBUG: ID *not found*");
+
+ int32 guid = atoi(pParam1);
+
+ // Number is invalid - maybe the user specified the mob's name
+ if(!guid)
+ {
+ std::string name = pParam1;
+ WorldDatabase.escape_string(name);
+ whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name "_LIKE_" '" << name << "'";
+ }
+ else
+ {
+ whereClause << "WHERE guid = '" << guid << "'";
+ }
+ }
+ //sLog.outError("DEBUG: %s", whereClause.c_str());
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM creature %s", whereClause.str().c_str() );
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ if( result->GetRowCount() > 1 )
+ {
+ SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
+ }
+
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ delete result;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, ort);
+ return true;
+}
+
+bool ChatHandler::HandleGUIDCommand(const char* /*args*/)
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid));
+ return true;
+}
+
+bool ChatHandler::HandleLookupFactionCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if (!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ 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++)
+ //for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(id);
+ //FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
+ if (factionEntry)
+ {
+ FactionStateList::const_iterator repItr = target->m_factions.find(factionEntry->reputationListID);
+
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = factionEntry->name[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->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;
+ ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r";
+
+ if (repItr != target->m_factions.end())
+ {
+ ReputationRank rank = target->GetReputationRank(factionEntry);
+ std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
+
+ ss << " " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
+
+ if(repItr->second.Flags & FACTION_FLAG_VISIBLE)
+ ss << GetMangosString(LANG_FACTION_VISIBLE);
+ if(repItr->second.Flags & FACTION_FLAG_AT_WAR)
+ ss << GetMangosString(LANG_FACTION_ATWAR);
+ if(repItr->second.Flags & FACTION_FLAG_PEACE_FORCED)
+ ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
+ if(repItr->second.Flags & FACTION_FLAG_HIDDEN)
+ ss << GetMangosString(LANG_FACTION_HIDDEN);
+ if(repItr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
+ ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
+ if(repItr->second.Flags & FACTION_FLAG_INACTIVE)
+ ss << GetMangosString(LANG_FACTION_INACTIVE);
+ }
+ else
+ ss << GetMangosString(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;
+
+ Player* target = NULL;
+ target = getSelectedPlayer();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* factionTxt = extractKeyFromLink((char*)args,"Hfaction");
+ if(!factionTxt)
+ return false;
+
+ uint32 factionId = atoi(factionTxt);
+
+ int32 amount = 0;
+ char *rankTxt = strtok(NULL, " ");
+ if (!factionTxt || !rankTxt)
+ return false;
+
+ amount = atoi(rankTxt);
+ if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0]))
+ {
+ std::string rankStr = rankTxt;
+ std::wstring wrankStr;
+ if(!Utf8toWStr(rankStr,wrankStr))
+ return false;
+ wstrToLower( wrankStr );
+
+ int r = 0;
+ amount = -42000;
+ for (; r < MAX_REPUTATION_RANK; ++r)
+ {
+ std::string rank = GetMangosString(ReputationRankStrIndex[r]);
+ if(rank.empty())
+ continue;
+
+ std::wstring wrank;
+ if(!Utf8toWStr(rank,wrank))
+ continue;
+
+ wstrToLower(wrank);
+
+ if(wrank.substr(0,wrankStr.size())==wrankStr)
+ {
+ char *deltaTxt = strtok(NULL, " ");
+ if (deltaTxt)
+ {
+ int32 delta = atoi(deltaTxt);
+ if ((delta < 0) || (delta > Player::ReputationRank_Length[r] -1))
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (Player::ReputationRank_Length[r]-1));
+ SetSentErrorMessage(true);
+ return false;
+ }
+ amount += delta;
+ }
+ break;
+ }
+ amount += Player::ReputationRank_Length[r];
+ }
+ if (r >= MAX_REPUTATION_RANK)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
+
+ if (!factionEntry)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (factionEntry->reputationListID < 0)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[m_session->GetSessionDbcLocale()], factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->SetFactionReputation(factionEntry,amount);
+ PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[m_session->GetSessionDbcLocale()], factionId, target->GetName(), target->GetReputation(factionId));
+ return true;
+}
+
+bool ChatHandler::HandleNameCommand(const char* args)
+{
+ /* Temp. disabled
+ if(!*args)
+ return false;
+
+ if(strlen((char*)args)>75)
+ {
+ PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75);
+ return true;
+ }
+
+ for (uint8 i = 0; i < strlen(args); i++)
+ {
+ if(!isalpha(args[i]) && args[i]!=' ')
+ {
+ SendSysMessage(LANG_CHARS_ONLY);
+ return false;
+ }
+ }
+
+ uint64 guid;
+ guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ pCreature->SetName(args);
+ uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName());
+ pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
+
+ pCreature->SaveToDB();
+ */
+
+ return true;
+}
+
+bool ChatHandler::HandleSubNameCommand(const char* /*args*/)
+{
+ /* Temp. disabled
+
+ if(!*args)
+ args = "";
+
+ if(strlen((char*)args)>75)
+ {
+
+ PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75);
+ return true;
+ }
+
+ for (uint8 i = 0; i < strlen(args); i++)
+ {
+ if(!isalpha(args[i]) && args[i]!=' ')
+ {
+ SendSysMessage(LANG_CHARS_ONLY);
+ return false;
+ }
+ }
+ uint64 guid;
+ guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID));
+ pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
+
+ pCreature->SaveToDB();
+ */
+ return true;
+}
+
+//move item to other slot
+bool ChatHandler::HandleItemMoveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ uint8 srcslot, dstslot;
+
+ char* pParam1 = strtok((char*)args, " ");
+ if (!pParam1)
+ return false;
+
+ char* pParam2 = strtok(NULL, " ");
+ if (!pParam2)
+ return false;
+
+ srcslot = (uint8)atoi(pParam1);
+ dstslot = (uint8)atoi(pParam2);
+
+ uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
+ uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
+
+ if(srcslot==dstslot)
+ return true;
+
+ m_session->GetPlayer()->SwapItem( src, dst );
+
+ return true;
+}
+
+//add spawn of creature
+bool ChatHandler::HandleAddSpwCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ char* charID = strtok((char*)args, " ");
+ if (!charID)
+ return false;
+
+ char* team = strtok(NULL, " ");
+ int32 teamval = 0;
+ if (team) { teamval = atoi(team); }
+ if (teamval < 0) { teamval = 0; }
+
+ uint32 id = atoi(charID);
+
+ Player *chr = m_session->GetPlayer();
+ float x = chr->GetPositionX();
+ float y = chr->GetPositionY();
+ float z = chr->GetPositionZ();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, (uint32)teamval))
+ {
+ delete pCreature;
+ return false;
+ }
+
+ pCreature->Relocate(x,y,z,o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+
+ uint32 db_guid = pCreature->GetDBTableGUIDLow();
+
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ pCreature->LoadFromDB(db_guid, map);
+
+ map->Add(pCreature);
+ objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid));
+ return true;
+}
+
+bool ChatHandler::HandleDelCreatureCommand(const char* args)
+{
+ Creature* unit = NULL;
+
+ if(*args)
+ {
+ // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid))
+ unit = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT));
+ }
+ else
+ unit = getSelectedCreature();
+
+ if(!unit || unit->isPet() || unit->isTotem())
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // Delete the creature
+ unit->CombatStop();
+ unit->DeleteFromDB();
+ unit->CleanupsBeforeDelete();
+ unit->AddObjectToRemoveList();
+
+ SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
+
+ return true;
+}
+
+//delete object by selection or guid
+bool ChatHandler::HandleDelObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 owner_guid = obj->GetOwnerGUID();
+ if(owner_guid)
+ {
+ Unit* owner = ObjectAccessor::GetUnit(*m_session->GetPlayer(),owner_guid);
+ if(!owner && !IS_PLAYER_GUID(owner_guid))
+ {
+ PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, GUID_LOPART(owner_guid), obj->GetGUIDLow());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ owner->RemoveGameObject(obj,false);
+ }
+
+ obj->SetRespawnTime(0); // not save respawn time
+ obj->Delete();
+ obj->DeleteFromDB();
+
+ PSendSysMessage(LANG_COMMAND_DELOBJMESSAGE, obj->GetGUIDLow());
+
+ return true;
+}
+
+//turn selected object
+bool ChatHandler::HandleTurnObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* po = strtok(NULL, " ");
+ float o;
+
+ if (po)
+ {
+ o = (float)atof(po);
+ }
+ else
+ {
+ Player *chr = m_session->GetPlayer();
+ o = chr->GetOrientation();
+ }
+
+ float rot2 = sin(o/2);
+ float rot3 = cos(o/2);
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o);
+
+ obj->SetFloatValue(GAMEOBJECT_FACING, o);
+ obj->SetFloatValue(GAMEOBJECT_ROTATION+2, rot2);
+ obj->SetFloatValue(GAMEOBJECT_ROTATION+3, rot3);
+
+ map->Add(obj);
+
+ obj->SaveToDB();
+ obj->Refresh();
+
+ PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), o);
+
+ return true;
+}
+
+//move selected creature
+bool ChatHandler::HandleMoveCreatureCommand(const char* args)
+{
+ uint32 lowguid = 0;
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+
+ /* FIXME: impossibel without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // Attempting creature load from DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 map_id = data->mapid;
+
+ if(m_session->GetPlayer()->GetMapId()!=map_id)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+
+ float x = m_session->GetPlayer()->GetPositionX();
+ float y = m_session->GetPlayer()->GetPositionY();
+ float z = m_session->GetPlayer()->GetPositionZ();
+ float o = m_session->GetPlayer()->GetOrientation();
+
+ if (pCreature)
+ {
+ if(CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow()))
+ {
+ const_cast<CreatureData*>(data)->posX = x;
+ const_cast<CreatureData*>(data)->posY = y;
+ const_cast<CreatureData*>(data)->posZ = z;
+ const_cast<CreatureData*>(data)->orientation = o;
+ }
+ MapManager::Instance().GetMap(pCreature->GetMapId(),pCreature)->CreatureRelocation(pCreature,x, y, z,o);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ }
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid);
+ PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
+ return true;
+}
+
+//move selected object
+bool ChatHandler::HandleMoveObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* px = strtok(NULL, " ");
+ char* py = strtok(NULL, " ");
+ char* pz = strtok(NULL, " ");
+
+ if (!px)
+ {
+ Player *chr = m_session->GetPlayer();
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), obj->GetOrientation());
+ obj->SetFloatValue(GAMEOBJECT_POS_X, chr->GetPositionX());
+ obj->SetFloatValue(GAMEOBJECT_POS_Y, chr->GetPositionY());
+ obj->SetFloatValue(GAMEOBJECT_POS_Z, chr->GetPositionZ());
+
+ map->Add(obj);
+ }
+ else
+ {
+ if(!py || !pz)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ float z = (float)atof(pz);
+
+ if(!MapManager::IsValidMapCoord(obj->GetMapId(),x,y,z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,obj->GetMapId());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(x, y, z, obj->GetOrientation());
+ obj->SetFloatValue(GAMEOBJECT_POS_X, x);
+ obj->SetFloatValue(GAMEOBJECT_POS_Y, y);
+ obj->SetFloatValue(GAMEOBJECT_POS_Z, z);
+
+ map->Add(obj);
+ }
+
+ obj->SaveToDB();
+ obj->Refresh();
+
+ PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow());
+
+ return true;
+}
+
+//demorph player or unit
+bool ChatHandler::HandleDeMorphCommand(const char* /*args*/)
+{
+ Unit *target = getSelectedUnit();
+ if(!target)
+ target = m_session->GetPlayer();
+
+ target->DeMorph();
+
+ return true;
+}
+
+//add item in vendorlist
+bool ChatHandler::HandleAddVendorItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Creature* vendor = getSelectedCreature();
+ if (!vendor || !vendor->isVendor())
+ {
+ SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* pitem = extractKeyFromLink((char*)args,"Hitem");
+ if (!pitem)
+ {
+ SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ uint32 itemId = atol(pitem);
+
+ char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
+ uint32 maxcount = 0;
+ if (fmaxcount)
+ maxcount = atol(fmaxcount);
+
+ char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
+ uint32 incrtime = 0;
+ if (fincrtime)
+ incrtime = atol(fincrtime);
+
+ char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
+ uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
+ if(!pProto)
+ {
+ PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(extendedcost && !sItemExtendedCostStore.LookupEntry(extendedcost))
+ {
+ PSendSysMessage(LANG_BAD_VALUE, extendedcost);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // load vendor items if not yet
+ vendor->LoadGoods();
+
+ if(vendor->FindItem(itemId))
+ {
+ PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (vendor->GetItemCount() >= MAX_VENDOR_ITEMS)
+ {
+ SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // add to DB and to current ingame vendor
+ WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",vendor->GetEntry(), itemId, maxcount,incrtime,extendedcost);
+ vendor->AddItem(itemId,maxcount,incrtime,extendedcost);
+ PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost);
+ return true;
+}
+
+//del item from vendor list
+bool ChatHandler::HandleDelVendorItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Creature* vendor = getSelectedCreature();
+ if (!vendor || !vendor->isVendor())
+ {
+ SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* pitem = extractKeyFromLink((char*)args,"Hitem");
+ if (!pitem)
+ {
+ SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ uint32 itemId = atol(pitem);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
+ if(!pProto)
+ {
+ PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // load vendor items if not yet
+ vendor->LoadGoods();
+
+ if (!vendor->RemoveItem(itemId))
+ {
+ PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",vendor->GetEntry(), itemId);
+ PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
+ return true;
+}
+
+//add move for creature
+bool ChatHandler::HandleAddMoveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* guid_str = strtok((char*)args, " ");
+ char* wait_str = strtok((char*)NULL, " ");
+
+ uint32 lowguid = atoi((char*)guid_str);
+
+ Creature* pCreature = NULL;
+
+ /* FIXME: impossible without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // attempt check creature existence by DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ // obtain real GUID for DB operations
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+
+ int wait = wait_str ? atoi(wait_str) : 0;
+
+ if(wait < 0)
+ wait = 0;
+
+ Player* player = m_session->GetPlayer();
+
+ WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0);
+
+ // update movement type
+ WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
+ if(pCreature)
+ {
+ pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ pCreature->SaveToDB();
+ }
+
+ SendSysMessage(LANG_WAYPOINT_ADDED);
+
+ return true;
+}
+
+/**
+ * Set the movement type for an NPC.<br/>
+ * <br/>
+ * Valid movement types are:
+ * <ul>
+ * <li> stay - NPC wont move </li>
+ * <li> random - NPC will move randomly according to the spawndist </li>
+ * <li> way - NPC will move with given waypoints set </li>
+ * </ul>
+ * additional parameter: NODEL - so no waypoints are deleted, if you
+ * change the movement type
+ */
+bool ChatHandler::HandleSetMoveTypeCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // 3 arguments:
+ // GUID (optional - you can also select the creature)
+ // stay|random|way (determines the kind of movement)
+ // NODEL (optional - tells the system NOT to delete any waypoints)
+ // this is very handy if you want to do waypoints, that are
+ // later switched on/off according to special events (like escort
+ // quests, etc)
+ char* guid_str = strtok((char*)args, " ");
+ char* type_str = strtok((char*)NULL, " ");
+ char* dontdel_str = strtok((char*)NULL, " ");
+
+ bool doNotDelete = false;
+
+ if(!guid_str)
+ return false;
+
+ uint32 lowguid = 0;
+ Creature* pCreature = NULL;
+
+ if( dontdel_str )
+ {
+ //sLog.outError("DEBUG: All 3 params are set");
+
+ // All 3 params are set
+ // GUID
+ // type
+ // doNotDEL
+ if( stricmp( dontdel_str, "NODEL" ) == 0 )
+ {
+ //sLog.outError("DEBUG: doNotDelete = true;");
+ doNotDelete = true;
+ }
+ }
+ else
+ {
+ // Only 2 params - but maybe NODEL is set
+ if( type_str )
+ {
+ sLog.outError("DEBUG: Only 2 params ");
+ if( stricmp( type_str, "NODEL" ) == 0 )
+ {
+ //sLog.outError("DEBUG: type_str, NODEL ");
+ doNotDelete = true;
+ type_str = NULL;
+ }
+ }
+ }
+
+ if(!type_str) // case .setmovetype $move_type (with selected creature)
+ {
+ type_str = guid_str;
+ pCreature = getSelectedCreature();
+ if(!pCreature)
+ return false;
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ else // case .setmovetype #creature_guid $move_type (with selected creature)
+ {
+ lowguid = atoi((char*)guid_str);
+
+ /* impossible without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // attempt check creature existence by DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ }
+
+ // now lowguid is low guid really existed creature
+ // and pCreature point (maybe) to this creature or NULL
+
+ MovementGeneratorType move_type;
+
+ std::string type = type_str;
+
+ if(type == "stay")
+ move_type = IDLE_MOTION_TYPE;
+ else if(type == "random")
+ move_type = RANDOM_MOTION_TYPE;
+ else if(type == "way")
+ move_type = WAYPOINT_MOTION_TYPE;
+ else
+ return false;
+
+ // update movement type
+ if(doNotDelete == false)
+ WaypointMgr.DeletePath(lowguid);
+
+ if(pCreature)
+ {
+ pCreature->SetDefaultMovementType(move_type);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ pCreature->SaveToDB();
+ }
+ if( doNotDelete == false )
+ {
+ PSendSysMessage(LANG_MOVE_TYPE_SET,type_str);
+ }
+ else
+ {
+ PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL,type_str);
+ }
+
+ return true;
+} // HandleSetMoveTypeCommand
+
+//change level of creature or pet
+bool ChatHandler::HandleChangeLevelCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint8 lvl = (uint8) atoi((char*)args);
+ if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(pCreature->isPet())
+ {
+ ((Pet*)pCreature)->GivePetLevel(lvl);
+ }
+ else
+ {
+ pCreature->SetMaxHealth( 100 + 30*lvl);
+ pCreature->SetHealth( 100 + 30*lvl);
+ pCreature->SetLevel( lvl);
+ pCreature->SaveToDB();
+ }
+
+ return true;
+}
+
+//set npcflag of creature
+bool ChatHandler::HandleNPCFlagCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 npcFlags = (uint32) atoi((char*)args);
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags);
+
+ WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry());
+
+ SendSysMessage(LANG_VALUE_SAVED_REJOIN);
+
+ return true;
+}
+
+//set model of creature
+bool ChatHandler::HandleSetModelCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 displayId = (uint32) atoi((char*)args);
+
+ Creature *pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->SetDisplayId(displayId);
+ pCreature->SetNativeDisplayId(displayId);
+
+ pCreature->SaveToDB();
+
+ return true;
+}
+
+//morph creature or player
+bool ChatHandler::HandleMorphCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint16 display_id = (uint16)atoi((char*)args);
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ target = m_session->GetPlayer();
+
+ target->SetDisplayId(display_id);
+
+ return true;
+}
+
+//set faction of creature or go
+bool ChatHandler::HandleFactionIdCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 factionId = (uint32) atoi((char*)args);
+
+ if (!sFactionTemplateStore.LookupEntry(factionId))
+ {
+ PSendSysMessage(LANG_WRONG_FACTION, factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->setFaction(factionId);
+
+ // faction is set in creature_template - not inside creature
+
+ // update in memory
+ if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo())
+ {
+ const_cast<CreatureInfo*>(cinfo)->faction_A = factionId;
+ const_cast<CreatureInfo*>(cinfo)->faction_H = factionId;
+ }
+
+ // and DB
+ WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry());
+
+ return true;
+}
+
+//kick player
+bool ChatHandler::HandleKickPlayerCommand(const char *args)
+{
+ char* kickName = strtok((char*)args, " ");
+ if (!kickName)
+ {
+ Player* player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player==m_session->GetPlayer())
+ {
+ SendSysMessage(LANG_COMMAND_KICKSELF);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player->GetSession()->KickPlayer();
+ }
+ else
+ {
+ std::string name = kickName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(name==m_session->GetPlayer()->GetName())
+ {
+ SendSysMessage(LANG_COMMAND_KICKSELF);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(sWorld.KickPlayer(name))
+ {
+ PSendSysMessage(LANG_COMMAND_KICKMESSAGE,name.c_str());
+ }
+ else
+ PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER,name.c_str());
+ }
+
+ return true;
+}
+
+//show info of player
+bool ChatHandler::HandlePInfoCommand(const char* args)
+{
+ Player* target = NULL;
+ uint64 targetGUID = 0;
+
+ char* px = strtok((char*)args, " ");
+ char* py = NULL;
+
+ std::string name;
+
+ if (px)
+ {
+ name = px;
+
+ if(name.empty())
+ return false;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = objmgr.GetPlayer(name.c_str());
+ if (target)
+ py = strtok(NULL, " ");
+ else
+ {
+ targetGUID = objmgr.GetPlayerGUIDByName(name);
+ if(targetGUID)
+ py = strtok(NULL, " ");
+ else
+ py = px;
+ }
+ }
+
+ if(!target && !targetGUID)
+ {
+ target = getSelectedPlayer();
+ }
+
+ if(!target && !targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 accId = 0;
+ uint32 money = 0;
+ uint32 total_player_time = 0;
+ uint32 level = 0;
+ uint32 latency = 0;
+
+ // get additional information from Player object
+ if(target)
+ {
+ targetGUID = target->GetGUID();
+ name = target->GetName(); // re-read for case getSelectedPlayer() target
+ accId = target->GetSession()->GetAccountId();
+ money = target->GetMoney();
+ total_player_time = target->GetTotalPlayedTime();
+ level = target->getLevel();
+ latency = target->GetSession()->GetLatency();
+ }
+ // get additional information from DB
+ else
+ {
+ accId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
+ Player plr(m_session); // use current session for temporary load
+ plr.MinimalLoadFromDB(NULL, targetGUID);
+ money = plr.GetMoney();
+ total_player_time = plr.GetTotalPlayedTime();
+ level = plr.getLevel();
+ }
+
+ std::string username = GetMangosString(LANG_ERROR);
+ std::string last_ip = GetMangosString(LANG_ERROR);
+ uint32 security = 0;
+ std::string last_login = GetMangosString(LANG_ERROR);
+
+ QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId);
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ username = fields[0].GetCppString();
+ security = fields[1].GetUInt32();
+ if(m_session->GetSecurity() >= security)
+ {
+ last_ip = fields[2].GetCppString();
+ last_login = fields[3].GetCppString();
+ }
+ else
+ {
+ last_ip = "-";
+ last_login = "-";
+ }
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetMangosString(LANG_OFFLINE)), name.c_str(), GUID_LOPART(targetGUID), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency);
+
+ std::string timeStr = secsToTimeString(total_player_time,true,true);
+ uint32 gold = money /GOLD;
+ uint32 silv = (money % GOLD) / SILVER;
+ uint32 copp = (money % GOLD) % SILVER;
+ PSendSysMessage(LANG_PINFO_LEVEL, timeStr.c_str(), level, gold,silv,copp );
+
+ if ( py && strncmp(py, "rep", 3) == 0 )
+ {
+ if(!target)
+ {
+ // rep option not implemented for offline case
+ SendSysMessage(LANG_PINFO_NO_REP);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* FactionName;
+ for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
+ if (factionEntry)
+ FactionName = factionEntry->name[m_session->GetSessionDbcLocale()];
+ else
+ FactionName = "#Not found#";
+ ReputationRank rank = target->GetReputationRank(factionEntry);
+ std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
+ std::ostringstream ss;
+ ss << itr->second.ID << ": |cffffffff|Hfaction:" << itr->second.ID << "|h[" << FactionName << "]|h|r " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
+
+ if(itr->second.Flags & FACTION_FLAG_VISIBLE)
+ ss << GetMangosString(LANG_FACTION_VISIBLE);
+ if(itr->second.Flags & FACTION_FLAG_AT_WAR)
+ ss << GetMangosString(LANG_FACTION_ATWAR);
+ if(itr->second.Flags & FACTION_FLAG_PEACE_FORCED)
+ ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
+ if(itr->second.Flags & FACTION_FLAG_HIDDEN)
+ ss << GetMangosString(LANG_FACTION_HIDDEN);
+ if(itr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
+ ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
+ if(itr->second.Flags & FACTION_FLAG_INACTIVE)
+ ss << GetMangosString(LANG_FACTION_INACTIVE);
+
+ SendSysMessage(ss.str().c_str());
+ }
+ }
+ return true;
+}
+
+//show tickets
+void ChatHandler::ShowTicket(uint64 guid, char const* text, char const* time)
+{
+ std::string name;
+ if(!objmgr.GetPlayerNameByGUID(guid,name))
+ name = GetMangosString(LANG_UNKNOWN);
+
+ PSendSysMessage(LANG_COMMAND_TICKETVIEW, name.c_str(),time,text);
+}
+
+//ticket commands
+bool ChatHandler::HandleTicketCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+
+ // ticket<end>
+ if (!px)
+ {
+ size_t count;
+ QueryResult *result = CharacterDatabase.Query("SELECT COUNT(ticket_id) FROM character_ticket");
+ if(result)
+ {
+ count = (*result)[0].GetUInt32();
+ delete result;
+ }
+ else
+ count = 0;
+
+ PSendSysMessage(LANG_COMMAND_TICKETCOUNT, count, m_session->GetPlayer()->isAcceptTickets() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
+ return true;
+ }
+
+ // ticket on
+ if(strncmp(px,"on",3) == 0)
+ {
+ m_session->GetPlayer()->SetAcceptTicket(true);
+ SendSysMessage(LANG_COMMAND_TICKETON);
+ return true;
+ }
+
+ // ticket off
+ if(strncmp(px,"off",4) == 0)
+ {
+ m_session->GetPlayer()->SetAcceptTicket(false);
+ SendSysMessage(LANG_COMMAND_TICKETOFF);
+ return true;
+ }
+
+ // ticket #num
+ int num = atoi(px);
+ if(num > 0)
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid,ticket_text,ticket_lastchange FROM character_ticket ORDER BY ticket_id ASC LIMIT %d,1",num-1);
+
+ if(!result)
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+
+ uint64 guid = fields[0].GetUInt64();
+ char const* text = fields[1].GetString();
+ char const* time = fields[2].GetString();
+
+ ShowTicket(guid,text,time);
+ delete result;
+ return true;
+ }
+
+ std::string name = px;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+
+ if(!guid)
+ return false;
+
+ // ticket $char_name
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_text,ticket_lastchange FROM character_ticket WHERE guid = '%u' ORDER BY ticket_id ASC",GUID_LOPART(guid));
+
+ if(!result)
+ return false;
+
+ Field* fields = result->Fetch();
+
+ char const* text = fields[0].GetString();
+ char const* time = fields[1].GetString();
+
+ ShowTicket(guid,text,time);
+ delete result;
+
+ return true;
+}
+
+uint32 ChatHandler::GetTicketIDByNum(uint32 num)
+{
+ QueryResult *result = CharacterDatabase.Query("SELECT ticket_id FROM character_ticket");
+
+ if(!result || num > result->GetRowCount())
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ return 0;
+ }
+
+ for(uint32 i = 1; i < num; ++i)
+ result->NextRow();
+
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ delete result;
+ return id;
+}
+
+//dell all tickets
+bool ChatHandler::HandleDelTicketCommand(const char *args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ // delticket all
+ if(strncmp(px,"all",4) == 0)
+ {
+ QueryResult *result = CharacterDatabase.Query("SELECT guid FROM character_ticket");
+
+ if(!result)
+ return true;
+
+ // notify players about ticket deleting
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint64 guid = fields[0].GetUInt64();
+
+ if(Player* sender = objmgr.GetPlayer(guid))
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+
+ }while(result->NextRow());
+
+ delete result;
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket");
+ SendSysMessage(LANG_COMMAND_ALLTICKETDELETED);
+ return true;
+ }
+
+ int num = (uint32)atoi(px);
+
+ // delticket #num
+ if(num > 0)
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_id,guid FROM character_ticket LIMIT %i",num);
+
+ if(!result || uint64(num) > result->GetRowCount())
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(int i = 1; i < num; ++i)
+ result->NextRow();
+
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ uint64 guid = fields[1].GetUInt64();
+ delete result;
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE ticket_id = '%u'", id);
+
+ // notify players about ticket deleting
+ if(Player* sender = objmgr.GetPlayer(guid))
+ {
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+ PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,sender->GetName());
+ }
+ else
+ SendSysMessage(LANG_COMMAND_TICKETDEL);
+
+ return true;
+ }
+
+ std::string name = px;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+
+ if(!guid)
+ return false;
+
+ // delticket $char_name
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",GUID_LOPART(guid));
+
+ // notify players about ticket deleting
+ if(Player* sender = objmgr.GetPlayer(guid))
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+
+ PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,px);
+ return true;
+}
+
+//set spawn dist of creature
+bool ChatHandler::HandleSpawnDistCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ float option = atof((char*)args);
+ if (option < 0.0f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ return false;
+ }
+
+ MovementGeneratorType mtype = IDLE_MOTION_TYPE;
+ if (option >0.0f)
+ mtype = RANDOM_MOTION_TYPE;
+
+ Creature *pCreature = getSelectedCreature();
+ uint32 u_guidlow = 0;
+
+ if (pCreature)
+ u_guidlow = pCreature->GetDBTableGUIDLow();
+ else
+ return false;
+
+ pCreature->SetRespawnRadius((float)option);
+ pCreature->SetDefaultMovementType(mtype);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow);
+ PSendSysMessage(LANG_COMMAND_SPAWNDIST,option);
+ return true;
+}
+
+bool ChatHandler::HandleSpawnTimeCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* stime = strtok((char*)args, " ");
+
+ if (!stime)
+ return false;
+
+ int i_stime = atoi((char*)stime);
+
+ if (i_stime < 0)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature *pCreature = getSelectedCreature();
+ uint32 u_guidlow = 0;
+
+ if (pCreature)
+ u_guidlow = pCreature->GetDBTableGUIDLow();
+ else
+ return false;
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow);
+ pCreature->SetRespawnDelay((uint32)i_stime);
+ PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime);
+
+ return true;
+}
+
+/**
+ * Add a waypoint to a creature.
+ *
+ * The user can either select an npc or provide its GUID.
+ *
+ * The user can even select a visual waypoint - then the new waypoint
+ * is placed *after* the selected one - this makes insertion of new
+ * waypoints possible.
+ *
+ * eg:
+ * .wp add 12345
+ * -> adds a waypoint to the npc with the GUID 12345
+ *
+ * .wp add
+ * -> adds a waypoint to the currently selected creature
+ *
+ *
+ * @param args if the user did not provide a GUID, it is NULL
+ *
+ * @return true - command did succeed, false - something went wrong
+ */
+bool ChatHandler::HandleWpAddCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpAddCommand");
+
+ // optional
+ char* guid_str = NULL;
+
+ if(*args)
+ {
+ guid_str = strtok((char*)args, " ");
+ }
+
+ uint32 lowguid = 0;
+ uint32 point = 0;
+ Creature* target = getSelectedCreature();
+ // Did player provide a GUID?
+ if (!guid_str)
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - No GUID provided");
+
+ // No GUID provided
+ // -> Player must have selected a creature
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ if (target->GetEntry() == VISUAL_WAYPOINT )
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - target->GetEntry() == VISUAL_WAYPOINT (1) ");
+
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u",
+ target->GetGUIDLow() );
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUIDLow());
+ // User selected a visual spawnpoint -> get the NPC
+ // Select NPC GUID
+ // Since we compare float values, we have to deal with
+ // some difficulties.
+ // Here we search for all waypoints that only differ in one from 1 thousand
+ // (0.001) - There is no other way to compare C++ floats with mySQL floats
+ // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
+ const char* maxDIFF = "0.01";
+ result = WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
+ target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ do
+ {
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();
+ point = fields[1].GetUInt32();
+ }while( result->NextRow() );
+ delete result;
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = target->GetDBTableGUIDLow();
+ }
+ }
+ else
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - GUID provided");
+
+ // GUID provided
+ // Warn if player also selected a creature
+ // -> Creature selection is ignored <-
+ if(target)
+ {
+ SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
+ }
+ lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ // lowguid -> GUID of the NPC
+ // point -> number of the waypoint (if not 0)
+ sLog.outDebug("DEBUG: HandleWpAddCommand - danach");
+
+ sLog.outDebug("DEBUG: HandleWpAddCommand - point == 0");
+
+ Player* player = m_session->GetPlayer();
+ WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0);
+
+ // update movement type
+ if(target)
+ {
+ target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ target->GetMotionMaster()->Initialize();
+ if(target->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ target->setDeathState(JUST_DIED);
+ target->Respawn();
+ }
+ target->SaveToDB();
+ }
+ else
+ WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
+
+ PSendSysMessage(LANG_WAYPOINT_ADDED, point, lowguid);
+
+ return true;
+} // HandleWpAddCommand
+
+/**
+ * .wp modify emote | spell | text | del | move | add
+ *
+ * add -> add a WP after the selected visual waypoint
+ * User must select a visual waypoint and then issue ".wp modify add"
+ *
+ * emote <emoteID>
+ * User has selected a visual waypoint before.
+ * <emoteID> is added to this waypoint. Everytime the
+ * NPC comes to this waypoint, the emote is called.
+ *
+ * emote <GUID> <WPNUM> <emoteID>
+ * User has not selected visual waypoint before.
+ * For the waypoint <WPNUM> for the NPC with <GUID>
+ * an emote <emoteID> is added.
+ * Everytime the NPC comes to this waypoint, the emote is called.
+ *
+ *
+ * info <GUID> <WPNUM> -> User did not select a visual waypoint and
+ */
+bool ChatHandler::HandleWpModifyCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpModifyCommand");
+
+ if(!*args)
+ return false;
+
+ // first arg: add del text emote spell waittime move
+ char* show_str = strtok((char*)args, " ");
+ if (!show_str)
+ {
+ return false;
+ }
+
+ std::string show = show_str;
+ // Check
+ // Remember: "show" must also be the name of a column!
+ if( (show != "emote") && (show != "spell") && (show != "text1") && (show != "text2")
+ && (show != "text3") && (show != "text4") && (show != "text5")
+ && (show != "waittime") && (show != "del") && (show != "move") && (show != "add")
+ && (show != "model1") && (show != "model2") && (show != "orientation"))
+ {
+ return false;
+ }
+
+ // Next arg is: <GUID> <WPNUM> <ARGUMENT>
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID of the NPC
+ uint32 lowguid = 0;
+ uint32 point = 0;
+ uint32 wpGuid = 0;
+ Creature* target = getSelectedCreature();
+
+ if(target)
+ {
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - User did select an NPC");
+
+ // Did the user select a visual spawnpoint?
+ if (target->GetEntry() != VISUAL_WAYPOINT )
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ wpGuid = target->GetGUIDLow();
+
+ // The visual waypoint
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u LIMIT 1",
+ target->GetGUIDLow() );
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, wpGuid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - After getting wpGuid");
+
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();
+ point = fields[1].GetUInt32();
+
+ // Cleanup memory
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - Cleanup memory");
+ delete result;
+ }
+ else
+ {
+ // User did provide <GUID> <WPNUM>
+
+ char* guid_str = strtok((char*)NULL, " ");
+ if( !guid_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOGUID);
+ return false;
+ }
+ lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage("DEBUG: GUID provided: %d", lowguid);
+
+ char* point_str = strtok((char*)NULL, " ");
+ if( !point_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOWAYPOINTGIVEN);
+ return false;
+ }
+ point = atoi((char*)point_str);
+
+ PSendSysMessage("DEBUG: wpNumber provided: %d", point);
+
+ // Now we need the GUID of the visual waypoint
+ // -> "del", "move", "add" command
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' AND point = '%u' LIMIT 1", lowguid, point);
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, lowguid, point);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+ wpGuid = fields[0].GetUInt32();
+
+ // Free memory
+ delete result;
+ }
+
+ char* arg_str = NULL;
+ // Check for argument
+ if( (show.find("text") == std::string::npos ) && (show != "del") && (show != "move") && (show != "add"))
+ {
+ // Text is enclosed in "<>", all other arguments not
+ if( show.find("text") != std::string::npos )
+ arg_str = strtok((char*)NULL, "<>");
+ else
+ arg_str = strtok((char*)NULL, " ");
+
+ if( !arg_str)
+ {
+ PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, show_str);
+ return false;
+ }
+ }
+
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - Parameters parsed - now execute the command");
+
+ // wpGuid -> GUID of the waypoint creature
+ // lowguid -> GUID of the NPC
+ // point -> waypoint number
+
+ // Special functions:
+ // add - move - del -> no args commands
+ // Add a waypoint after the selected visual
+ if(show == "add" && target)
+ {
+ PSendSysMessage("DEBUG: wp modify add, GUID: %u", lowguid);
+
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ if( !npcCreature )
+ {
+ PSendSysMessage(LANG_WAYPOINT_NPCNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - add -- npcCreature");
+
+ // What to do:
+ // Add the visual spawnpoint (DB only)
+ // Adjust the waypoints
+ // Respawn the owner of the waypoints
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - add");
+
+ Player* chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+
+ if(npcCreature)
+ {
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+
+ // create the waypoint creature
+ wpGuid = 0;
+ Creature* wpCreature = new Creature;
+ if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map,VISUAL_WAYPOINT,0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
+ delete wpCreature;
+ }
+ else
+ {
+ wpCreature->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
+
+ if(!wpCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
+ delete wpCreature;
+ }
+ else
+ {
+ wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(), map);
+ map->Add(wpCreature);
+ wpGuid = wpCreature->GetGUIDLow();
+ }
+ }
+
+ WaypointMgr.AddAfterNode(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), 0, 0, wpGuid);
+
+ if(!wpGuid)
+ return false;
+
+ PSendSysMessage(LANG_WAYPOINT_ADDED_NO, point+1);
+ return true;
+ } // add
+
+ if(show == "del" && target)
+ {
+ PSendSysMessage("DEBUG: wp modify del, GUID: %u", lowguid);
+
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ // wpCreature
+ Creature* wpCreature = NULL;
+ if( wpGuid != 0 )
+ {
+ wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
+ wpCreature->DeleteFromDB();
+ wpCreature->CleanupsBeforeDelete();
+ wpCreature->AddObjectToRemoveList();
+ }
+
+ // What to do:
+ // Remove the visual spawnpoint
+ // Adjust the waypoints
+ // Respawn the owner of the waypoints
+
+ WaypointMgr.DeleteNode(lowguid, point);
+
+ if(npcCreature)
+ {
+ // Any waypoints left?
+ QueryResult *result2 = WorldDatabase.PQuery( "SELECT point FROM creature_movement WHERE id = '%u'",lowguid);
+ if(!result2)
+ {
+ npcCreature->SetDefaultMovementType(RANDOM_MOTION_TYPE);
+ }
+ else
+ {
+ npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ delete result2;
+ }
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ npcCreature->SaveToDB();
+ }
+
+ PSendSysMessage(LANG_WAYPOINT_REMOVED);
+ return true;
+ } // del
+
+ if(show == "move" && target)
+ {
+ PSendSysMessage("DEBUG: wp move, GUID: %u", lowguid);
+
+ Player *chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+ {
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ // wpCreature
+ Creature* wpCreature = NULL;
+ // What to do:
+ // Move the visual spawnpoint
+ // Respawn the owner of the waypoints
+ if( wpGuid != 0 )
+ {
+ wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
+ wpCreature->DeleteFromDB();
+ wpCreature->CleanupsBeforeDelete();
+ wpCreature->AddObjectToRemoveList();
+ // re-create
+ Creature* wpCreature2 = new Creature;
+ if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, VISUAL_WAYPOINT, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
+ delete wpCreature2;
+ return false;
+ }
+
+ wpCreature2->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
+
+ if(!wpCreature2->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature2->GetGUIDLow(),wpCreature2->GetEntry(),wpCreature2->GetPositionX(),wpCreature2->GetPositionY());
+ delete wpCreature2;
+ return false;
+ }
+
+ wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map);
+ map->Add(wpCreature2);
+ //MapManager::Instance().GetMap(npcCreature->GetMapId())->Add(wpCreature2);
+ }
+
+ WaypointMgr.SetNodePosition(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ());
+
+ if(npcCreature)
+ {
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+ PSendSysMessage(LANG_WAYPOINT_CHANGED);
+ }
+ return true;
+ } // move
+
+ // Create creature - npc that has the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WaypointMgr.SetNodeText(lowguid, point, show_str, arg_str);
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+ if(npcCreature)
+ {
+ npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+ PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str);
+
+ return true;
+}
+
+/**
+ * .wp show info | on | off
+ *
+ * info -> User has selected a visual waypoint before
+ *
+ * info <GUID> <WPNUM> -> User did not select a visual waypoint and
+ * provided the GUID of the NPC and the number of
+ * the waypoint.
+ *
+ * on -> User has selected an NPC; all visual waypoints for this
+ * NPC are added to the world
+ *
+ * on <GUID> -> User did not select an NPC - instead the GUID of the
+ * NPC is provided. All visual waypoints for this NPC
+ * are added from the world.
+ *
+ * off -> User has selected an NPC; all visual waypoints for this
+ * NPC are removed from the world.
+ *
+ * on <GUID> -> User did not select an NPC - instead the GUID of the
+ * NPC is provided. All visual waypoints for this NPC
+ * are removed from the world.
+ *
+ *
+ */
+bool ChatHandler::HandleWpShowCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpShowCommand");
+
+ if(!*args)
+ return false;
+
+ // first arg: on, off, first, last
+ char* show_str = strtok((char*)args, " ");
+ if (!show_str)
+ {
+ return false;
+ }
+ // second arg: GUID (optional, if a creature is selected)
+ char* guid_str = strtok((char*)NULL, " ");
+ sLog.outDebug("DEBUG: HandleWpShowCommand: show_str: %s guid_str: %s", show_str, guid_str);
+ //if (!guid_str) {
+ // return false;
+ //}
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID
+ Creature* target = getSelectedCreature();
+ // Did player provide a GUID?
+ if (!guid_str)
+ {
+ sLog.outDebug("DEBUG: HandleWpShowCommand: !guid_str");
+ // No GUID provided
+ // -> Player must have selected a creature
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ sLog.outDebug("DEBUG: HandleWpShowCommand: GUID provided");
+ // GUID provided
+ // Warn if player also selected a creature
+ // -> Creature selection is ignored <-
+ if(target)
+ {
+ SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
+ }
+
+ uint32 lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ uint32 lowguid = target->GetDBTableGUIDLow();
+
+ std::string show = show_str;
+ uint32 Maxpoint;
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: lowguid: %u", lowguid);
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: Habe creature: %ld", target );
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: wpshow - show: %s", show_str);
+ //PSendSysMessage("wpshow - show: %s", show);
+
+ // Show info for the selected waypoint
+ if(show == "info")
+ {
+ PSendSysMessage("DEBUG: wp info, GUID: %u", lowguid);
+
+ // Check if the user did specify a visual waypoint
+ if( target->GetEntry() != VISUAL_WAYPOINT )
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //PSendSysMessage("wp on, GUID: %u", lowguid);
+
+ //pCreature->GetPositionX();
+
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE wpguid = %u",
+ target->GetGUID() );
+ if(!result)
+ {
+ // Since we compare float values, we have to deal with
+ // some difficulties.
+ // Here we search for all waypoints that only differ in one from 1 thousand
+ // (0.001) - There is no other way to compare C++ floats with mySQL floats
+ // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
+ const char* maxDIFF = "0.01";
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUID());
+
+ result = WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
+ target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 creGUID = fields[0].GetUInt32();
+ uint32 point = fields[1].GetUInt32();
+ int waittime = fields[2].GetUInt32();
+ uint32 emote = fields[3].GetUInt32();
+ uint32 spell = fields[4].GetUInt32();
+ const char * text1 = fields[5].GetString();
+ const char * text2 = fields[6].GetString();
+ const char * text3 = fields[7].GetString();
+ const char * text4 = fields[8].GetString();
+ const char * text5 = fields[9].GetString();
+ uint32 model1 = fields[10].GetUInt32();
+ uint32 model2 = fields[11].GetUInt32();
+
+ // Get the creature for which we read the waypoint
+ Creature* wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(creGUID,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ PSendSysMessage(LANG_WAYPOINT_INFO_TITLE, point, (wpCreature ? wpCreature->GetName() : "<not found>"), creGUID);
+ PSendSysMessage(LANG_WAYPOINT_INFO_WAITTIME, waittime);
+ PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 1, model1);
+ PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 2, model2);
+ PSendSysMessage(LANG_WAYPOINT_INFO_EMOTE, emote);
+ PSendSysMessage(LANG_WAYPOINT_INFO_SPELL, spell);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 1, text1);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 2, text2);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 3, text3);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 4, text4);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 5, text5);
+
+ }while( result->NextRow() );
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "on")
+ {
+ PSendSysMessage("DEBUG: wp on, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT point, position_x,position_y,position_z FROM creature_movement WHERE id = '%u'",lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ // Delete all visuals for this NPC
+ QueryResult *result2 = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' and wpguid <> 0", lowguid);
+ if(result2)
+ {
+ bool hasError = false;
+ do
+ {
+ Field *fields = result2->Fetch();
+ uint32 wpguid = fields[0].GetUInt32();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpguid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ if(!pCreature)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid);
+ hasError = true;
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", wpguid);
+ }
+ else
+ {
+ pCreature->DeleteFromDB();
+ pCreature->CleanupsBeforeDelete();
+ pCreature->AddObjectToRemoveList();
+ }
+
+ }while( result2->NextRow() );
+ delete result2;
+ if( hasError )
+ {
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
+ }
+ }
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 point = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+ float o = chr->GetOrientation();
+
+ Creature* wpCreature = new Creature;
+ if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
+ delete wpCreature;
+ delete result;
+ return false;
+ }
+
+ wpCreature->Relocate(x, y, z, o);
+
+ if(!wpCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
+ delete wpCreature;
+ delete result;
+ return false;
+ }
+
+ wpCreature->SetVisibility(VISIBILITY_OFF);
+ sLog.outDebug("DEBUG: UPDATE creature_movement SET wpguid = '%u");
+ // set "wpguid" column to the visual waypoint
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), lowguid, point);
+
+ wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map);
+ map->Add(wpCreature);
+ //MapManager::Instance().GetMap(wpCreature->GetMapId())->Add(wpCreature);
+ }while( result->NextRow() );
+
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "first")
+ {
+ PSendSysMessage("DEBUG: wp first, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point='1' AND id = '%u'",lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->Relocate(x, y, z, o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
+ map->Add(pCreature);
+ //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "First Waypoint");
+
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "last")
+ {
+ PSendSysMessage("DEBUG: wp last, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'",lowguid);
+ if( result )
+ {
+ Maxpoint = (*result)[0].GetUInt32();
+
+ delete result;
+ }
+ else
+ Maxpoint = 0;
+
+ result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point ='%u' AND id = '%u'",Maxpoint, lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDLAST, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->Relocate(x, y, z, o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
+ map->Add(pCreature);
+ //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "Last Waypoint");
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "off")
+ {
+ QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id = '%d'", VISUAL_WAYPOINT);
+ if(!result)
+ {
+ SendSysMessage(LANG_WAYPOINT_VP_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ bool hasError = false;
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(guid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ //Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid);
+ hasError = true;
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", guid);
+ }
+ else
+ {
+ pCreature->DeleteFromDB();
+ pCreature->CleanupsBeforeDelete();
+ pCreature->AddObjectToRemoveList();
+ }
+ }while(result->NextRow());
+ // set "wpguid" column to "empty" - no visual waypoint spawned
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0'");
+
+ if( hasError )
+ {
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
+ }
+
+ SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED);
+ // Cleanup memory
+ delete result;
+
+ return true;
+ }
+
+ PSendSysMessage("DEBUG: wpshow - no valid command found");
+
+ return true;
+} // HandleWpShowCommand
+
+bool ChatHandler::HandleWpExportCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ // Next arg is: <GUID> <ARGUMENT>
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID of the NPC
+ uint32 lowguid = 0;
+ Creature* target = getSelectedCreature();
+ char* arg_str = NULL;
+ if (target)
+ {
+ if (target->GetEntry() != VISUAL_WAYPOINT)
+ lowguid = target->GetGUIDLow();
+ else
+ {
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id FROM creature_movement WHERE wpguid = %u LIMIT 1", target->GetGUIDLow() );
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
+ return true;
+ }
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();;
+ delete result;
+ }
+
+ arg_str = strtok((char*)args, " ");
+ }
+ else
+ {
+ // user provided <GUID>
+ char* guid_str = strtok((char*)args, " ");
+ if( !guid_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOGUID);
+ return false;
+ }
+ lowguid = atoi((char*)guid_str);
+
+ arg_str = strtok((char*)NULL, " ");
+ }
+
+ if( !arg_str)
+ {
+ PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, "export");
+ return false;
+ }
+
+ PSendSysMessage("DEBUG: wp export, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ "SELECT point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id FROM creature_movement WHERE id = '%u' ORDER BY point", lowguid );
+
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTHINGTOEXPORT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::ofstream outfile;
+ outfile.open (arg_str);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ outfile << "INSERT INTO creature_movement ";
+ outfile << "( id, point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5 ) VALUES ";
+
+ outfile << "( ";
+ outfile << fields[15].GetUInt32(); // id
+ outfile << ", ";
+ outfile << fields[0].GetUInt32(); // point
+ outfile << ", ";
+ outfile << fields[1].GetFloat(); // position_x
+ outfile << ", ";
+ outfile << fields[2].GetFloat(); // position_y
+ outfile << ", ";
+ outfile << fields[3].GetUInt32(); // position_z
+ outfile << ", ";
+ outfile << fields[4].GetUInt32(); // orientation
+ outfile << ", ";
+ outfile << fields[5].GetUInt32(); // model1
+ outfile << ", ";
+ outfile << fields[6].GetUInt32(); // model2
+ outfile << ", ";
+ outfile << fields[7].GetUInt16(); // waittime
+ outfile << ", ";
+ outfile << fields[8].GetUInt32(); // emote
+ outfile << ", ";
+ outfile << fields[9].GetUInt32(); // spell
+ outfile << ", ";
+ const char *tmpChar = fields[10].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text1
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text1
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[11].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text2
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text2
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[12].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text3
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text3
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[13].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text4
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text4
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[14].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text5
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text5
+ outfile << "'";
+ }
+ outfile << ");\n ";
+
+ } while( result->NextRow() );
+ delete result;
+
+ PSendSysMessage(LANG_WAYPOINT_EXPORTED);
+ outfile.close();
+
+ return true;
+}
+
+bool ChatHandler::HandleWpImportCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* arg_str = strtok((char*)args, " ");
+ if (!arg_str)
+ return false;
+
+ std::string line;
+ std::ifstream infile (arg_str);
+ if (infile.is_open())
+ {
+ while (! infile.eof() )
+ {
+ getline (infile,line);
+ //cout << line << endl;
+ QueryResult *result = WorldDatabase.PQuery(line.c_str());
+ delete result;
+ }
+ infile.close();
+ }
+ PSendSysMessage(LANG_WAYPOINT_IMPORTED);
+
+ return true;
+}
+
+//rename characters
+bool ChatHandler::HandleRenameCommand(const char* args)
+{
+ Player* target = NULL;
+ uint64 targetGUID = 0;
+ std::string oldname;
+
+ char* px = strtok((char*)args, " ");
+
+ if(px)
+ {
+ oldname = px;
+
+ if(!normalizePlayerName(oldname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = objmgr.GetPlayer(oldname.c_str());
+
+ if (!target)
+ targetGUID = objmgr.GetPlayerGUIDByName(oldname);
+ }
+
+ if(!target && !targetGUID)
+ {
+ target = getSelectedPlayer();
+ }
+
+ if(!target && !targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(target)
+ {
+ PSendSysMessage(LANG_RENAME_PLAYER, target->GetName());
+ target->SetAtLoginFlag(AT_LOGIN_RENAME);
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", target->GetGUIDLow());
+ }
+ else
+ {
+ PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldname.c_str(), GUID_LOPART(targetGUID));
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(targetGUID));
+ }
+
+ return true;
+}
+
+//spawn go
+bool ChatHandler::HandleGameObjectCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char* pParam1 = strtok((char*)args, " ");
+ if (!pParam1)
+ return false;
+
+ uint32 id = atoi((char*)pParam1);
+ if(!id)
+ return false;
+
+ char* spawntimeSecs = strtok(NULL, " ");
+
+ const GameObjectInfo *goI = objmgr.GetGameObjectInfo(id);
+
+ if (!goI)
+ {
+ PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = m_session->GetPlayer();
+ float x = float(chr->GetPositionX());
+ float y = float(chr->GetPositionY());
+ float z = float(chr->GetPositionZ());
+ float o = float(chr->GetOrientation());
+ Map *map = chr->GetMap();
+
+ float rot2 = sin(o/2);
+ float rot3 = cos(o/2);
+
+ GameObject* pGameObj = new GameObject;
+ uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ if(!pGameObj->Create(db_lowGUID, goI->id, map, x, y, z, o, 0, 0, rot2, rot3, 0, 1))
+ {
+ delete pGameObj;
+ return false;
+ }
+
+ if( spawntimeSecs )
+ {
+ uint32 value = atoi((char*)spawntimeSecs);
+ pGameObj->SetRespawnTime(value);
+ //sLog.outDebug("*** spawntimeSecs: %d", value);
+ }
+
+ // fill the gameobject data and save to the db
+ pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+
+ // this will generate a new guid if the object is in an instance
+ if(!pGameObj->LoadFromDB(db_lowGUID, map))
+ {
+ delete pGameObj;
+ return false;
+ }
+
+ sLog.outDebug(GetMangosString(LANG_GAMEOBJECT_CURRENT), goI->name, db_lowGUID, x, y, z, o);
+
+ map->Add(pGameObj);
+
+ // TODO: is it really necessary to add both the real and DB table guid here ?
+ objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID));
+
+ PSendSysMessage(LANG_GAMEOBJECT_ADD,id,goI->name,db_lowGUID,x,y,z);
+ return true;
+}
+
+//show animation
+bool ChatHandler::HandleAnimCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 anim_id = atoi((char*)args);
+ m_session->GetPlayer()->HandleEmoteCommand(anim_id);
+ return true;
+}
+
+//change standstate
+bool ChatHandler::HandleStandStateCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 anim_id = atoi((char*)args);
+ m_session->GetPlayer( )->SetUInt32Value( UNIT_NPC_EMOTESTATE , anim_id );
+
+ return true;
+}
+
+bool ChatHandler::HandleAddHonorCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 amount = (uint32)atoi(args);
+ target->RewardHonor(NULL, 1, amount);
+ return true;
+}
+
+bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/)
+{
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ m_session->GetPlayer()->RewardHonor(target, 1);
+ return true;
+}
+
+bool ChatHandler::HandleUpdateHonorFieldsCommand(const char* /*args*/)
+{
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->UpdateHonorFields();
+ return true;
+}
+
+bool ChatHandler::HandleLookupEventCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ for(uint32 id = 0; id < events.size(); ++id )
+ {
+ GameEventData const& eventData = events[id];
+
+ std::string descr = eventData.description;
+ if(descr.empty())
+ continue;
+
+ if (Utf8FitTo(descr, wnamepart))
+ {
+ char const* active = activeEvents.find(id) != activeEvents.end() ? GetMangosString(LANG_ACTIVE) : "";
+ PSendSysMessage(LANG_EVENT_ENTRY_LIST,id,id,descr.c_str(),active );
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_NOEVENTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleEventActiveListCommand(const char* args)
+{
+ uint32 counter = 0;
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ char const* active = GetMangosString(LANG_ACTIVE);
+
+ for(GameEvent::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr )
+ {
+ uint32 event_id = *itr;
+ GameEventData const& eventData = events[event_id];
+
+ PSendSysMessage(LANG_EVENT_ENTRY_LIST,event_id,event_id,eventData.description.c_str(),active );
+ ++counter;
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_NOEVENTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleEventInfoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ uint32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+ bool active = activeEvents.find(event_id) != activeEvents.end();
+ char const* activeStr = active ? GetMangosString(LANG_ACTIVE) : "";
+
+ std::string startTimeStr = TimeToTimestampStr(eventData.start);
+ std::string endTimeStr = TimeToTimestampStr(eventData.end);
+
+ uint32 delay = gameeventmgr.NextCheck(event_id);
+ time_t nextTime = time(NULL)+delay;
+ std::string nextStr = nextTime >= eventData.start && nextTime < eventData.end ? TimeToTimestampStr(time(NULL)+delay) : "-";
+
+ std::string occurenceStr = secsToTimeString(eventData.occurence * MINUTE);
+ std::string lengthStr = secsToTimeString(eventData.length * MINUTE);
+
+ PSendSysMessage(LANG_EVENT_INFO,event_id,eventData.description.c_str(),activeStr,
+ startTimeStr.c_str(),endTimeStr.c_str(),occurenceStr.c_str(),lengthStr.c_str(),
+ nextStr.c_str());
+ return true;
+}
+
+bool ChatHandler::HandleEventStartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ int32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id < 1 || event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+ if(activeEvents.find(event_id) != activeEvents.end())
+ {
+ PSendSysMessage(LANG_EVENT_ALREADY_ACTIVE,event_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ gameeventmgr.StartEvent(event_id,true);
+ return true;
+}
+
+bool ChatHandler::HandleEventStopCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ int32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id < 1 || event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ if(activeEvents.find(event_id) == activeEvents.end())
+ {
+ PSendSysMessage(LANG_EVENT_NOT_ACTIVE,event_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ gameeventmgr.StopEvent(event_id,true);
+ return true;
+}
+
+bool ChatHandler::HandleCombatStopCommand(const char* args)
+{
+ Player *player;
+
+ if(*args)
+ {
+ std::string playername = args;
+
+ if(!normalizePlayerName(playername))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(playername.c_str());
+
+ if(!player)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ player = getSelectedPlayer();
+
+ if (!player)
+ player = m_session->GetPlayer();
+ }
+
+ player->CombatStop();
+ player->getHostilRefManager().deleteReferences();
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/)
+{
+ uint32 classmask = m_session->GetPlayer()->getClassMask();
+
+ for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
+ if( !skillInfo )
+ continue;
+
+ if( skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY )
+ {
+ for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
+ if( !skillLine )
+ continue;
+
+ // skip racial skills
+ if( skillLine->racemask != 0 )
+ continue;
+
+ // skip wrong class skills
+ if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
+ continue;
+
+ if( skillLine->skillId != i || skillLine->forward_spellid )
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ m_session->GetPlayer()->learnSpell(skillLine->spellId);
+ }
+ }
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_ALL_CRAFT);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllRecipesCommand(const char* args)
+{
+ // Learns all recipes of specified profession and sets skill to max
+ // Example: .learn all_recipes enchanting
+
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(args,wnamepart))
+ return false;
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 classmask = m_session->GetPlayer()->getClassMask();
+
+ for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
+ if( !skillInfo )
+ continue;
+
+ if( skillInfo->categoryId != SKILL_CATEGORY_PROFESSION &&
+ skillInfo->categoryId != SKILL_CATEGORY_SECONDARY )
+ continue;
+
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = skillInfo->name[loc];
+
+ if(Utf8FitTo(name, wnamepart))
+ {
+ for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
+ if( !skillLine )
+ continue;
+
+ if( skillLine->skillId != i || skillLine->forward_spellid )
+ continue;
+
+ // skip racial skills
+ if( skillLine->racemask != 0 )
+ continue;
+
+ // skip wrong class skills
+ if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ if( !target->HasSpell(spellInfo->Id) )
+ m_session->GetPlayer()->learnSpell(skillLine->spellId);
+ }
+
+ uint16 maxLevel = target->GetPureMaxSkillValue(skillInfo->id);
+ target->SetSkill(skillInfo->id, maxLevel, maxLevel);
+ PSendSysMessage(LANG_COMMAND_LEARN_ALL_RECIPES, name.c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
new file mode 100644
index 00000000000..93f39229b7d
--- /dev/null
+++ b/src/game/Level3.cpp
@@ -0,0 +1,5424 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "PlayerDump.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "GameObject.h"
+#include "Chat.h"
+#include "Log.h"
+#include "Guild.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "SpellAuras.h"
+#include "ScriptCalls.h"
+#include "Language.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "Weather.h"
+#include "PointMovementGenerator.h"
+#include "TargetedMovementGenerator.h"
+#include "SkillDiscovery.h"
+#include "SkillExtraItems.h"
+#include "SystemConfig.h"
+#include "Config/ConfigEnv.h"
+#include "Util.h"
+#include "ItemEnchantmentMgr.h"
+#include "InstanceSaveMgr.h"
+#include "InstanceData.h"
+
+//reload commands
+bool ChatHandler::HandleReloadCommand(const char* arg)
+{
+ // this is error catcher for wrong table name in .reload commands
+ PSendSysMessage("Db table with name starting from '%s' not found and can't be reloaded.",arg);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+bool ChatHandler::HandleReloadAllCommand(const char*)
+{
+ HandleReloadAreaTriggerTeleportCommand("");
+ //HandleReloadAreaTriggerTavernCommand(""); -- reloaded in HandleReloadAreaTriggerTavernCommand
+ //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand
+ HandleReloadSkillFishingBaseLevelCommand("");
+
+ HandleReloadAllLootCommand("");
+ HandleReloadAllQuestCommand("");
+ HandleReloadAllSpellCommand("");
+ HandleReloadAllItemCommand("");
+
+ HandleReloadCommandCommand("");
+ HandleReloadReservedNameCommand("");
+ HandleReloadMangosStringCommand("");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllAreaCommand(const char*)
+{
+ HandleReloadAreaTriggerTeleportCommand("");
+ //HandleReloadAreaTriggerTavernCommand(""); -- reloaded in HandleReloadAreaTriggerTavernCommand
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllQuestCommand(const char* /*args*/)
+{
+ HandleReloadQuestAreaTriggersCommand("a");
+ HandleReloadQuestTemplateCommand("a");
+
+ sLog.outString( "Re-Loading Quests Relations..." );
+ objmgr.LoadQuestRelations();
+ SendGlobalSysMessage("DB tables `*_questrelation` and `*_involvedrelation` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllLootCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables..." );
+ LoadLootTables();
+ SendGlobalSysMessage("DB tables `*_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ PSendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ sLog.outString( "Re-Loading Scripts..." );
+ HandleReloadGameObjectScriptsCommand("a");
+ HandleReloadEventScriptsCommand("a");
+ HandleReloadQuestEndScriptsCommand("a");
+ HandleReloadQuestStartScriptsCommand("a");
+ HandleReloadSpellScriptsCommand("a");
+ SendGlobalSysMessage("DB tables `*_scripts` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllSpellCommand(const char*)
+{
+ HandleReloadSkillDiscoveryTemplateCommand("a");
+ HandleReloadSkillExtraItemTemplateCommand("a");
+ HandleReloadSpellAffectCommand("a");
+ HandleReloadSpellChainCommand("a");
+ HandleReloadSpellElixirCommand("a");
+ HandleReloadSpellLearnSpellCommand("a");
+ HandleReloadSpellProcEventCommand("a");
+ HandleReloadSpellScriptTargetCommand("a");
+ HandleReloadSpellTargetPositionCommand("a");
+ HandleReloadSpellThreatsCommand("a");
+ HandleReloadSpellPetAurasCommand("a");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllItemCommand(const char*)
+{
+ HandleReloadPageTextsCommand("a");
+ HandleReloadItemEnchantementsCommand("a");
+ return true;
+}
+
+bool ChatHandler::HandleReloadConfigCommand(const char* arg)
+{
+ sLog.outString( "Re-Loading config settings..." );
+ sWorld.LoadConfigSettings(true);
+ SendGlobalSysMessage("World config settings reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*)
+{
+ sLog.outString( "Re-Loading Tavern Area Triggers..." );
+ objmgr.LoadTavernAreaTriggers();
+ SendGlobalSysMessage("DB table `areatrigger_tavern` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*)
+{
+ sLog.outString( "Re-Loading AreaTrigger teleport definitions..." );
+ objmgr.LoadAreaTriggerTeleports();
+ SendGlobalSysMessage("DB table `areatrigger_teleport` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCommandCommand(const char*)
+{
+ load_command_table = true;
+ SendGlobalSysMessage("DB table `command` will be reloaded at next chat command use.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`creature_questrelation`)" );
+ objmgr.LoadCreatureQuestRelations();
+ SendGlobalSysMessage("DB table `creature_questrelation` (creature quest givers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`creature_involvedrelation`)" );
+ objmgr.LoadCreatureInvolvedRelations();
+ SendGlobalSysMessage("DB table `creature_involvedrelation` (creature quest takers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`gameobject_questrelation`)" );
+ objmgr.LoadGameobjectQuestRelations();
+ SendGlobalSysMessage("DB table `gameobject_questrelation` (gameobject quest givers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`gameobject_involvedrelation`)" );
+ objmgr.LoadGameobjectInvolvedRelations();
+ SendGlobalSysMessage("DB table `gameobject_involvedrelation` (gameobject quest takers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*)
+{
+ sLog.outString( "Re-Loading Quest Area Triggers..." );
+ objmgr.LoadQuestAreaTriggers();
+ SendGlobalSysMessage("DB table `areatrigger_involvedrelation` (quest area triggers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestTemplateCommand(const char*)
+{
+ sLog.outString( "Re-Loading Quest Templates..." );
+ objmgr.LoadQuests();
+ SendGlobalSysMessage("DB table `quest_template` (quest definitions) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`creature_loot_template`)" );
+ LoadLootTemplates_Creature();
+ LootTemplates_Creature.CheckLootRefs();
+ SendGlobalSysMessage("DB table `creature_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`disenchant_loot_template`)" );
+ LoadLootTemplates_Disenchant();
+ LootTemplates_Disenchant.CheckLootRefs();
+ SendGlobalSysMessage("DB table `disenchant_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`fishing_loot_template`)" );
+ LoadLootTemplates_Fishing();
+ LootTemplates_Fishing.CheckLootRefs();
+ SendGlobalSysMessage("DB table `fishing_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`gameobject_loot_template`)" );
+ LoadLootTemplates_Gameobject();
+ LootTemplates_Gameobject.CheckLootRefs();
+ SendGlobalSysMessage("DB table `gameobject_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`item_loot_template`)" );
+ LoadLootTemplates_Item();
+ LootTemplates_Item.CheckLootRefs();
+ SendGlobalSysMessage("DB table `item_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" );
+ LoadLootTemplates_Pickpocketing();
+ LootTemplates_Pickpocketing.CheckLootRefs();
+ SendGlobalSysMessage("DB table `pickpocketing_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`prospecting_loot_template`)" );
+ LoadLootTemplates_Prospecting();
+ LootTemplates_Prospecting.CheckLootRefs();
+ SendGlobalSysMessage("DB table `prospecting_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" );
+ LoadLootTemplates_QuestMail();
+ LootTemplates_QuestMail.CheckLootRefs();
+ SendGlobalSysMessage("DB table `quest_mail_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`reference_loot_template`)" );
+ LoadLootTemplates_Reference();
+ SendGlobalSysMessage("DB table `reference_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`skinning_loot_template`)" );
+ LoadLootTemplates_Skinning();
+ LootTemplates_Skinning.CheckLootRefs();
+ SendGlobalSysMessage("DB table `skinning_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadMangosStringCommand(const char*)
+{
+ sLog.outString( "Re-Loading mangos_string Table!" );
+ objmgr.LoadMangosStrings();
+ SendGlobalSysMessage("DB table `mangos_string` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadReservedNameCommand(const char*)
+{
+ sLog.outString( "Loading ReservedNames... (`reserved_name`)" );
+ objmgr.LoadReservedPlayersNames();
+ SendGlobalSysMessage("DB table `reserved_name` (player reserved names) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Discovery Table..." );
+ LoadSkillDiscoveryTable();
+ SendGlobalSysMessage("DB table `skill_discovery_template` (recipes discovered at crafting) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillExtraItemTemplateCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Extra Item Table..." );
+ LoadSkillExtraItemTable();
+ SendGlobalSysMessage("DB table `skill_extra_item_template` (extra item creation when crafting) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Fishing base level requirements..." );
+ objmgr.LoadFishingBaseSkillLevel();
+ SendGlobalSysMessage("DB table `skill_fishing_base_level` (fishing base level for zone/subzone) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellAffectCommand(const char*)
+{
+ sLog.outString( "Re-Loading SpellAffect definitions..." );
+ spellmgr.LoadSpellAffects();
+ SendGlobalSysMessage("DB table `spell_affect` (spell mods apply requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellChainCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Chain Data... " );
+ spellmgr.LoadSpellChains();
+ SendGlobalSysMessage("DB table `spell_chain` (spell ranks) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellElixirCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Elixir types..." );
+ spellmgr.LoadSpellElixirs();
+ SendGlobalSysMessage("DB table `spell_elixir` (spell exlixir types) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Learn Spells..." );
+ spellmgr.LoadSpellLearnSpells();
+ SendGlobalSysMessage("DB table `spell_learn_spell` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellProcEventCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Proc Event conditions..." );
+ spellmgr.LoadSpellProcEvents();
+ SendGlobalSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*)
+{
+ sLog.outString( "Re-Loading SpellsScriptTarget..." );
+ spellmgr.LoadSpellScriptTarget();
+ SendGlobalSysMessage("DB table `spell_script_target` (spell targets selection in case specific creature/GO requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell target coordinates..." );
+ spellmgr.LoadSpellTargetPositions();
+ SendGlobalSysMessage("DB table `spell_target_position` (destination coordinates for spell targets) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellThreatsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Aggro Spells Definitions...");
+ spellmgr.LoadSpellThreats();
+ SendGlobalSysMessage("DB table `spell_threat` (spell aggro definitions) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell pet auras...");
+ spellmgr.LoadSpellPetAuras();
+ SendGlobalSysMessage("DB table `spell_pet_auras` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadPageTextsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Page Texts..." );
+ objmgr.LoadPageTexts();
+ SendGlobalSysMessage("DB table `page_texts` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Item Random Enchantments Table..." );
+ LoadRandomEnchantmentsTable();
+ SendGlobalSysMessage("DB table `item_enchantment_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `gameobject_scripts`...");
+
+ objmgr.LoadGameObjectScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `gameobject_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `event_scripts`...");
+
+ objmgr.LoadEventScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `event_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `quest_end_scripts`...");
+
+ objmgr.LoadQuestEndScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `quest_end_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `quest_start_scripts`...");
+
+ objmgr.LoadQuestStartScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `quest_start_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `spell_scripts`...");
+
+ objmgr.LoadSpellScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `spell_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadGameGraveyardZoneCommand(const char* /*arg*/)
+{
+ sLog.outString( "Re-Loading Graveyard-zone links...");
+
+ objmgr.LoadGraveyardZones();
+
+ SendGlobalSysMessage("DB table `game_graveyard_zone` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleLoadScriptsCommand(const char* args)
+{
+ if(!LoadScriptingModule(args)) return true;
+
+ sWorld.SendWorldText(LANG_SCRIPTS_RELOADED);
+ return true;
+}
+
+bool ChatHandler::HandleSecurityCommand(const char* args)
+{
+ char* arg1 = strtok((char*)args, " ");
+ if( !arg1 )
+ return false;
+
+ char* arg2 = 0;
+
+ std::string targetName;
+ uint32 targetAccountId = 0;
+ uint32 targetSecurity = 0;
+
+ Player* targetPlayer = getSelectedPlayer();
+ if(targetPlayer)
+ {
+ targetName = targetPlayer->GetName();
+ targetAccountId = targetPlayer->GetSession()->GetAccountId();
+ targetSecurity = targetPlayer->GetSession()->GetSecurity();
+ arg2 = arg1;
+ }
+ else
+ {
+ targetName = arg1;
+ if(!normalizePlayerName(targetName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ targetPlayer = ObjectAccessor::Instance().FindPlayerByName(targetName.c_str());
+ if(targetPlayer)
+ {
+ targetAccountId = targetPlayer->GetSession()->GetAccountId();
+ targetSecurity = targetPlayer->GetSession()->GetSecurity();
+ }
+ else
+ {
+ uint64 targetGUID = objmgr.GetPlayerGUIDByName(targetName.c_str());
+ if(!targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ targetAccountId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
+ targetSecurity = objmgr.GetSecurityByAccount(targetAccountId);
+ }
+
+ arg2 = strtok(NULL, " ");
+ }
+
+ int32 gm = (int32)atoi(arg2);
+ if ( gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR )
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // can set security level only for target with less security and to less security that we have
+ if(targetSecurity >= m_session->GetSecurity() || uint32(gm) >= m_session->GetSecurity() )
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(targetPlayer)
+ {
+ if( targetPlayer != m_session->GetPlayer() )
+ ChatHandler(targetPlayer).PSendSysMessage(LANG_YOURS_SECURITY_CHANGED,m_session->GetPlayer()->GetName(), gm);
+
+ targetPlayer->GetSession()->SetSecurity(gm);
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetName.c_str(), gm);
+ loginDatabase.PExecute("UPDATE account SET gmlevel = '%i' WHERE id = '%u'", gm, targetAccountId);
+
+ return true;
+}
+
+bool ChatHandler::HandleAllowMovementCommand(const char* /*args*/)
+{
+ if(sWorld.getAllowMovement())
+ {
+ sWorld.SetAllowMovement(false);
+ SendSysMessage(LANG_CREATURE_MOVE_DISABLED);
+ }
+ else
+ {
+ sWorld.SetAllowMovement(true);
+ SendSysMessage(LANG_CREATURE_MOVE_ENABLED);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/)
+{
+ Player* SelectedPlayer = getSelectedPlayer();
+ if(!SelectedPlayer)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // each skills that have max skill value dependent from level seted to current level max skill value
+ SelectedPlayer->UpdateSkillsToMaxSkillsForLevel();
+ return true;
+}
+
+bool ChatHandler::HandleSetSkillCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hskill:skill_id|h[name]|h|r
+ char* skill_p = extractKeyFromLink((char*)args,"Hskill");
+ if(!skill_p)
+ return false;
+
+ char *level_p = strtok (NULL, " ");
+
+ if( !level_p)
+ return false;
+
+ char *max_p = strtok (NULL, " ");
+
+ int32 skill = atoi(skill_p);
+
+ if (skill <= 0)
+ {
+ PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 level = atol (level_p);
+
+ Player * target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SkillLineEntry const* sl = sSkillLineStore.LookupEntry(skill);
+ if(!sl)
+ {
+ PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!target->GetSkillValue(skill))
+ {
+ PSendSysMessage(LANG_SET_SKILL_ERROR, target->GetName(), skill, sl->name[0]);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 max = max_p ? atol (max_p) : target->GetPureMaxSkillValue(skill);
+
+ if( level <= 0 || level > max || max <= 0 )
+ return false;
+
+ target->SetSkill(skill, level, max);
+ PSendSysMessage(LANG_SET_SKILL, skill, sl->name[0], target->GetName(), level, max);
+
+ return true;
+}
+
+bool ChatHandler::HandleUnLearnCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ uint32 min_id = extractSpellIdFromLink((char*)args);
+ if(!min_id)
+ return false;
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ char* tail = strtok(NULL,"");
+
+ uint32 max_id = extractSpellIdFromLink(tail);
+
+ if (!max_id)
+ {
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ max_id = min_id+1;
+ }
+ else
+ {
+ if (max_id < min_id)
+ std::swap(min_id,max_id);
+
+ max_id=max_id+1;
+ }
+
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(uint32 spell=min_id;spell<max_id;spell++)
+ {
+ if (target->HasSpell(spell))
+ target->removeSpell(spell);
+ else
+ SendSysMessage(LANG_FORGET_SPELL);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleCooldownCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!*args)
+ {
+ target->RemoveAllSpellCooldown();
+ PSendSysMessage(LANG_REMOVEALL_COOLDOWN, target->GetName());
+ }
+ else
+ {
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell_id = extractSpellIdFromLink((char*)args);
+ if(!spell_id)
+ return false;
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ PSendSysMessage(LANG_UNKNOWN_SPELL, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldPacket data( SMSG_CLEAR_COOLDOWN, (4+8) );
+ data << uint32(spell_id);
+ data << uint64(target->GetGUID());
+ target->GetSession()->SendPacket(&data);
+ target->RemoveSpellCooldown(spell_id);
+ PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
+ }
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllCommand(const char* /*args*/)
+{
+ static const char *allSpellList[] =
+ {
+ "3365",
+ "6233",
+ "6247",
+ "6246",
+ "6477",
+ "6478",
+ "22810",
+ "8386",
+ "21651",
+ "21652",
+ "522",
+ "7266",
+ "8597",
+ "2479",
+ "22027",
+ "6603",
+ "5019",
+ "133",
+ "168",
+ "227",
+ "5009",
+ "9078",
+ "668",
+ "203",
+ "20599",
+ "20600",
+ "81",
+ "20597",
+ "20598",
+ "20864",
+ "1459",
+ "5504",
+ "587",
+ "5143",
+ "118",
+ "5505",
+ "597",
+ "604",
+ "1449",
+ "1460",
+ "2855",
+ "1008",
+ "475",
+ "5506",
+ "1463",
+ "12824",
+ "8437",
+ "990",
+ "5145",
+ "8450",
+ "1461",
+ "759",
+ "8494",
+ "8455",
+ "8438",
+ "6127",
+ "8416",
+ "6129",
+ "8451",
+ "8495",
+ "8439",
+ "3552",
+ "8417",
+ "10138",
+ "12825",
+ "10169",
+ "10156",
+ "10144",
+ "10191",
+ "10201",
+ "10211",
+ "10053",
+ "10173",
+ "10139",
+ "10145",
+ "10192",
+ "10170",
+ "10202",
+ "10054",
+ "10174",
+ "10193",
+ "12826",
+ "2136",
+ "143",
+ "145",
+ "2137",
+ "2120",
+ "3140",
+ "543",
+ "2138",
+ "2948",
+ "8400",
+ "2121",
+ "8444",
+ "8412",
+ "8457",
+ "8401",
+ "8422",
+ "8445",
+ "8402",
+ "8413",
+ "8458",
+ "8423",
+ "8446",
+ "10148",
+ "10197",
+ "10205",
+ "10149",
+ "10215",
+ "10223",
+ "10206",
+ "10199",
+ "10150",
+ "10216",
+ "10207",
+ "10225",
+ "10151",
+ "116",
+ "205",
+ "7300",
+ "122",
+ "837",
+ "10",
+ "7301",
+ "7322",
+ "6143",
+ "120",
+ "865",
+ "8406",
+ "6141",
+ "7302",
+ "8461",
+ "8407",
+ "8492",
+ "8427",
+ "8408",
+ "6131",
+ "7320",
+ "10159",
+ "8462",
+ "10185",
+ "10179",
+ "10160",
+ "10180",
+ "10219",
+ "10186",
+ "10177",
+ "10230",
+ "10181",
+ "10161",
+ "10187",
+ "10220",
+ "2018",
+ "2663",
+ "12260",
+ "2660",
+ "3115",
+ "3326",
+ "2665",
+ "3116",
+ "2738",
+ "3293",
+ "2661",
+ "3319",
+ "2662",
+ "9983",
+ "8880",
+ "2737",
+ "2739",
+ "7408",
+ "3320",
+ "2666",
+ "3323",
+ "3324",
+ "3294",
+ "22723",
+ "23219",
+ "23220",
+ "23221",
+ "23228",
+ "23338",
+ "10788",
+ "10790",
+ "5611",
+ "5016",
+ "5609",
+ "2060",
+ "10963",
+ "10964",
+ "10965",
+ "22593",
+ "22594",
+ "596",
+ "996",
+ "499",
+ "768",
+ "17002",
+ "1448",
+ "1082",
+ "16979",
+ "1079",
+ "5215",
+ "20484",
+ "5221",
+ "15590",
+ "17007",
+ "6795",
+ "6807",
+ "5487",
+ "1446",
+ "1066",
+ "5421",
+ "3139",
+ "779",
+ "6811",
+ "6808",
+ "1445",
+ "5216",
+ "1737",
+ "5222",
+ "5217",
+ "1432",
+ "6812",
+ "9492",
+ "5210",
+ "3030",
+ "1441",
+ "783",
+ "6801",
+ "20739",
+ "8944",
+ "9491",
+ "22569",
+ "5226",
+ "6786",
+ "1433",
+ "8973",
+ "1828",
+ "9495",
+ "9006",
+ "6794",
+ "8993",
+ "5203",
+ "16914",
+ "6784",
+ "9635",
+ "22830",
+ "20722",
+ "9748",
+ "6790",
+ "9753",
+ "9493",
+ "9752",
+ "9831",
+ "9825",
+ "9822",
+ "5204",
+ "5401",
+ "22831",
+ "6793",
+ "9845",
+ "17401",
+ "9882",
+ "9868",
+ "20749",
+ "9893",
+ "9899",
+ "9895",
+ "9832",
+ "9902",
+ "9909",
+ "22832",
+ "9828",
+ "9851",
+ "9883",
+ "9869",
+ "17406",
+ "17402",
+ "9914",
+ "20750",
+ "9897",
+ "9848",
+ "3127",
+ "107",
+ "204",
+ "9116",
+ "2457",
+ "78",
+ "18848",
+ "331",
+ "403",
+ "2098",
+ "1752",
+ "11278",
+ "11288",
+ "11284",
+ "6461",
+ "2344",
+ "2345",
+ "6463",
+ "2346",
+ "2352",
+ "775",
+ "1434",
+ "1612",
+ "71",
+ "2468",
+ "2458",
+ "2467",
+ "7164",
+ "7178",
+ "7367",
+ "7376",
+ "7381",
+ "21156",
+ "5209",
+ "3029",
+ "5201",
+ "9849",
+ "9850",
+ "20719",
+ "22568",
+ "22827",
+ "22828",
+ "22829",
+ "6809",
+ "8972",
+ "9005",
+ "9823",
+ "9827",
+ "6783",
+ "9913",
+ "6785",
+ "6787",
+ "9866",
+ "9867",
+ "9894",
+ "9896",
+ "6800",
+ "8992",
+ "9829",
+ "9830",
+ "780",
+ "769",
+ "6749",
+ "6750",
+ "9755",
+ "9754",
+ "9908",
+ "20745",
+ "20742",
+ "20747",
+ "20748",
+ "9746",
+ "9745",
+ "9880",
+ "9881",
+ "5391",
+ "842",
+ "3025",
+ "3031",
+ "3287",
+ "3329",
+ "1945",
+ "3559",
+ "4933",
+ "4934",
+ "4935",
+ "4936",
+ "5142",
+ "5390",
+ "5392",
+ "5404",
+ "5420",
+ "6405",
+ "7293",
+ "7965",
+ "8041",
+ "8153",
+ "9033",
+ "9034",
+ //"9036", problems with ghost state
+ "16421",
+ "21653",
+ "22660",
+ "5225",
+ "9846",
+ "2426",
+ "5916",
+ "6634",
+ //"6718", phasing stealth, annoing for learn all case.
+ "6719",
+ "8822",
+ "9591",
+ "9590",
+ "10032",
+ "17746",
+ "17747",
+ "8203",
+ "11392",
+ "12495",
+ "16380",
+ "23452",
+ "4079",
+ "4996",
+ "4997",
+ "4998",
+ "4999",
+ "5000",
+ "6348",
+ "6349",
+ "6481",
+ "6482",
+ "6483",
+ "6484",
+ "11362",
+ "11410",
+ "11409",
+ "12510",
+ "12509",
+ "12885",
+ "13142",
+ "21463",
+ "23460",
+ "11421",
+ "11416",
+ "11418",
+ "1851",
+ "10059",
+ "11423",
+ "11417",
+ "11422",
+ "11419",
+ "11424",
+ "11420",
+ "27",
+ "31",
+ "33",
+ "34",
+ "35",
+ "15125",
+ "21127",
+ "22950",
+ "1180",
+ "201",
+ "12593",
+ "12842",
+ "16770",
+ "6057",
+ "12051",
+ "18468",
+ "12606",
+ "12605",
+ "18466",
+ "12502",
+ "12043",
+ "15060",
+ "12042",
+ "12341",
+ "12848",
+ "12344",
+ "12353",
+ "18460",
+ "11366",
+ "12350",
+ "12352",
+ "13043",
+ "11368",
+ "11113",
+ "12400",
+ "11129",
+ "16766",
+ "12573",
+ "15053",
+ "12580",
+ "12475",
+ "12472",
+ "12953",
+ "12488",
+ "11189",
+ "12985",
+ "12519",
+ "16758",
+ "11958",
+ "12490",
+ "11426",
+ "3565",
+ "3562",
+ "18960",
+ "3567",
+ "3561",
+ "3566",
+ "3563",
+ "1953",
+ "2139",
+ "12505",
+ "13018",
+ "12522",
+ "12523",
+ "5146",
+ "5144",
+ "5148",
+ "8419",
+ "8418",
+ "10213",
+ "10212",
+ "10157",
+ "12524",
+ "13019",
+ "12525",
+ "13020",
+ "12526",
+ "13021",
+ "18809",
+ "13031",
+ "13032",
+ "13033",
+ "4036",
+ "3920",
+ "3919",
+ "3918",
+ "7430",
+ "3922",
+ "3923",
+ "7411",
+ "7418",
+ "7421",
+ "13262",
+ "7412",
+ "7415",
+ "7413",
+ "7416",
+ "13920",
+ "13921",
+ "7745",
+ "7779",
+ "7428",
+ "7457",
+ "7857",
+ "7748",
+ "7426",
+ "13421",
+ "7454",
+ "13378",
+ "7788",
+ "14807",
+ "14293",
+ "7795",
+ "6296",
+ "20608",
+ "755",
+ "444",
+ "427",
+ "428",
+ "442",
+ "447",
+ "3578",
+ "3581",
+ "19027",
+ "3580",
+ "665",
+ "3579",
+ "3577",
+ "6755",
+ "3576",
+ "2575",
+ "2577",
+ "2578",
+ "2579",
+ "2580",
+ "2656",
+ "2657",
+ "2576",
+ "3564",
+ "10248",
+ "8388",
+ "2659",
+ "14891",
+ "3308",
+ "3307",
+ "10097",
+ "2658",
+ "3569",
+ "16153",
+ "3304",
+ "10098",
+ "4037",
+ "3929",
+ "3931",
+ "3926",
+ "3924",
+ "3930",
+ "3977",
+ "3925",
+ "136",
+ "228",
+ "5487",
+ "43",
+ "202",
+ "0"
+ };
+
+ int loop = 0;
+ while(strcmp(allSpellList[loop], "0"))
+ {
+ uint32 spell = atol((char*)allSpellList[loop++]);
+
+ if (m_session->GetPlayer()->HasSpell(spell))
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ continue;
+ }
+
+ m_session->GetPlayer()->learnSpell(spell);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS);
+
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/)
+{
+ static const char *gmSpellList[] =
+ {
+ "24347", // Become A Fish, No Breath Bar
+ "35132", // Visual Boom
+ "38488", // Attack 4000-8000 AOE
+ "38795", // Attack 2000 AOE + Slow Down 90%
+ "15712", // Attack 200
+ "1852", // GM Spell Silence
+ "31899", // Kill
+ "31924", // Kill
+ "29878", // Kill My Self
+ "26644", // More Kill
+
+ "28550", //Invisible 24
+ "23452", //Invisible + Target
+ "0"
+ };
+
+ uint16 gmSpellIter = 0;
+ while( strcmp(gmSpellList[gmSpellIter], "0") )
+ {
+ uint32 spell = atol((char*)gmSpellList[gmSpellIter++]);
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ continue;
+ }
+
+ m_session->GetPlayer()->learnSpell(spell);
+ }
+
+ SendSysMessage(LANG_LEARNING_GM_SKILLS);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllMyClassCommand(const char* /*args*/)
+{
+ HandleLearnAllMySpellsCommand("");
+ HandleLearnAllMyTalentsCommand("");
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/)
+{
+ ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(m_session->GetPlayer()->getClass());
+ if(!clsEntry)
+ return true;
+ uint32 family = clsEntry->spellfamily;
+
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); i++)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ // skip wrong class/race skills
+ if(!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id))
+ continue;
+
+ // skip other spell families
+ if( spellInfo->SpellFamilyName != family)
+ continue;
+
+ //TODO: skip triggered spells
+
+ // skip spells with first rank learned as talent (and all talents then also)
+ uint32 first_rank = spellmgr.GetFirstSpellInChain(spellInfo->Id);
+ if(GetTalentSpellCost(first_rank) > 0 )
+ continue;
+
+ // skip broken spells
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ m_session->GetPlayer()->learnSpell(i);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS);
+ return true;
+}
+
+static void learnAllHighRanks(Player* player, uint32 spellid)
+{
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator itr = nextMap.lower_bound(spellid); itr != nextMap.upper_bound(spellid); ++itr)
+ {
+ player->learnSpell(itr->second);
+ learnAllHighRanks(player,itr->second);
+ }
+}
+
+bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/)
+{
+ Player* player = m_session->GetPlayer();
+ uint32 classMask = player->getClassMask();
+
+ for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
+ {
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
+ if(!talentInfo)
+ continue;
+
+ TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+ if(!talentTabInfo)
+ continue;
+
+ if( (classMask & talentTabInfo->ClassMask) == 0 )
+ continue;
+
+ // search highest talent rank
+ uint32 spellid = 0;
+ int rank = 4;
+ for(; rank >= 0; --rank)
+ {
+ if(talentInfo->RankID[rank]!=0)
+ {
+ spellid = talentInfo->RankID[rank];
+ break;
+ }
+ }
+
+ if(!spellid) // ??? none spells in telent
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ // learn highest rank of talent
+ player->learnSpell(spellid);
+
+ // and learn all non-talent spell ranks (recursive by tree)
+ learnAllHighRanks(player,spellid);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/)
+{
+ // skipping UNIVERSAL language (0)
+ for(int i = 1; i < LANGUAGES_COUNT; ++i)
+ m_session->GetPlayer()->learnSpell(lang_description[i].spell_id);
+
+ SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllDefaultCommand(const char* args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player->learnDefaultSpells();
+ player->learnQuestRewardedSpells();
+
+ PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,player->GetName());
+ return true;
+}
+
+bool ChatHandler::HandleLearnCommand(const char* args)
+{
+ Player* targetPlayer = getSelectedPlayer();
+
+ if(!targetPlayer)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ if (targetPlayer->HasSpell(spell))
+ {
+ if(targetPlayer == m_session->GetPlayer())
+ SendSysMessage(LANG_YOU_KNOWN_SPELL);
+ else
+ PSendSysMessage(LANG_TARGET_KNOWN_SPELL,targetPlayer->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ targetPlayer->learnSpell(spell);
+
+ return true;
+}
+
+bool ChatHandler::HandleAddItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 itemId = 0;
+
+ if(args[0]=='[') // [name] manual form
+ {
+ char* citemName = citemName = strtok((char*)args, "]");
+
+ if(citemName && citemName[0])
+ {
+ std::string itemName = citemName+1;
+ WorldDatabase.escape_string(itemName);
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE name = '%s'", itemName.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_COMMAND_COULDNOTFIND, citemName+1);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ itemId = result->Fetch()->GetUInt16();
+ delete result;
+ }
+ else
+ return false;
+ }
+ else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r
+ {
+ char* cId = extractKeyFromLink((char*)args,"Hitem");
+ if(!cId)
+ return false;
+ itemId = atol(cId);
+ }
+
+ char* ccount = strtok(NULL, " ");
+
+ int32 count = 1;
+
+ if (ccount)
+ count = strtol(ccount, NULL, 10);
+
+ if (count == 0)
+ count = 1;
+
+ Player* pl = m_session->GetPlayer();
+ Player* plTarget = getSelectedPlayer();
+ if(!plTarget)
+ plTarget = pl;
+
+ sLog.outDetail(GetMangosString(LANG_ADDITEM), itemId, count);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
+ if(!pProto)
+ {
+ PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //Subtract
+ if (count < 0)
+ {
+ plTarget->DestroyItemCount(itemId, -count, true, false);
+ PSendSysMessage(LANG_REMOVEITEM, itemId, -count, plTarget->GetName());
+ return true;
+ }
+
+ //Adding items
+ uint32 noSpaceForCount = 0;
+
+ // check space and find places
+ ItemPosCountVec dest;
+ uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
+ if( msg != EQUIP_ERR_OK ) // convert to possible store amount
+ count -= noSpaceForCount;
+
+ if( count == 0 || dest.empty()) // can't add any
+ {
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount );
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Item* item = plTarget->StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
+
+ // remove binding (let GM give it to another player later)
+ if(pl==plTarget)
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
+ if(Item* item1 = pl->GetItemByPos(itr->pos))
+ item1->SetBinding( false );
+
+ if(count > 0 && item)
+ {
+ pl->SendNewItem(item,count,false,true);
+ if(pl!=plTarget)
+ plTarget->SendNewItem(item,count,true,false);
+ }
+
+ if(noSpaceForCount > 0)
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount);
+
+ return true;
+}
+
+bool ChatHandler::HandleAddItemSetCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char* cId = extractKeyFromLink((char*)args,"Hitemset"); // number or [name] Shift-click form |color|Hitemset:itemset_id|h[name]|h|r
+ if (!cId)
+ return false;
+
+ uint32 itemsetId = atol(cId);
+
+ // prevent generation all items with itemset field value '0'
+ if (itemsetId == 0)
+ {
+ PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player* pl = m_session->GetPlayer();
+ Player* plTarget = getSelectedPlayer();
+ if(!plTarget)
+ plTarget = pl;
+
+ sLog.outDetail(GetMangosString(LANG_ADDITEMSET), itemsetId);
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE itemset = %u",itemsetId);
+
+ if(!result)
+ {
+ PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
+
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 itemId = fields[0].GetUInt32();
+
+ ItemPosCountVec dest;
+ uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, 1 );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = plTarget->StoreNewItem( dest, itemId, true);
+
+ // remove binding (let GM give it to another player later)
+ if(pl==plTarget)
+ item->SetBinding( false );
+
+ pl->SendNewItem(item,1,false,true);
+ if(pl!=plTarget)
+ plTarget->SendNewItem(item,1,true,false);
+ }
+ else
+ {
+ pl->SendEquipError( msg, NULL, NULL );
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, 1);
+ }
+
+ }while( result->NextRow() );
+
+ delete result;
+
+ return true;
+}
+
+bool ChatHandler::HandleListItemCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* cId = extractKeyFromLink((char*)args,"Hitem");
+ if(!cId)
+ return false;
+ uint32 item_id = atol(cId);
+
+ ItemPrototype const* itemProto = item_id ? itemProto = objmgr.GetItemPrototype(item_id) : NULL;
+
+ if(!itemProto)
+ {
+ PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ QueryResult *result;
+
+ // inventory case
+ uint32 inv_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM character_inventory WHERE item_template='%u'",item_id);
+ if(result)
+ {
+ inv_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5
+ "SELECT ci.item, cibag.slot AS bag, ci.slot, ci.guid, characters.account,characters.name "
+ "FROM character_inventory AS ci LEFT JOIN character_inventory AS cibag ON (cibag.item=ci.bag),characters "
+ "WHERE ci.item_template='%u' AND ci.guid = characters.guid LIMIT %u ",
+ item_id,uint32(count));
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_bag = fields[1].GetUInt32();
+ uint32 item_slot = fields[2].GetUInt32();
+ uint32 owner_guid = fields[3].GetUInt32();
+ uint32 owner_acc = fields[4].GetUInt32();
+ std::string owner_name = fields[5].GetCppString();
+
+ char const* item_pos = 0;
+ if(Player::IsEquipmentPos(item_bag,item_slot))
+ item_pos = "[equipped]";
+ else if(Player::IsInventoryPos(item_bag,item_slot))
+ item_pos = "[in inventory]";
+ else if(Player::IsBankPos(item_bag,item_slot))
+ item_pos = "[in bank]";
+ else
+ item_pos = "";
+
+ PSendSysMessage(LANG_ITEMLIST_SLOT,
+ item_guid,owner_name.c_str(),owner_guid,owner_acc,item_pos);
+ } while (result->NextRow());
+
+ int64 res_count = result->GetRowCount();
+
+ delete result;
+
+ if(count > res_count)
+ count-=res_count;
+ else if(count)
+ count = 0;
+ }
+
+ // mail case
+ uint32 mail_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM mail_items WHERE item_template='%u'", item_id);
+ if(result)
+ {
+ mail_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ if(count > 0)
+ {
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5 6
+ "SELECT mail_items.item_guid, mail.sender, mail.receiver, char_s.account, char_s.name, char_r.account, char_r.name "
+ "FROM mail,mail_items,characters as char_s,characters as char_r "
+ "WHERE mail_items.item_template='%u' AND char_s.guid = mail.sender AND char_r.guid = mail.receiver AND mail.id=mail_items.mail_id LIMIT %u",
+ item_id,uint32(count));
+ }
+ else
+ result = NULL;
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_s = fields[1].GetUInt32();
+ uint32 item_r = fields[2].GetUInt32();
+ uint32 item_s_acc = fields[3].GetUInt32();
+ std::string item_s_name = fields[4].GetCppString();
+ uint32 item_r_acc = fields[5].GetUInt32();
+ std::string item_r_name = fields[6].GetCppString();
+
+ char const* item_pos = "[in mail]";
+
+ PSendSysMessage(LANG_ITEMLIST_MAIL,
+ item_guid,item_s_name.c_str(),item_s,item_s_acc,item_r_name.c_str(),item_r,item_r_acc,item_pos);
+ } while (result->NextRow());
+
+ int64 res_count = result->GetRowCount();
+
+ delete result;
+
+ if(count > res_count)
+ count-=res_count;
+ else if(count)
+ count = 0;
+ }
+
+ // auction case
+ uint32 auc_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM auctionhouse WHERE item_template='%u'",item_id);
+ if(result)
+ {
+ auc_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ if(count > 0)
+ {
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3
+ "SELECT auctionhouse.itemguid, auctionhouse.itemowner, characters.account, characters.name "
+ "FROM auctionhouse,characters WHERE auctionhouse.item_template='%u' AND characters.guid = auctionhouse.itemowner LIMIT %u",
+ item_id,uint32(count));
+ }
+ else
+ result = NULL;
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 owner = fields[1].GetUInt32();
+ uint32 owner_acc = fields[2].GetUInt32();
+ std::string owner_name = fields[3].GetCppString();
+
+ char const* item_pos = "[in auction]";
+
+ PSendSysMessage(LANG_ITEMLIST_AUCTION, item_guid, owner_name.c_str(), owner, owner_acc,item_pos);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ if(inv_count+mail_count+auc_count == 0)
+ {
+ SendSysMessage(LANG_COMMAND_NOITEMFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTITEMMESSAGE,item_id,inv_count+mail_count+auc_count,inv_count,mail_count,auc_count);
+
+ return true;
+}
+
+bool ChatHandler::HandleListObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry");
+ if(!cId)
+ return false;
+
+ uint32 go_id = atol(cId);
+
+ GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(go_id);
+
+ if(!go_id || !gInfo)
+ {
+ PSendSysMessage(LANG_COMMAND_LISTOBJINVALIDID, go_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+
+ uint32 obj_count = 0;
+ result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM gameobject WHERE id='%u'",go_id);
+ if(result)
+ {
+ obj_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),go_id,uint32(count));
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTOBJMESSAGE,go_id,obj_count);
+ return true;
+}
+
+bool ChatHandler::HandleNearObjectCommand(const char* args)
+{
+ float distance = (!*args) ? 10 : atol(args);
+ uint32 count = 0;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, "
+ "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ "
+ "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),
+ pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance);
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ uint32 entry = fields[1].GetUInt32();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
+ int mapid = fields[5].GetUInt16();
+
+ GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry);
+
+ if(!gInfo)
+ continue;
+
+ PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count);
+ return true;
+}
+
+bool ChatHandler::HandleListCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature_entry");
+ if(!cId)
+ return false;
+
+ uint32 cr_id = atol(cId);
+
+ CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(cr_id);
+
+ if(!cr_id || !cInfo)
+ {
+ PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, cr_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+
+ uint32 cr_count = 0;
+ result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id='%u'",cr_id);
+ if(result)
+ {
+ cr_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM creature WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), cr_id,uint32(count));
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ PSendSysMessage(LANG_CREATURE_LIST, guid, guid, cInfo->Name, x, y, z, mapid);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTCREATUREMESSAGE,cr_id,cr_count);
+ return true;
+}
+
+bool ChatHandler::HandleLookupItemCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ // Search in `item_template`
+ for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
+ {
+ ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype >(id);
+ if(!pProto)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ {
+ std::string name = il->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = pProto->Name1;
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOITEMFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupItemSetCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ 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.
+
+ // Search in ItemSet.dbc
+ for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++)
+ {
+ ItemSetEntry const *set = sItemSetStore.LookupEntry(id);
+ if(set)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = set->name[m_session->GetSessionDbcLocale()];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = set->name[m_session->GetSessionDbcLocale()];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send item set in "id - [namedlink locale]" format
+ PSendSysMessage(LANG_ITEMSET_LIST,id,id,name.c_str(),localeNames[loc]);
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOITEMSETFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupSkillCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ 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.
+
+ // Search in SkillLine.dbc
+ for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(id);
+ if(skillInfo)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = skillInfo->name[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = skillInfo->name[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send skill in "id - [namedlink locale]" format
+ PSendSysMessage(LANG_SKILL_LIST,id,id,name.c_str(),localeNames[loc],(target->HasSkill(id) ? m_session->GetMangosString(LANG_KNOWN) : ""));
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOSKILLFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupSpellCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ 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.
+
+ // Search in Spell.dbc
+ for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(id);
+ if(spellInfo)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = spellInfo->SpellName[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = spellInfo->SpellName[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ bool known = target->HasSpell(id);
+ bool learn = (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL);
+
+ uint32 telentCost = GetTalentSpellCost(id);
+
+ bool talent = (telentCost > 0);
+ bool passive = IsPassiveSpell(id);
+ bool active = target->HasAura(id,0) || target->HasAura(id,1) || target->HasAura(id,2);
+
+ // unit32 used to prevent interpreting uint8 as char at output
+ // find rank of learned spell for learning spell, or talent rank
+ uint32 rank = telentCost ? telentCost : spellmgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[0] : id);
+
+ // send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
+ std::ostringstream ss;
+ ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
+
+ // include rank in link name
+ if(rank)
+ ss << GetMangosString(LANG_SPELL_RANK) << rank;
+
+ ss << " " << localeNames[loc] << "]|h|r";
+
+ if(talent)
+ ss << GetMangosString(LANG_TALENT);
+ if(passive)
+ ss << GetMangosString(LANG_PASSIVE);
+ if(learn)
+ ss << GetMangosString(LANG_LEARN);
+ if(known)
+ ss << GetMangosString(LANG_KNOWN);
+ if(active)
+ ss << GetMangosString(LANG_ACTIVE);
+
+ SendSysMessage(ss.str().c_str());
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOSPELLFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupQuestCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0 ;
+
+ ObjectMgr::QuestMap const& qTemplates = objmgr.GetQuestTemplates();
+ for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter)
+ {
+ Quest * qinfo = iter->second;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ QuestLocale const *il = objmgr.GetQuestLocale(qinfo->GetQuestId());
+ if (il)
+ {
+ if (il->Title.size() > loc_idx && !il->Title[loc_idx].empty())
+ {
+ std::string title = il->Title[loc_idx];
+
+ if (Utf8FitTo(title, wnamepart))
+ {
+ QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
+ char const* statusStr = "";
+ if(status == QUEST_STATUS_COMPLETE)
+ {
+ if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
+ else
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
+ }
+ else if(status == QUEST_STATUS_INCOMPLETE)
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
+
+ PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(),title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string title = qinfo->GetTitle();
+ if(title.empty())
+ continue;
+
+ if (Utf8FitTo(title, wnamepart))
+ {
+ QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
+ char const* statusStr = "";
+ if(status == QUEST_STATUS_COMPLETE)
+ {
+ if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
+ else
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
+ }
+ else if(status == QUEST_STATUS_INCOMPLETE)
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
+
+ PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(), title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOQUESTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ for (uint32 id = 0; id< sCreatureStorage.MaxEntry; id++ )
+ {
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(id);
+ if(!cInfo)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ CreatureLocale const *cl = objmgr.GetCreatureLocale(id);
+ if (cl)
+ {
+ if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty())
+ {
+ std::string name = cl->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_CREATURE_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = cInfo->Name;
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_CREATURE_ENTRY_LIST,id,id,name.c_str());
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOCREATUREFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ for (uint32 id = 0; id< sGOStorage.MaxEntry; id++ )
+ {
+ GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
+ if(!gInfo)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ GameObjectLocale const *gl = objmgr.GetGameObjectLocale(id);
+ if (gl)
+ {
+ if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty())
+ {
+ std::string name = gl->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = gInfo->name;
+ if(name.empty())
+ continue;
+
+ if(Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ }
+ }
+
+ if(counter==0)
+ SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND);
+
+ return true;
+}
+
+/** \brief GM command level 3 - Create a guild.
+ *
+ * This command allows a GM (level 3) to create a guild.
+ *
+ * The "args" parameter contains the name of the guild leader
+ * and then the name of the guild.
+ *
+ */
+bool ChatHandler::HandleGuildCreateCommand(const char* args)
+{
+
+ if (!*args)
+ return false;
+
+ Guild *guild;
+ Player * player;
+ char *lname,*gname;
+ std::string guildname;
+
+ lname = strtok((char*)args, " ");
+ gname = strtok(NULL, "");
+
+ if(!lname)
+ return false;
+ else if(!gname)
+ {
+ SendSysMessage(LANG_INSERT_GUILD_NAME);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ guildname = gname;
+ player = ObjectAccessor::Instance().FindPlayerByName(lname);
+
+ if(!player)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!player->GetGuildId())
+ {
+ guild = new Guild;
+ if(!guild->create(player->GetGUID(),guildname))
+ {
+ delete guild;
+ SendSysMessage(LANG_GUILD_NOT_CREATED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ objmgr.AddGuild(guild);
+ }
+ else
+ SendSysMessage(LANG_PLAYER_IN_GUILD);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildInviteCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ char* par2 = strtok (NULL, "");
+ if(!par1 || !par2)
+ return false;
+
+ std::string glName = par2;
+ Guild* targetGuild = objmgr.GetGuildByName(glName);
+ if(!targetGuild)
+ return false;
+
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ plGuid = targetPlayer->GetGUID();
+ else
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+
+ if(!plGuid)
+ false;
+
+ // players's guild membership checked in AddMember before add
+ if(!targetGuild->AddMember(plGuid,targetGuild->GetLowestRank()))
+ return false;
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildUninviteCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ if(!par1)
+ return false;
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ uint32 glId = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ {
+ plGuid = targetPlayer->GetGUID();
+ glId = targetPlayer->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+ glId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!plGuid || !glId)
+ return false;
+
+ Guild* targetGuild = objmgr.GetGuildById(glId);
+ if(!targetGuild)
+ return false;
+
+ targetGuild->DelMember(plGuid);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildRankCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ char* par2 = strtok(NULL, " ");
+ if(!par1 || !par2)
+ return false;
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ uint32 glId = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ {
+ plGuid = targetPlayer->GetGUID();
+ glId = targetPlayer->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+ glId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!plGuid || !glId)
+ return false;
+
+ Guild* targetGuild = objmgr.GetGuildById(glId);
+ if(!targetGuild)
+ return false;
+
+ uint32 newrank = uint32(atoi(par2));
+ if(newrank > targetGuild->GetLowestRank())
+ return false;
+
+ targetGuild->ChangeRank(plGuid,newrank);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildDeleteCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ if(!par1)
+ return false;
+
+ std::string gld = par1;
+
+ Guild* targetGuild = objmgr.GetGuildByName(gld);
+ if(!targetGuild)
+ return false;
+
+ targetGuild->Disband();
+
+ return true;
+}
+
+bool ChatHandler::HandleGetDistanceCommand(const char* /*args*/)
+{
+ Unit* pUnit = getSelectedUnit();
+
+ if(!pUnit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(pUnit),m_session->GetPlayer()->GetDistance2d(pUnit));
+
+ return true;
+}
+
+// FIX-ME!!!
+
+bool ChatHandler::HandleAddWeaponCommand(const char* /*args*/)
+{
+ /*if (!*args)
+ return false;
+
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ char* pSlotID = strtok((char*)args, " ");
+ if (!pSlotID)
+ return false;
+
+ char* pItemID = strtok(NULL, " ");
+ if (!pItemID)
+ return false;
+
+ uint32 ItemID = atoi(pItemID);
+ uint32 SlotID = atoi(pSlotID);
+
+ ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID);
+
+ bool added = false;
+ if(tmpItem)
+ {
+ switch(SlotID)
+ {
+ case 1:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID);
+ added = true;
+ break;
+ case 2:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID);
+ added = true;
+ break;
+ case 3:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID);
+ added = true;
+ break;
+ default:
+ PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID);
+ added = false;
+ break;
+ }
+ if(added)
+ {
+ PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID);
+ }
+ }
+ else
+ {
+ PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID);
+ return true;
+ }
+ */
+ return true;
+}
+
+bool ChatHandler::HandleDieCommand(const char* /*args*/)
+{
+ Unit* target = getSelectedUnit();
+
+ if(!target || !m_session->GetPlayer()->GetSelection())
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if( target->isAlive() )
+ {
+ m_session->GetPlayer()->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleDamageCommand(const char * args)
+{
+ if (!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target || !m_session->GetPlayer()->GetSelection())
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if( !target->isAlive() )
+ return true;
+
+ char* damageStr = strtok((char*)args, " ");
+ if(!damageStr)
+ return false;
+
+ int32 damage = atoi((char*)damageStr);
+ if(damage <=0)
+ return true;
+
+ char* schoolStr = strtok((char*)NULL, " ");
+
+ // flat melee damage without resistence/etc reduction
+ if(!schoolStr)
+ {
+ m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_NORMAL, 0);
+ return true;
+ }
+
+ uint32 school = schoolStr ? atoi((char*)schoolStr) : SPELL_SCHOOL_NORMAL;
+ if(school >= MAX_SPELL_SCHOOL)
+ return false;
+
+ SpellSchoolMask schoolmask = SpellSchoolMask(1 << school);
+
+ if ( schoolmask & SPELL_SCHOOL_MASK_NORMAL )
+ damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage);
+
+ char* spellStr = strtok((char*)NULL, " ");
+
+ // melee damage by specific school
+ if(!spellStr)
+ {
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ m_session->GetPlayer()->CalcAbsorbResist(target,schoolmask, SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ if (damage <= absorb + resist)
+ return true;
+
+ damage -= absorb + resist;
+
+ m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false);
+ m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, schoolmask, damage, absorb, resist, VICTIMSTATE_NORMAL, 0);
+ return true;
+ }
+
+ // non-melee damage
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spellid = extractSpellIdFromLink((char*)args);
+ if(!spellid || !sSpellStore.LookupEntry(spellid))
+ return false;
+
+ m_session->GetPlayer()->SpellNonMeleeDamageLog(target, spellid, damage, false);
+ return true;
+}
+
+bool ChatHandler::HandleModifyArenaCommand(const char * args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 amount = (uint32)atoi(args);
+
+ target->ModifyArenaPoints(amount);
+
+ PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, target->GetName(), target->GetArenaPoints());
+
+ return true;
+}
+
+bool ChatHandler::HandleReviveCommand(const char* args)
+{
+ Player* SelectedPlayer = NULL;
+
+ if (*args)
+ {
+ std::string name = args;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SelectedPlayer = objmgr.GetPlayer(name.c_str());
+ }
+ else
+ SelectedPlayer = getSelectedPlayer();
+
+ if(!SelectedPlayer)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SelectedPlayer->ResurrectPlayer(0.5f);
+ SelectedPlayer->SpawnCorpseBones();
+ SelectedPlayer->SaveToDB();
+ return true;
+}
+
+bool ChatHandler::HandleAuraCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 spellID = (uint32)atoi(px);
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID );
+ if(spellInfo)
+ {
+ for(uint32 i = 0;i<3;i++)
+ {
+ uint8 eff = spellInfo->Effect[i];
+ if (eff>=TOTAL_SPELL_EFFECTS)
+ continue;
+ if( IsAreaAuraEffect(eff) ||
+ eff == SPELL_EFFECT_APPLY_AURA ||
+ eff == SPELL_EFFECT_PERSISTENT_AREA_AURA )
+ {
+ Aura *Aur = CreateAura(spellInfo, i, NULL, target);
+ target->AddAura(Aur);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleUnAuraCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string argstr = args;
+ if (argstr == "all")
+ {
+ target->RemoveAllAuras();
+ return true;
+ }
+
+ uint32 spellID = (uint32)atoi(px);
+ target->RemoveAurasDueToSpell(spellID);
+
+ return true;
+}
+
+bool ChatHandler::HandleLinkGraveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ uint32 g_id = (uint32)atoi(px);
+
+ uint32 g_team;
+
+ char* px2 = strtok(NULL, " ");
+
+ if (!px2)
+ g_team = 0;
+ else if (strncmp(px2,"horde",6)==0)
+ g_team = HORDE;
+ else if (strncmp(px2,"alliance",9)==0)
+ g_team = ALLIANCE;
+ else
+ return false;
+
+ WorldSafeLocsEntry const* graveyard = sWorldSafeLocsStore.LookupEntry(g_id);
+
+ if(!graveyard )
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, g_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player* player = m_session->GetPlayer();
+
+ uint32 zoneId = player->GetZoneId();
+
+ AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
+ if(!areaEntry || areaEntry->zone !=0 )
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, g_id,zoneId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(graveyard->map_id != areaEntry->mapid && g_team != 0)
+ {
+ SendSysMessage(LANG_COMMAND_GRAVEYARDWRONGTEAM);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(objmgr.AddGraveYardLink(g_id,player->GetZoneId(),g_team))
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDLINKED, g_id,zoneId);
+ else
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDALRLINKED, g_id,zoneId);
+
+ return true;
+}
+
+bool ChatHandler::HandleNearGraveCommand(const char* args)
+{
+ uint32 g_team;
+
+ size_t argslen = strlen(args);
+
+ if(!*args)
+ g_team = 0;
+ else if (strncmp((char*)args,"horde",argslen)==0)
+ g_team = HORDE;
+ else if (strncmp((char*)args,"alliance",argslen)==0)
+ g_team = ALLIANCE;
+ else
+ return false;
+
+ Player* player = m_session->GetPlayer();
+
+ WorldSafeLocsEntry const* graveyard = objmgr.GetClosestGraveYard(
+ player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(),g_team);
+
+ if(graveyard)
+ {
+ uint32 g_id = graveyard->ID;
+
+ GraveYardData const* data = objmgr.FindGraveYardData(g_id,player->GetZoneId());
+ if (!data)
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDERROR,g_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ g_team = data->team;
+
+ std::string team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_NOTEAM);
+
+ if(g_team == 0)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
+ else if(g_team == HORDE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
+ else if(g_team == ALLIANCE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),player->GetZoneId());
+ }
+ else
+ {
+ std::string team_name;
+
+ if(g_team == 0)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
+ else if(g_team == HORDE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
+ else if(g_team == ALLIANCE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
+ if(g_team == ~uint32(0))
+ PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, player->GetZoneId());
+ else
+ PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, player->GetZoneId(),team_name.c_str());
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleSpawnTransportCommand(const char* /*args*/)
+{
+ return true;
+}
+
+//play npc emote
+bool ChatHandler::HandlePlayEmoteCommand(const char* args)
+{
+ uint32 emote = atoi((char*)args);
+
+ Creature* target = getSelectedCreature();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote);
+
+ return true;
+}
+
+bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
+{
+ Creature* target = getSelectedCreature();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 faction = target->getFaction();
+ uint32 npcflags = target->GetUInt32Value(UNIT_NPC_FLAGS);
+ uint32 displayid = target->GetDisplayId();
+ uint32 nativeid = target->GetNativeDisplayId();
+ uint32 Entry = target->GetEntry();
+ CreatureInfo const* cInfo = target->GetCreatureInfo();
+
+ int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
+ if(curRespawnDelay < 0)
+ curRespawnDelay = 0;
+ std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
+ std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
+
+ PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(), faction, npcflags, Entry, displayid, nativeid);
+ PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
+ PSendSysMessage(LANG_NPCINFO_HEALTH,target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
+ PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUInt32Value(UNIT_FIELD_FLAGS), target->GetUInt32Value(UNIT_DYNAMIC_FLAGS), target->getFaction());
+ PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
+ PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,cInfo->pickpocketLootId,cInfo->SkinLootId);
+ PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
+ PSendSysMessage(LANG_NPCINFO_POSITION,float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
+
+ if ((npcflags & UNIT_NPC_FLAG_VENDOR) )
+ {
+ SendSysMessage(LANG_NPCINFO_VENDOR);
+ }
+ if ((npcflags & UNIT_NPC_FLAG_TRAINER) )
+ {
+ SendSysMessage(LANG_NPCINFO_TRAINER);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleExploreCheatCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int flag = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (flag != 0)
+ {
+ PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, chr->GetName());
+ if(chr!=m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,m_session->GetPlayer()->GetName());
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, chr->GetName());
+ if(chr!=m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,m_session->GetPlayer()->GetName());
+ }
+
+ for (uint8 i=0; i<128; i++)
+ {
+ if (flag != 0)
+ {
+ m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
+ }
+ else
+ {
+ m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0);
+ }
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleHoverCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ uint32 flag;
+ if (!px)
+ flag = 1;
+ else
+ flag = atoi(px);
+
+ m_session->GetPlayer()->SetHover(flag);
+
+ if (flag)
+ SendSysMessage(LANG_HOVER_ENABLED);
+ else
+ SendSysMessage(LANG_HOVER_DISABLED);
+
+ return true;
+}
+
+bool ChatHandler::HandleLevelUpCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ char* py = strtok((char*)NULL, " ");
+
+ // command format parsing
+ char* pname = (char*)NULL;
+ int addlevel = 1;
+
+ if(px && py) // .levelup name level
+ {
+ addlevel = atoi(py);
+ pname = px;
+ }
+ else if(px && !py) // .levelup name OR .levelup level
+ {
+ if(isalpha(px[0])) // .levelup name
+ pname = px;
+ else // .levelup level
+ addlevel = atoi(px);
+ }
+ // else .levelup - nothing do for prepering
+
+ // player
+ Player *chr = NULL;
+ uint64 chr_guid = 0;
+
+ std::string name;
+
+ if(pname) // player by name
+ {
+ name = pname;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr = objmgr.GetPlayer(name.c_str());
+ if(!chr) // not in game
+ {
+ chr_guid = objmgr.GetPlayerGUIDByName(name);
+ if (chr_guid == 0)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ }
+ else // player by selection
+ {
+ chr = getSelectedPlayer();
+
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ name = chr->GetName();
+ }
+
+ assert(chr || chr_guid);
+
+ int32 oldlevel = chr ? chr->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,chr_guid);
+ int32 newlevel = oldlevel + addlevel;
+ if(newlevel < 1)
+ newlevel = 1;
+ if(newlevel > 255) // hardcoded maximum level
+ newlevel = 255;
+
+ if(chr)
+ {
+ chr->GiveLevel(newlevel);
+ chr->InitTalentForLevel();
+ chr->SetUInt32Value(PLAYER_XP,0);
+
+ if(oldlevel == newlevel)
+ ChatHandler(chr).SendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET);
+ else
+ if(oldlevel < newlevel)
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_UP,newlevel-oldlevel);
+ else
+ if(oldlevel > newlevel)
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_DOWN,newlevel-oldlevel);
+ }
+ else
+ {
+ // update levle and XP at level, all other will be updated at loading
+ Tokens values;
+ Player::LoadValuesArrayFromDB(values,chr_guid);
+ Player::SetUInt32ValueInArray(values,UNIT_FIELD_LEVEL,newlevel);
+ Player::SetUInt32ValueInArray(values,PLAYER_XP,0);
+ Player::SaveValuesArrayInDB(values,chr_guid);
+ }
+
+ if(m_session->GetPlayer() != chr) // including chr==NULL
+ PSendSysMessage(LANG_YOU_CHANGE_LVL,name.c_str(),newlevel);
+ return true;
+}
+
+bool ChatHandler::HandleShowAreaCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int area = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int offset = area / 32;
+ uint32 val = (uint32)(1 << (area % 32));
+
+ if(offset >= 128)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+ chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
+
+ SendSysMessage(LANG_EXPLORE_AREA);
+ return true;
+}
+
+bool ChatHandler::HandleHideAreaCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int area = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int offset = area / 32;
+ uint32 val = (uint32)(1 << (area % 32));
+
+ if(offset >= 128)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+ chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields ^ val));
+
+ SendSysMessage(LANG_UNEXPLORE_AREA);
+ return true;
+}
+
+bool ChatHandler::HandleUpdate(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() );
+
+ return true;
+}
+
+bool ChatHandler::HandleChangeWeather(const char* args)
+{
+ if(!*args)
+ return false;
+
+ //Weather is OFF
+ if (!sWorld.getConfig(CONFIG_WEATHER))
+ {
+ SendSysMessage(LANG_WEATHER_DISABLED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //*Change the weather of a cell
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ uint32 type = (uint32)atoi(px); //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand
+ float grade = (float)atof(py); //0 to 1, sending -1 is instand good weather
+
+ Player *player = m_session->GetPlayer();
+ uint32 zoneid = player->GetZoneId();
+
+ Weather* wth = sWorld.FindWeather(zoneid);
+
+ if(!wth)
+ wth = sWorld.AddWeather(zoneid);
+ if(!wth)
+ {
+ SendSysMessage(LANG_NO_WEATHER);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ wth->SetWeather(WeatherType(type), grade);
+
+ return true;
+}
+
+bool ChatHandler::HandleSetValue(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;
+
+ 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 = (uint32)atoi(py);
+ sLog.outDebug(GetMangosString(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(GetMangosString(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::HandleGetValue(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(GetMangosString(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(GetMangosString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
+ PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleSet32Bit(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);
+ uint32 Value = (uint32)atoi(py);
+ if (Value > 32) //uint32 = 32 bits
+ return false;
+
+ sLog.outDebug(GetMangosString(LANG_SET_32BIT), Opcode, Value);
+
+ m_session->GetPlayer( )->SetUInt32Value( Opcode , 2^Value );
+
+ PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode,1);
+ return true;
+}
+
+bool ChatHandler::HandleMod32Value(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(GetMangosString(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::HandleAddTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+ QueryResult *result;
+ Player *player=m_session->GetPlayer();
+ if (!player) return false;
+
+ std::string name = args;
+ WorldDatabase.escape_string(name);
+ result = WorldDatabase.PQuery("SELECT id FROM game_tele WHERE name = '%s'",name.c_str());
+ if (result)
+ {
+ SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
+ delete result;
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ float x = player->GetPositionX();
+ float y = player->GetPositionY();
+ float z = player->GetPositionZ();
+ float ort = player->GetOrientation();
+ int mapid = player->GetMapId();
+
+ if(WorldDatabase.PExecuteLog("INSERT INTO game_tele (position_x,position_y,position_z,orientation,map,name) VALUES (%f,%f,%f,%f,%d,'%s')",x,y,z,ort,mapid,name.c_str()))
+ {
+ SendSysMessage(LANG_COMMAND_TP_ADDED);
+ }
+ else
+ {
+ SendSysMessage(LANG_COMMAND_TP_ADDEDERR);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleDelTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+ WorldDatabase.escape_string(name);
+
+ QueryResult *result=WorldDatabase.PQuery("SELECT id FROM game_tele WHERE name = '%s'",name.c_str());
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ delete result;
+
+ if(WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",name.c_str()))
+ {
+ SendSysMessage(LANG_COMMAND_TP_DELETED);
+ }
+ else
+ {
+ SendSysMessage(LANG_COMMAND_TP_DELETEERR);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ return true;
+}
+
+bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
+{
+ Unit *unit = getSelectedUnit();
+ if(!unit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char const* talentStr = GetMangosString(LANG_TALENT);
+ char const* passiveStr = GetMangosString(LANG_PASSIVE);
+
+ Unit::AuraMap const& uAuras = unit->GetAuras();
+ PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, uAuras.size());
+ for (Unit::AuraMap::const_iterator itr = uAuras.begin(); itr != uAuras.end(); ++itr)
+ {
+ bool talent = GetTalentSpellCost(itr->second->GetId()) > 0;
+ PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(),
+ itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(),
+ itr->second->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],
+ (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
+ IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID()));
+ }
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ {
+ Unit::AuraList const& uAuraList = unit->GetAurasByType(AuraType(i));
+ if (uAuraList.empty()) continue;
+ PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, uAuraList.size(), i);
+ for (Unit::AuraList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr)
+ {
+ bool talent = GetTalentSpellCost((*itr)->GetId()) > 0;
+ PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(),
+ (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
+ IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID()));
+ }
+ }
+ return true;
+}
+
+bool ChatHandler::HandleResetHonorCommand (const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ return true;
+ }
+
+ player->SetUInt32Value(PLAYER_FIELD_KILLS, 0);
+ player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0);
+ player->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, 0);
+ player->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
+ player->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
+
+ return true;
+}
+
+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)
+ {
+ sLog.outError("Class %u not found in DBC (Wrong DBC files?)",player->getClass());
+ return false;
+ }
+
+ uint8 powertype = cEntry->powerType;
+
+ uint32 unitfield;
+ if(powertype == POWER_RAGE)
+ unitfield = 0x1100EE00;
+ else if(powertype == POWER_ENERGY)
+ unitfield = 0x00000000;
+ else if(powertype == POWER_MANA)
+ unitfield = 0x0000EE00;
+ else
+ {
+ sLog.outError("Invalid default powertype %u for player (class %u)",powertype,player->getClass());
+ return false;
+ }
+
+ // reset m_form if no aura
+ if(!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT))
+ player->m_form = FORM_NONE;
+
+ player->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
+ player->SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
+
+ player->setFactionForRace(player->getRace());
+
+ player->SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( player->getRace() ) | ( player->getClass() << 8 ) | ( player->getGender() << 16 ) | ( powertype << 24 ) ) );
+
+ // 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;
+ }
+ }
+
+ // set UNIT_FIELD_BYTES_1 to init state but preserve m_form value
+ player->SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
+ player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
+ player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form);
+
+ player->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+
+ //-1 is default value
+ player->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
+
+ //player->SetUInt32Value(PLAYER_FIELD_BYTES, 0xEEE00000 );
+ return true;
+}
+
+bool ChatHandler::HandleResetLevelCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!HandleResetStatsOrLevelHelper(player))
+ return false;
+
+ player->SetLevel(1);
+ player->InitStatsForLevel(true);
+ player->InitTaxiNodesForLevel();
+ player->InitTalentForLevel();
+ player->SetUInt32Value(PLAYER_XP,0);
+
+ // reset level to summoned pet
+ Pet* pet = player->GetPet();
+ if(pet && pet->getPetType()==SUMMON_PET)
+ pet->InitStatsForLevel(1);
+
+ return true;
+}
+
+bool ChatHandler::HandleResetStatsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!HandleResetStatsOrLevelHelper(player))
+ return false;
+
+ player->InitStatsForLevel(true);
+ player->InitTaxiNodesForLevel();
+ player->InitTalentForLevel();
+
+ return true;
+}
+
+bool ChatHandler::HandleResetSpellsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ uint64 playerGUID = 0;
+ if (pName)
+ {
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ if(!player)
+ playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player && !playerGUID)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player)
+ {
+ player->resetSpells();
+
+ ChatHandler(player).SendSysMessage(LANG_RESET_SPELLS);
+
+ if(m_session->GetPlayer()!=player)
+ PSendSysMessage(LANG_RESET_SPELLS_ONLINE,player->GetName());
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_SPELLS), GUID_LOPART(playerGUID));
+ PSendSysMessage(LANG_RESET_SPELLS_OFFLINE,pName);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleResetTalentsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ uint64 playerGUID = 0;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ if(!player)
+ playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player && !playerGUID)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player)
+ {
+ player->resetTalents(true);
+
+ ChatHandler(player).SendSysMessage(LANG_RESET_TALENTS);
+
+ if(m_session->GetPlayer()!=player)
+ PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName());
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(playerGUID) );
+ PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,pName);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleResetAllCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ std::string casename = args;
+
+ AtLoginFlags atLogin;
+
+ // Command specially created as single command to prevent using short case names
+ if(casename=="spells")
+ {
+ atLogin = AT_LOGIN_RESET_SPELLS;
+ sWorld.SendWorldText(LANG_RESETALL_SPELLS);
+ }
+ else if(casename=="talents")
+ {
+ atLogin = AT_LOGIN_RESET_TALENTS;
+ sWorld.SendWorldText(LANG_RESETALL_TALENTS);
+ }
+ else
+ {
+ PSendSysMessage(LANG_RESETALL_UNKNOWN_CASE,args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u'",atLogin);
+ HashMapHolder<Player>::MapType const& plist = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr)
+ itr->second->SetAtLoginFlag(atLogin);
+
+ return true;
+}
+
+bool ChatHandler::HandleShutDownCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleRestartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleIdleRestartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART+SHUTDOWN_MASK_IDLE);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleIdleShutDownCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleAddQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .addquest #entry'
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ if(!pQuest)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND,entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // check item starting quest (it can work incorrectly if added without item in inventory)
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE startquest = '%u' LIMIT 1",entry);
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ uint32 item_id = fields[0].GetUInt32();
+ delete result;
+
+ PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry,item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // ok, normal (creature/GO starting) quest
+ if( player->CanAddQuest( pQuest, true ) )
+ {
+ player->AddQuest( pQuest, NULL );
+
+ if ( player->CanCompleteQuest( entry ) )
+ player->CompleteQuest( entry );
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleRemoveQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .removequest #entry'
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ if(!pQuest)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // remove all quest entries for 'entry' from quest log
+ for(uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot )
+ {
+ uint32 quest = player->GetQuestSlotQuestId(slot);
+ if(quest==entry)
+ {
+ player->SetQuestSlot(slot,0);
+
+ // we ignore unequippable quest items in this case, its' still be equipped
+ player->TakeQuestSourceItem( quest, false );
+ }
+ }
+
+ // set quest status to not started (will updated in DB at next save)
+ player->SetQuestStatus( entry, QUEST_STATUS_NONE);
+
+ // reset rewarded for restart repeatable quest
+ player->getQuestStatusMap()[entry].m_rewarded = false;
+
+ SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
+ return true;
+}
+
+bool ChatHandler::HandleCompleteQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .quest complete #entry
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ // If player doesn't have the quest
+ if(!pQuest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // Add quest items for quests that require items
+ for(uint8 x = 0; x < QUEST_OBJECTIVES_COUNT; ++x)
+ {
+ uint32 id = pQuest->ReqItemId[x];
+ uint32 count = pQuest->ReqItemCount[x];
+ if(!id || !count)
+ continue;
+
+ uint32 curItemCount = player->GetItemCount(id,true);
+
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, id, count-curItemCount );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = player->StoreNewItem( dest, id, true);
+ player->SendNewItem(item,count-curItemCount,true,false);
+ }
+ }
+
+ // All creature/GO slain/casted (not required, but otherwise it will display "Creature slain 0/10")
+ for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ uint32 creature = pQuest->ReqCreatureOrGOId[i];
+ uint32 creaturecount = pQuest->ReqCreatureOrGOCount[i];
+
+ if(uint32 spell_id = pQuest->ReqSpell[i])
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->CastedCreatureOrGO(creature,0,spell_id);
+ }
+ else if(creature > 0)
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->KilledMonster(creature,0);
+ }
+ else if(creature < 0)
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->CastedCreatureOrGO(creature,0,0);
+ }
+ }
+
+ // If the quest requires reputation to complete
+ if(uint32 repFaction = pQuest->GetRepObjectiveFaction())
+ {
+ uint32 repValue = pQuest->GetRepObjectiveValue();
+ uint32 curRep = player->GetReputation(repFaction);
+ if(curRep < repValue)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction);
+ player->SetFactionReputation(factionEntry,repValue);
+ }
+ }
+
+ // If the quest requires money
+ int32 ReqOrRewMoney = pQuest->GetRewOrReqMoney();
+ if(ReqOrRewMoney < 0)
+ player->ModifyMoney(-ReqOrRewMoney);
+
+ player->CompleteQuest(entry);
+ return true;
+}
+
+bool ChatHandler::HandleBanCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ char* type = strtok((char*)args, " ");
+
+ if(!type)
+ return false;
+ char* nameOrIP = strtok(NULL, " ");
+
+ if(!nameOrIP)
+ return false;
+
+ char* duration = strtok(NULL," ");
+ if(!duration || !atoi(duration))
+ return false;
+
+ char* reason = strtok(NULL,"");
+ if(!reason)
+ return false;
+
+ switch(sWorld.BanAccount(type, nameOrIP, duration, reason,m_session->GetPlayerName()))
+ {
+ case BAN_SUCCESS:
+ if(atoi(duration)>0)
+ PSendSysMessage(LANG_BAN_YOUBANNED,nameOrIP,secsToTimeString(TimeStringToSecs(duration),true).c_str(),reason);
+ else
+ PSendSysMessage(LANG_BAN_YOUPERMBANNED,nameOrIP,reason);
+ break;
+ case BAN_SYNTAX_ERROR:
+ return false;
+ case BAN_NOTFOUND:
+ PSendSysMessage(LANG_BAN_NOTFOUND,type,nameOrIP);
+ break;
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleUnBanCommand(const char* args)
+{
+ if(!args)
+ return false;
+ char* type = strtok((char*)args, " ");
+ if(!type)
+ return false;
+ char* nameOrIP = strtok(NULL, " ");
+
+ if(!nameOrIP)
+ return false;
+
+ if(sWorld.RemoveBanAccount(type,nameOrIP))
+ PSendSysMessage(LANG_UNBAN_UNBANNED,nameOrIP);
+ else
+ PSendSysMessage(LANG_UNBAN_ERROR,nameOrIP);
+
+ return true;
+}
+
+bool ChatHandler::HandleBanInfoCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ char* cType = strtok((char*)args, " ");
+ char* cnameOrIP = strtok(NULL, "");
+ if(!cType || !cnameOrIP)
+ return false;
+
+ std::string nameOrIP = cnameOrIP;
+ std::string type = cType;
+ if (!IsIPAddress(cnameOrIP) && type=="ip")
+ return false;
+
+ Field *fields;
+ if(type != "ip")
+ {
+ //look the accountid up
+ uint32 accountid;
+ std::string accountname;
+ if(type == "account")
+ {
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *result = loginDatabase.PQuery("SELECT id, username FROM account WHERE username = '%s'",nameOrIP.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOACCOUNT);
+ return true;
+ }
+ fields = result->Fetch();
+ accountid = fields[0].GetUInt32();
+ accountname = fields[1].GetCppString();
+ delete result;
+ }
+ else if(type == "character")
+ {
+ if(!normalizePlayerName(nameOrIP))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", nameOrIP.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOCHARACTER);
+ return true;
+ }
+ fields = result->Fetch();
+ accountid = fields[0].GetUInt32();
+ delete result;
+ result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", accountid);
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOCHARACTER);
+ return true;
+ }
+ fields = result->Fetch();
+ accountname = fields[0].GetCppString();
+ delete result;
+ }
+ else
+ return false;
+
+ 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.c_str());
+ return true;
+ }
+
+ PSendSysMessage(LANG_BANINFO_BANHISTORY,accountname.c_str());
+ do
+ {
+ fields = result->Fetch();
+
+ time_t unbandate = time_t(fields[3].GetUInt64());
+ bool active = false;
+ if(fields[2].GetBool() && (fields[1].GetUInt64() == (uint64)0 ||unbandate >= time(NULL)) )
+ active = true;
+ bool permanent = (fields[1].GetUInt64() == (uint64)0);
+ std::string bantime = permanent?GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[1].GetUInt64(), true);
+ PSendSysMessage(LANG_BANINFO_HISTORYENTRY,
+ fields[0].GetString(), bantime.c_str(), active ? GetMangosString(LANG_BANINFO_YES):GetMangosString(LANG_BANINFO_NO), fields[4].GetString(), fields[5].GetString());
+ }while (result->NextRow());
+
+ delete result;
+ }
+ else
+ {
+ loginDatabase.escape_string(nameOrIP);
+ 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'",nameOrIP.c_str());
+ if(!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOIP);
+ return true;
+ }
+ fields = result->Fetch();
+ bool permanent = (fields[6].GetUInt64()==(uint64)0);
+ PSendSysMessage(LANG_BANINFO_IPENTRY,
+ fields[0].GetString(), fields[1].GetString(), permanent ? GetMangosString(LANG_BANINFO_NEVER):fields[2].GetString(),
+ permanent ? GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[3].GetUInt64(), true).c_str(), fields[4].GetString(), fields[5].GetString());
+ delete result;
+ }
+ return true;
+}
+
+bool ChatHandler::HandleBanListCommand(const char* args)
+{
+ loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+ if(!*args)
+ return false;
+ char* cType = strtok((char*)args, " ");
+ char* cFilter = strtok(NULL, "");
+ if(!cType || !cFilter)
+ return false;
+ std::string Filter = cFilter;
+ std::string Type = cType;
+ loginDatabase.escape_string(Filter);
+
+ QueryResult* result = NULL;
+ Field *fields = NULL;
+ if(Type == "ip")
+ {
+ result = loginDatabase.PQuery("SELECT ip FROM ip_banned WHERE ip "_LIKE_" '""%%%s%%""'",Filter.c_str());
+ if(!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOIP);
+ return true;
+ }
+ PSendSysMessage(LANG_BANLIST_MATCHINGIP);
+ do
+ {
+ fields = result->Fetch();
+ PSendSysMessage("%s",fields[0].GetString());
+ } while (result->NextRow());
+
+ delete result;
+ return true;
+ }
+ //lookup accountid
+ if(Type == "account")
+ {
+ result = loginDatabase.PQuery("SELECT id FROM account WHERE username "_LIKE_" '""%%%s%%""' ",Filter.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOACCOUNT);
+ return true;
+ }
+ //do not delete result
+ }
+ else if(Type == "characters")
+ {
+ result = CharacterDatabase.PQuery("SELECT account FROM characters, WHERE name "_LIKE_" '""%%%s%%""' ",Filter.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOCHARACTER);
+ return true;
+ }
+ }
+ else
+ return false;
+
+ PSendSysMessage(LANG_BANLIST_MATCHINGACCOUNT);
+ do
+ {
+ 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.active = '1' AND account_banned.id=account.id",accountid);
+ if(banresult)
+ {
+ Field* fields2 = banresult->Fetch();
+ PSendSysMessage("%s",fields2[0].GetString());
+ delete banresult;
+ }
+ } while (result->NextRow());
+
+ delete result;
+ return true;
+}
+
+bool ChatHandler::HandleRespawnCommand(const char* /*args*/)
+{
+ Player* pl = m_session->GetPlayer();
+
+ CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::RespawnDo u_do;
+ MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo> worker(u_do);
+
+ TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo>, GridTypeMapContainer > obj_worker(worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, obj_worker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
+
+ return true;
+}
+
+bool ChatHandler::HandleFlyModeCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ Unit *unit = getSelectedUnit();
+ if (!unit || (unit->GetTypeId() != TYPEID_PLAYER))
+ unit = m_session->GetPlayer();
+
+ WorldPacket data(12);
+ if (strncmp(args, "on", 3) == 0)
+ data.SetOpcode(SMSG_MOVE_SET_CAN_FLY);
+ else if (strncmp(args, "off", 4) == 0)
+ data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY);
+ else
+ {
+ SendSysMessage(LANG_USE_BOL);
+ return false;
+ }
+ data.append(unit->GetPackGUID());
+ data << uint32(0); // unknown
+ unit->SendMessageToSet(&data, true);
+ PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, unit->GetName(), args);
+ return true;
+}
+
+bool ChatHandler::HandleLoadPDumpCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ char * file = strtok((char*)args, " "); if(!file) return false;
+ char * acc = strtok(NULL, " "); if(!acc) return false;
+ if(!file || !acc)
+ return false;
+
+ uint32 account_id = objmgr.GetAccountByAccountName(acc);
+ if(!account_id)
+ {
+ account_id = atoi(acc);
+ if(account_id)
+ {
+ std::string acc_name;
+ if(!objmgr.GetAccountNameByAccount(account_id,acc_name))
+ return false;
+ }
+ else
+ return false;
+ }
+
+ char * name = strtok(NULL, " ");
+ char * guid_str = name ? strtok(NULL, " ") : NULL;
+
+ uint32 guid = guid_str ? atoi(guid_str) : 0;
+
+ if(PlayerDumpReader().LoadDump(file, account_id, name ? name : "", guid))
+ PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS);
+ else
+ PSendSysMessage(LANG_COMMAND_IMPORT_FAILED);
+
+ return true;
+}
+
+bool ChatHandler::HandleChangeEntryCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ uint32 newEntryNum = atoi(args);
+ if(!newEntryNum)
+ return false;
+
+ Unit* unit = getSelectedUnit();
+ if(!unit || unit->GetTypeId() != TYPEID_UNIT)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Creature* creature = (Creature*)unit;
+ if(creature->UpdateEntry(newEntryNum))
+ SendSysMessage(LANG_DONE);
+ else
+ SendSysMessage(LANG_ERROR);
+ return true;
+}
+
+bool ChatHandler::HandleWritePDumpCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ char* file = strtok((char*)args, " ");
+ char* p2 = strtok(NULL, " ");
+
+ if(!file || !p2)
+ return false;
+
+ uint32 guid = objmgr.GetPlayerGUIDByName(p2);
+ if(!guid)
+ guid = atoi(p2);
+
+ if (PlayerDumpWriter().WriteDump(file, guid))
+ PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS);
+ else
+ PSendSysMessage(LANG_COMMAND_EXPORT_FAILED);
+
+ return true;
+}
+
+bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
+{
+ Unit* unit = getSelectedUnit();
+ if(!unit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_MOVEGENS_LIST,(unit->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature" ),unit->GetGUIDLow());
+
+ MotionMaster* mm = unit->GetMotionMaster();
+ for(MotionMaster::const_iterator itr = mm->begin(); itr != mm->end(); ++itr)
+ {
+ switch((*itr)->GetMovementGeneratorType())
+ {
+ case IDLE_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_IDLE); break;
+ case RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_RANDOM); break;
+ case WAYPOINT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_WAYPOINT); break;
+ case ANIMAL_RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_ANIMAL_RANDOM); break;
+ case CONFUSED_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_CONFUSED); break;
+ case TARGETED_MOTION_TYPE:
+ {
+ if(unit->GetTypeId()==TYPEID_PLAYER)
+ {
+ TargetedMovementGenerator<Player> const* mgen = static_cast<TargetedMovementGenerator<Player> const*>(*itr);
+ Unit* target = mgen->GetTarget();
+ if(target)
+ PSendSysMessage(LANG_MOVEGENS_TARGETED_PLAYER,target->GetName(),target->GetGUIDLow());
+ else
+ SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
+ }
+ else
+ {
+ TargetedMovementGenerator<Creature> const* mgen = static_cast<TargetedMovementGenerator<Creature> const*>(*itr);
+ Unit* target = mgen->GetTarget();
+ if(target)
+ PSendSysMessage(LANG_MOVEGENS_TARGETED_CREATURE,target->GetName(),target->GetGUIDLow());
+ else
+ SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
+ }
+ break;
+ }
+ case HOME_MOTION_TYPE:
+ if(unit->GetTypeId()==TYPEID_UNIT)
+ {
+ float x,y,z;
+ (*itr)->GetDestination(x,y,z);
+ PSendSysMessage(LANG_MOVEGENS_HOME_CREATURE,x,y,z);
+ }
+ else
+ SendSysMessage(LANG_MOVEGENS_HOME_PLAYER);
+ break;
+ case FLIGHT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FLIGHT); break;
+ case POINT_MOTION_TYPE:
+ {
+ float x,y,z;
+ (*itr)->GetDestination(x,y,z);
+ PSendSysMessage(LANG_MOVEGENS_POINT,x,y,z);
+ break;
+ }
+ case FLEEING_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FEAR); break;
+ case DISTRACT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_DISTRACT); break;
+ default:
+ PSendSysMessage(LANG_MOVEGENS_UNKNOWN,(*itr)->GetMovementGeneratorType());
+ break;
+ }
+ }
+ return true;
+}
+
+bool ChatHandler::HandlePLimitCommand(const char *args)
+{
+ if(*args)
+ {
+ char* param = strtok((char*)args, " ");
+ if(!param)
+ return false;
+
+ int l = strlen(param);
+
+ if( strncmp(param,"player",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_PLAYER);
+ else if(strncmp(param,"moderator",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_MODERATOR);
+ else if(strncmp(param,"gamemaster",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_GAMEMASTER);
+ else if(strncmp(param,"administrator",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_ADMINISTRATOR);
+ else if(strncmp(param,"reset",l) == 0 )
+ sWorld.SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT) );
+ else
+ {
+ int val = atoi(param);
+ if(val < -SEC_ADMINISTRATOR) val = -SEC_ADMINISTRATOR;
+
+ sWorld.SetPlayerLimit(val);
+ }
+
+ // kick all low security level players
+ if(sWorld.GetPlayerAmountLimit() > SEC_PLAYER)
+ sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit());
+ }
+
+ uint32 pLimit = sWorld.GetPlayerAmountLimit();
+ AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
+ char const* secName = "";
+ switch(allowedAccountType)
+ {
+ case SEC_PLAYER: secName = "Player"; break;
+ case SEC_MODERATOR: secName = "Moderator"; break;
+ case SEC_GAMEMASTER: secName = "Gamemaster"; break;
+ case SEC_ADMINISTRATOR: secName = "Administrator"; break;
+ default: secName = "<unknown>"; break;
+ }
+
+ PSendSysMessage("Player limits: amount %u, min. security level %s.",pLimit,secName);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ m_session->GetPlayer()->CastSpell(target,spell,triggered);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastBackCommand(const char* args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ // update orientation at server
+ caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
+
+ // and client
+ WorldPacket data;
+ caster->BuildHeartBeatMsg(&data);
+ caster->SendMessageToSet(&data,true);
+
+ caster->CastSpell(m_session->GetPlayer(),spell,false);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastDistCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char *distStr = strtok(NULL, " ");
+
+ float dist = 0;
+
+ if(distStr)
+ sscanf(distStr, "%f", &dist);
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,dist);
+
+ m_session->GetPlayer()->CastSpell(x,y,z,spell,triggered);
+ return true;
+}
+
+bool ChatHandler::HandleCastTargetCommand(const char* args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!caster->getVictim())
+ {
+ SendSysMessage(LANG_SELECTED_TARGET_NOT_HAVE_VICTIM);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ // update orientation at server
+ caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
+
+ // and client
+ WorldPacket data;
+ caster->BuildHeartBeatMsg(&data);
+ caster->SendMessageToSet(&data,true);
+
+ caster->CastSpell(caster->getVictim(),spell,false);
+
+ return true;
+}
+
+/*
+ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator
+Without this function 3rd party scripting library will get linking errors (unresolved external)
+when attempting to use the PointMovementGenerator
+*/
+bool ChatHandler::HandleComeToMeCommand(const char *args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* newFlagStr = strtok((char*)args, " ");
+
+ if(!newFlagStr)
+ return false;
+
+ uint32 newFlags = atoi(newFlagStr);
+
+ caster->SetUnitMovementFlags(newFlags);
+
+ Player* pl = m_session->GetPlayer();
+
+ caster->GetMotionMaster()->MovePoint(0, pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ());
+ return true;
+}
+
+bool ChatHandler::HandleCastSelfCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->CastSpell(target,spell,false);
+
+ return true;
+}
+
+std::string GetTimeString(uint32 time)
+{
+ uint16 days = time / DAY, hours = (time % DAY) / HOUR, minute = (time % HOUR) / MINUTE;
+ std::ostringstream ss;
+ if(days) ss << days << "d ";
+ if(hours) ss << hours << "h ";
+ ss << minute << "m";
+ return ss.str();
+}
+
+bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/)
+{
+ Player* player = getSelectedPlayer();
+ if (!player) player = m_session->GetPlayer();
+ uint32 counter = 0;
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
+ for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ counter++;
+ }
+ }
+ PSendSysMessage("player binds: %d", counter);
+ counter = 0;
+ Group *group = player->GetGroup();
+ if(group)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Group::BoundInstancesMap &binds = group->GetBoundInstances(i);
+ for(Group::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ counter++;
+ }
+ }
+ }
+ PSendSysMessage("group binds: %d", counter);
+
+ return true;
+}
+
+bool ChatHandler::HandleInstanceUnbindCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string cmd = args;
+ if(cmd == "all")
+ {
+ Player* player = getSelectedPlayer();
+ if (!player) player = m_session->GetPlayer();
+ uint32 counter = 0;
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
+ for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();)
+ {
+ if(itr->first != player->GetMapId())
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("unbinding map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ player->UnbindInstance(itr, i);
+ counter++;
+ }
+ else
+ ++itr;
+ }
+ }
+ PSendSysMessage("instances unbound: %d", counter);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/)
+{
+ PSendSysMessage("instances loaded: %d", MapManager::Instance().GetNumInstances());
+ PSendSysMessage("players in instances: %d", MapManager::Instance().GetNumPlayersInInstances());
+ PSendSysMessage("instance saves: %d", sInstanceSaveManager.GetNumInstanceSaves());
+ PSendSysMessage("players bound: %d", sInstanceSaveManager.GetNumBoundPlayersTotal());
+ PSendSysMessage("groups bound: %d", sInstanceSaveManager.GetNumBoundGroupsTotal());
+ return true;
+}
+
+bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/)
+{
+ Player* pl = m_session->GetPlayer();
+
+ Map* map = pl->GetMap();
+ if (!map->IsDungeon())
+ {
+ PSendSysMessage("Map is not a dungeon.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!((InstanceMap*)map)->GetInstanceData())
+ {
+ PSendSysMessage("Map has no instance data.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ ((InstanceMap*)map)->GetInstanceData()->SaveToDB();
+ return true;
+}
diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp
new file mode 100644
index 00000000000..05c0ac8a997
--- /dev/null
+++ b/src/game/LootHandler.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "Log.h"
+#include "Corpse.h"
+#include "GameObject.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "WorldSession.h"
+#include "LootMgr.h"
+#include "Object.h"
+#include "Group.h"
+#include "World.h"
+#include "Util.h"
+
+void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
+ Player *player = GetPlayer();
+ uint64 lguid = player->GetLootGUID();
+ Loot *loot;
+ uint8 lootSlot;
+
+ recv_data >> lootSlot;
+
+ if (IS_GAMEOBJECT_GUID(lguid))
+ {
+ GameObject *go =
+ ObjectAccessor::GetGameObject(*player, lguid);
+
+ // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
+ if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
+ {
+ player->SendLootRelease(lguid);
+ return;
+ }
+
+ loot = &go->loot;
+ }
+ else if (IS_ITEM_GUID(lguid))
+ {
+ Item *pItem = player->GetItemByGuid( lguid );
+
+ if (!pItem)
+ {
+ player->SendLootRelease(lguid);
+ return;
+ }
+
+ loot = &pItem->loot;
+ }
+ else
+ {
+ Creature* pCreature =
+ ObjectAccessor::GetCreature(*player, lguid);
+
+ bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
+
+ if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
+ {
+ player->SendLootRelease(lguid);
+ return;
+ }
+
+ loot = &pCreature->loot;
+ }
+
+ QuestItem *qitem = NULL;
+ QuestItem *ffaitem = NULL;
+ QuestItem *conditem = NULL;
+
+ LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem);
+
+ if(!item)
+ {
+ player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL );
+ return;
+ }
+
+ // questitems use the blocked field for other purposes
+ if (!qitem && item->is_blocked)
+ {
+ player->SendLootRelease(lguid);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId);
+
+ if (qitem)
+ {
+ qitem->is_looted = true;
+ //freeforall is 1 if everyone's supposed to get the quest item.
+ if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
+ player->SendNotifyLootItemRemoved(lootSlot);
+ else
+ loot->NotifyQuestItemRemoved(qitem->index);
+ }
+ else
+ {
+ if (ffaitem)
+ {
+ //freeforall case, notify only one player of the removal
+ ffaitem->is_looted=true;
+ player->SendNotifyLootItemRemoved(lootSlot);
+ }
+ else
+ {
+ //not freeforall, notify everyone
+ if(conditem)
+ conditem->is_looted=true;
+ loot->NotifyItemRemoved(lootSlot);
+ }
+ }
+
+ //if only one person is supposed to loot the item, then set it to looted
+ if (!item->freeforall)
+ item->is_looted = true;
+
+ --loot->unlootedCount;
+
+ player->SendNewItem(newitem, uint32(item->count), false, false, true);
+ }
+ else
+ player->SendEquipError( msg, NULL, NULL );
+}
+
+void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_LOOT_MONEY");
+
+ Player *player = GetPlayer();
+ uint64 guid = player->GetLootGUID();
+ if(!guid)
+ return;
+
+ Loot *pLoot = NULL;
+
+ switch(GUID_HIPART(guid))
+ {
+ case HIGHGUID_GAMEOBJECT:
+ {
+ GameObject *pGameObject = ObjectAccessor::GetGameObject(*GetPlayer(), guid);
+
+ // not check distance for GO in case owned GO (fishing bobber case, for example)
+ if( pGameObject && (pGameObject->GetOwnerGUID()==_player->GetGUID() || pGameObject->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) )
+ pLoot = &pGameObject->loot;
+
+ break;
+ }
+ case HIGHGUID_CORPSE: // remove insignia ONLY in BG
+ {
+ Corpse *bones = ObjectAccessor::GetCorpse(*GetPlayer(), guid);
+
+ if (bones && bones->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
+ pLoot = &bones->loot;
+
+ break;
+ }
+ case HIGHGUID_ITEM:
+ {
+ if(Item *item = GetPlayer()->GetItemByGuid(guid))
+ pLoot = &item->loot;
+ break;
+ }
+ case HIGHGUID_UNIT:
+ {
+ Creature* pCreature = ObjectAccessor::GetCreature(*GetPlayer(), guid);
+
+ bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
+
+ if ( ok_loot && pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
+ pLoot = &pCreature->loot ;
+
+ break;
+ }
+ default:
+ return; // unlootable type
+ }
+
+ if( pLoot )
+ {
+ if (!IS_ITEM_GUID(guid) && player->GetGroup()) //item can be looted only single player
+ {
+ Group *group = player->GetGroup();
+
+ std::vector<Player*> playersNear;
+ for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* playerGroup = itr->getSource();
+ if(!playerGroup)
+ continue;
+ if (player->GetDistance2d(playerGroup) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ playersNear.push_back(playerGroup);
+ }
+
+ uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size()));
+
+ for (std::vector<Player*>::iterator i = playersNear.begin(); i != playersNear.end(); ++i)
+ {
+ (*i)->ModifyMoney( money_per_player );
+ //Offset surely incorrect, but works
+ WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 );
+ data << uint32(money_per_player);
+ (*i)->GetSession()->SendPacket( &data );
+ }
+ }
+ else
+ player->ModifyMoney( pLoot->gold );
+ pLoot->gold = 0;
+ pLoot->NotifyMoneyRemoved();
+ }
+}
+
+void WorldSession::HandleLootOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: CMSG_LOOT");
+
+ uint64 guid;
+ recv_data >> guid;
+
+ GetPlayer()->SendLoot(guid, LOOT_CORPSE);
+}
+
+void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: CMSG_LOOT_RELEASE");
+
+ // cheaters can modify lguid to prevent correct apply loot release code and re-loot
+ // use internal stored guid
+ //uint64 lguid;
+ //recv_data >> lguid;
+
+ if(uint64 lguid = GetPlayer()->GetLootGUID())
+ DoLootRelease(lguid);
+}
+
+void WorldSession::DoLootRelease( uint64 lguid )
+{
+ Player *player = GetPlayer();
+ Loot *loot;
+
+ player->SetLootGUID(0);
+ player->SendLootRelease(lguid);
+
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
+
+ if (IS_GAMEOBJECT_GUID(lguid))
+ {
+ GameObject *go =
+ ObjectAccessor::GetGameObject(*player, lguid);
+
+ // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
+ if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
+ return;
+
+ loot = &go->loot;
+
+ if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR)
+ {
+ // locked doors are opened with spelleffect openlock, prevent remove its as looted
+ go->UseDoorOrButton();
+ }
+ else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
+ {
+ // GO is mineral vein? so it is not removed after its looted
+ if(go->GetGoType() == GAMEOBJECT_TYPE_CHEST)
+ {
+ uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens;
+ uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens;
+
+ // only vein pass this check
+ if(go_min != 0 && go_max > go_min)
+ {
+ float amount_rate = sWorld.getRate(RATE_MINING_AMOUNT);
+ float min_amount = go_min*amount_rate;
+ float max_amount = go_max*amount_rate;
+
+ go->AddUse();
+ float uses = float(go->GetUseCount());
+
+ if(uses < max_amount)
+ {
+ if(uses >= min_amount)
+ {
+ float chance_rate = sWorld.getRate(RATE_MINING_NEXT);
+
+ int32 ReqValue = 175;
+ LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId);
+ if(lockInfo)
+ ReqValue = lockInfo->requiredminingskill;
+ float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25);
+ double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses));
+ if(roll_chance_f(100*chance+skill))
+ {
+ go->SetLootState(GO_READY);
+ }
+ else // not have more uses
+ go->SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else // 100% chance until min uses
+ go->SetLootState(GO_READY);
+ }
+ else // max uses already
+ go->SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else // not vein
+ go->SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
+ { // The fishing hole used once more
+ go->AddUse(); // if the max usage is reached, will be despawned in next tick
+ if (go->GetUseCount()>=irand(go->GetGOInfo()->fishinghole.minSuccessOpens,go->GetGOInfo()->fishinghole.maxSuccessOpens))
+ {
+ go->SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else
+ go->SetLootState(GO_READY);
+ }
+ else // not chest (or vein/herb/etc)
+ go->SetLootState(GO_JUST_DEACTIVATED);
+
+ loot->clear();
+ }
+ else
+ // not fully looted object
+ go->SetLootState(GO_ACTIVATED);
+ }
+ else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG
+ {
+ Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid);
+ if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
+ return;
+
+ loot = &corpse->loot;
+
+ if (loot->isLooted())
+ {
+ loot->clear();
+ corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
+ }
+ }
+ else if (IS_ITEM_GUID(lguid))
+ {
+ Item *pItem = player->GetItemByGuid(lguid );
+ if(!pItem)
+ return;
+ if( (pItem->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) &&
+ pItem->GetProto()->Class == ITEM_CLASS_TRADE_GOODS &&
+ pItem->GetCount() >= 5)
+ {
+ pItem->m_lootGenerated = false;
+ pItem->loot.clear();
+
+ uint32 count = 5;
+ player->DestroyItemCount(pItem, count, true);
+ }
+ else
+ // FIXME: item don't must be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or checting possible.
+ player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true);
+ return; // item can be looted only single player
+ }
+ else
+ {
+ Creature* pCreature = ObjectAccessor::GetCreature(*player, lguid);
+
+ bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
+ if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
+ return;
+
+ loot = &pCreature->loot;
+
+ // update next looter
+ if(Player *recipient = pCreature->GetLootRecipient())
+ if(Group* group = recipient->GetGroup())
+ if (group->GetLooterGuid() == player->GetGUID())
+ group->UpdateLooterGuid(pCreature);
+
+ if (loot->isLooted())
+ {
+ // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
+ if(!pCreature->isAlive())
+ pCreature->AllLootRemovedFromCorpse();
+
+ pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+ loot->clear();
+ }
+ }
+
+ //Player is not looking at loot list, he doesn't need to see updates on the loot list
+ loot->RemoveLooter(player->GetGUID());
+}
+
+void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1+8);
+
+ uint8 slotid;
+ uint64 lootguid, target_playerguid;
+
+ recv_data >> lootguid >> slotid >> target_playerguid;
+
+ if(!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
+ {
+ _player->SendLootRelease(GetPlayer()->GetLootGUID());
+ return;
+ }
+
+ Player *target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
+ if(!target)
+ return;
+
+ sLog.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName());
+
+ if(_player->GetLootGUID() != lootguid)
+ return;
+
+ Loot *pLoot = NULL;
+
+ if(IS_CREATURE_GUID(GetPlayer()->GetLootGUID()))
+ {
+ Creature *pCreature = ObjectAccessor::GetCreature(*GetPlayer(), lootguid);
+ if(!pCreature)
+ return;
+
+ pLoot = &pCreature->loot;
+ }
+ else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
+ {
+ GameObject *pGO = ObjectAccessor::GetGameObject(*GetPlayer(), lootguid);
+ if(!pGO)
+ return;
+
+ pLoot = &pGO->loot;
+ }
+
+ if(!pLoot)
+ return;
+
+ if (slotid > pLoot->items.size())
+ {
+ sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %d)",GetPlayer()->GetName(), slotid, pLoot->items.size());
+ return;
+ }
+
+ LootItem& item = pLoot->items[slotid];
+
+ ItemPosCountVec dest;
+ uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count );
+ if ( msg != EQUIP_ERR_OK )
+ {
+ target->SendEquipError( msg, NULL, NULL );
+ _player->SendEquipError( msg, NULL, NULL ); // send duplicate of error massage to master looter
+ return;
+ }
+
+ // not move item from loot to target inventory
+ Item * newitem = target->StoreNewItem( dest, item.itemid, true, item.randomPropertyId );
+ target->SendNewItem(newitem, uint32(item.count), false, false, true );
+
+ // mark as looted
+ item.count=0;
+ item.is_looted=true;
+
+
+ pLoot->NotifyItemRemoved(slotid);
+ --pLoot->unlootedCount;
+}
diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp
new file mode 100644
index 00000000000..e7848ff4094
--- /dev/null
+++ b/src/game/LootMgr.cpp
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "LootMgr.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "World.h"
+#include "Util.h"
+#include "SharedDefines.h"
+
+static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
+ RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
+ RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
+ RATE_DROP_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON
+ RATE_DROP_ITEM_RARE, // ITEM_QUALITY_RARE
+ RATE_DROP_ITEM_EPIC, // ITEM_QUALITY_EPIC
+ RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
+ RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
+};
+
+LootStore LootTemplates_Creature( "creature_loot_template", "creature entry");
+LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id");
+LootStore LootTemplates_Fishing( "fishing_loot_template", "area id");
+LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry");
+LootStore LootTemplates_Item( "item_loot_template", "item entry");
+LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid");
+LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry");
+LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id");
+LootStore LootTemplates_Reference( "reference_loot_template", "reference id");
+LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id");
+
+
+class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
+{
+ public:
+ void AddEntry(LootStoreItem& item); // Adds an entry to the group (at loading stage)
+ bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
+ bool HasQuestDropForPlayer(Player const * player) const;
+ // The same for active quests of the player
+ void Process(Loot& loot) const; // Rolls an item from the group (if any) and adds the item to the loot
+ float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
+ float TotalChance() const; // Overall chance for the group
+
+ void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const;
+ void CollectLootIds(LootIdSet& set) const;
+ void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const;
+ private:
+ LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB
+ LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance
+
+ LootStoreItem const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
+};
+
+//Remove all data and free all memory
+void LootStore::Clear()
+{
+ for (LootTemplateMap::const_iterator itr=m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr)
+ delete itr->second;
+ m_LootTemplates.clear();
+}
+
+// Checks validity of the loot store
+// Actual checks are done within LootTemplate::Verify() which is called for every template
+void LootStore::Verify() const
+{
+ for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i )
+ i->second->Verify(*this, i->first);
+}
+
+// Loads a *_loot_template DB table into loot store
+// All checks of the loaded template are called from here, no error reports at loot generation required
+void LootStore::LoadLootTable()
+{
+ LootTemplateMap::iterator tab;
+ uint32 count = 0;
+
+ // Clearing store (for reloading case)
+ Clear();
+
+ sLog.outString( "%s :", GetName());
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName());
+
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint32 item = fields[1].GetUInt32();
+ float chanceOrQuestChance = fields[2].GetFloat();
+ uint8 group = fields[3].GetUInt8();
+ int32 mincountOrRef = fields[4].GetInt32();
+ uint8 maxcount = fields[5].GetUInt8();
+ ConditionType condition = (ConditionType)fields[6].GetUInt8();
+ uint32 cond_value1 = fields[7].GetUInt32();
+ uint32 cond_value2 = fields[8].GetUInt32();
+
+ if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2))
+ {
+ sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item);
+ continue; // error already printed to log/console.
+ }
+
+ // (condition + cond_value1/2) are converted into single conditionId
+ uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2);
+
+ LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount);
+
+ if (!storeitem.IsValid(*this,entry)) // Validity checks
+ continue;
+
+ // Looking for the template of the entry
+ // often entries are put together
+ if (m_LootTemplates.empty() || tab->first != entry)
+ {
+ // Searching the template (in case template Id changed)
+ tab = m_LootTemplates.find(entry);
+ if ( tab == m_LootTemplates.end() )
+ {
+ std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate));
+ tab = pr.first;
+ }
+ }
+ // else is empty - template Id and iter are the same
+ // finally iter refers to already existed or just created <entry, LootTemplate>
+
+ // Adds current row to the template
+ tab->second->AddEntry(storeitem);
+ ++count;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ Verify(); // Checks validity of the loot store
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u loot definitions (%d templates)", count, m_LootTemplates.size());
+ }
+ else
+ {
+ sLog.outString();
+ sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() );
+ }
+}
+
+bool LootStore::HaveQuestLootFor(uint32 loot_id) const
+{
+ LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id);
+ if(itr == m_LootTemplates.end())
+ return false;
+
+ // scan loot for quest items
+ return itr->second->HasQuestDrop(m_LootTemplates);
+}
+
+bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const
+{
+ LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
+ if (tab != m_LootTemplates.end())
+ if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player))
+ return true;
+
+ return false;
+}
+
+LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const
+{
+ LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
+
+ if (tab == m_LootTemplates.end())
+ return NULL;
+
+ return tab->second;
+}
+
+void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set)
+{
+ LoadLootTable();
+
+ for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab)
+ ids_set.insert(tab->first);
+}
+
+void LootStore::CheckLootRefs(LootIdSet* ref_set) const
+{
+ for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr)
+ ltItr->second->CheckLootRefs(m_LootTemplates,ref_set);
+}
+
+void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const
+{
+ // all still listed ids isn't referenced
+ for(LootIdSet::const_iterator itr = ids_set.begin(); itr != ids_set.end(); ++itr)
+ sLog.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr,GetEntryName());
+}
+
+void LootStore::ReportNotExistedId(uint32 id) const
+{
+ sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName());
+}
+
+//
+// --------- LootStoreItem ---------
+//
+
+// Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
+// RATE_DROP_ITEMS is no longer used for all types of entries
+bool LootStoreItem::Roll() const
+{
+ if(chance>=100.f)
+ return true;
+
+ if(mincountOrRef < 0) // reference case
+ return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED));
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
+
+ float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
+
+ return roll_chance_f(chance*qualityModifier);
+}
+
+// Checks correctness of values
+bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
+{
+ if (mincountOrRef == 0)
+ {
+ sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef);
+ return false;
+ }
+
+ if( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped
+ {
+ ItemPrototype const *proto = objmgr.GetItemPrototype(itemid);
+ if(!proto)
+ {
+ sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid);
+ return false;
+ }
+
+ if( chance == 0 && group == 0) // Zero chance is allowed for grouped entries only
+ {
+ sLog.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
+ return false;
+ }
+
+ if( chance != 0 && chance < 0.000001f ) // loot with low chance
+ {
+ sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%d) - skipped", store.GetName(), entry, itemid, chance);
+ return false;
+ }
+ }
+ else // mincountOrRef < 0
+ {
+ if (needs_quest)
+ sLog.outErrorDb("Table '%s' entry %d item %d: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid);
+ else if( chance == 0 ) // no chance for the reference
+ {
+ sLog.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
+ return false;
+ }
+ }
+ return true; // Referenced template existence is checked at whole store level
+}
+
+//
+// --------- LootItem ---------
+//
+
+// Constructor, copies most fields from LootStoreItem and generates random count
+LootItem::LootItem(LootStoreItem const& li)
+{
+ itemid = li.itemid;
+ conditionId = li.conditionId;
+
+ ItemPrototype const* proto = objmgr.GetItemPrototype(itemid);
+ freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT);
+
+ needs_quest = li.needs_quest;
+
+ count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only
+ randomSuffix = GenerateEnchSuffixFactor(itemid);
+ randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
+ is_looted = 0;
+ is_blocked = 0;
+ is_underthreshold = 0;
+ is_counted = 0;
+}
+
+// Basic checks for player/item compatibility - if false no chance to see the item in the loot
+bool LootItem::AllowedForPlayer(Player const * player) const
+{
+ // DB conditions check
+ if ( !objmgr.IsPlayerMeetToCondition(player,conditionId) )
+ return false;
+
+ if ( needs_quest )
+ {
+ // Checking quests for quest-only drop (check only quests requirements in this case)
+ if( !player->HasQuestForItem(itemid) )
+ return false;
+ }
+ else
+ {
+ // Not quest only drop (check quest starting items for already accepted non-repeatable quests)
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
+ if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid))
+ return false;
+ }
+
+ return true;
+}
+
+//
+// --------- Loot ---------
+//
+
+// Inserts the item into the loot (called by LootTemplate processors)
+void Loot::AddItem(LootStoreItem const & item)
+{
+ if (item.needs_quest) // Quest drop
+ {
+ if (quest_items.size() < MAX_NR_QUEST_ITEMS)
+ quest_items.push_back(LootItem(item));
+ }
+ else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop
+ {
+ items.push_back(LootItem(item));
+
+ // non-conditional one-player only items are counted here,
+ // free for all items are counted in FillFFALoot(),
+ // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
+ if( !item.conditionId )
+ {
+ ItemPrototype const* proto = objmgr.GetItemPrototype(item.itemid);
+ if( !proto || (proto->Flags & ITEM_FLAGS_PARTY_LOOT)==0 )
+ ++unlootedCount;
+ }
+ }
+}
+
+// Calls processor of corresponding LootTemplate (which handles everything including references)
+void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner)
+{
+ LootTemplate const* tab = store.GetLootFor(loot_id);
+
+ if (!tab)
+ {
+ sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
+ return;
+ }
+
+ items.reserve(MAX_NR_LOOT_ITEMS);
+ quest_items.reserve(MAX_NR_QUEST_ITEMS);
+
+ tab->Process(*this, store); // Processing is done there, callback via Loot::AddItem()
+
+ // Setting access rights fow group-looting case
+ if(!loot_owner)
+ return;
+ Group * pGroup=loot_owner->GetGroup();
+ if(!pGroup)
+ return;
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ //fill the quest item map for every player in the recipient's group
+ Player* pl = itr->getSource();
+ if(!pl)
+ continue;
+ uint32 plguid = pl->GetGUIDLow();
+ QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid);
+ if (qmapitr == PlayerQuestItems.end())
+ {
+ FillQuestLoot(pl);
+ }
+ qmapitr = PlayerFFAItems.find(plguid);
+ if (qmapitr == PlayerFFAItems.end())
+ {
+ FillFFALoot(pl);
+ }
+ qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid);
+ if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end())
+ {
+ FillNonQuestNonFFAConditionalLoot(pl);
+ }
+ }
+}
+
+QuestItemList* Loot::FillFFALoot(Player* player)
+{
+ QuestItemList *ql = new QuestItemList();
+
+ for(uint8 i = 0; i < items.size(); i++)
+ {
+ LootItem &item = items[i];
+ if(!item.is_looted && item.freeforall && item.AllowedForPlayer(player) )
+ {
+ ql->push_back(QuestItem(i));
+ ++unlootedCount;
+ }
+ }
+ if (ql->empty())
+ {
+ delete ql;
+ return NULL;
+ }
+
+ PlayerFFAItems[player->GetGUIDLow()] = ql;
+ return ql;
+}
+
+QuestItemList* Loot::FillQuestLoot(Player* player)
+{
+ if (items.size() == MAX_NR_LOOT_ITEMS) return NULL;
+ QuestItemList *ql = new QuestItemList();
+
+ for(uint8 i = 0; i < quest_items.size(); i++)
+ {
+ LootItem &item = quest_items[i];
+ if(!item.is_looted && item.AllowedForPlayer(player) )
+ {
+ ql->push_back(QuestItem(i));
+
+ // questitems get blocked when they first apper in a
+ // player's quest vector
+ //
+ // increase once if one looter only, looter-times if free for all
+ if (item.freeforall || !item.is_blocked)
+ ++unlootedCount;
+
+ item.is_blocked = true;
+
+ if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
+ break;
+ }
+ }
+ if (ql->empty())
+ {
+ delete ql;
+ return NULL;
+ }
+
+ PlayerQuestItems[player->GetGUIDLow()] = ql;
+ return ql;
+}
+
+QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
+{
+ QuestItemList *ql = new QuestItemList();
+
+ for(uint8 i = 0; i < items.size(); ++i)
+ {
+ LootItem &item = items[i];
+ if(!item.is_looted && !item.freeforall && item.conditionId && item.AllowedForPlayer(player))
+ {
+ ql->push_back(QuestItem(i));
+ if(!item.is_counted)
+ {
+ ++unlootedCount;
+ item.is_counted=true;
+ }
+ }
+ }
+ if (ql->empty())
+ {
+ delete ql;
+ return NULL;
+ }
+
+ PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql;
+ return ql;
+}
+
+//===================================================
+
+void Loot::NotifyItemRemoved(uint8 lootIndex)
+{
+ // notify all players that are looting this that the item was removed
+ // convert the index to the slot the player sees
+ std::set<uint64>::iterator i_next;
+ for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
+ {
+ i_next = i;
+ ++i_next;
+ if(Player* pl = ObjectAccessor::FindPlayer(*i))
+ pl->SendNotifyLootItemRemoved(lootIndex);
+ else
+ PlayersLooting.erase(i);
+ }
+}
+
+void Loot::NotifyMoneyRemoved()
+{
+ // notify all players that are looting this that the money was removed
+ std::set<uint64>::iterator i_next;
+ for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
+ {
+ i_next = i;
+ ++i_next;
+ if(Player* pl = ObjectAccessor::FindPlayer(*i))
+ pl->SendNotifyLootMoneyRemoved();
+ else
+ PlayersLooting.erase(i);
+ }
+}
+
+void Loot::NotifyQuestItemRemoved(uint8 questIndex)
+{
+ // when a free for all questitem is looted
+ // all players will get notified of it being removed
+ // (other questitems can be looted by each group member)
+ // bit inefficient but isnt called often
+
+ std::set<uint64>::iterator i_next;
+ for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
+ {
+ i_next = i;
+ ++i_next;
+ if(Player* pl = ObjectAccessor::FindPlayer(*i))
+ {
+ QuestItemMap::iterator pq = PlayerQuestItems.find(pl->GetGUIDLow());
+ if (pq != PlayerQuestItems.end() && pq->second)
+ {
+ // find where/if the player has the given item in it's vector
+ QuestItemList& pql = *pq->second;
+
+ uint8 j;
+ for (j = 0; j < pql.size(); ++j)
+ if (pql[j].index == questIndex)
+ break;
+
+ if (j < pql.size())
+ pl->SendNotifyLootItemRemoved(items.size()+j);
+ }
+ }
+ else
+ PlayersLooting.erase(i);
+ }
+}
+
+void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
+{
+ if (maxAmount > 0)
+ {
+ if (maxAmount <= minAmount)
+ gold = uint32(maxAmount * sWorld.getRate(RATE_DROP_MONEY));
+ else if ((maxAmount - minAmount) < 32700)
+ gold = uint32(urand(minAmount, maxAmount) * sWorld.getRate(RATE_DROP_MONEY));
+ else
+ gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8;
+ }
+}
+
+LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem)
+{
+ LootItem* item = NULL;
+ bool is_looted = true;
+ if (lootSlot >= items.size())
+ {
+ uint32 questSlot = lootSlot - items.size();
+ QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow());
+ if (itr != PlayerQuestItems.end() && questSlot < itr->second->size())
+ {
+ QuestItem *qitem2 = &itr->second->at(questSlot);
+ if(qitem)
+ *qitem = qitem2;
+ item = &quest_items[qitem2->index];
+ is_looted = qitem2->is_looted;
+ }
+ }
+ else
+ {
+ item = &items[lootSlot];
+ is_looted = item->is_looted;
+ if(item->freeforall)
+ {
+ QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUIDLow());
+ if (itr != PlayerFFAItems.end())
+ {
+ for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
+ if(iter->index==lootSlot)
+ {
+ QuestItem *ffaitem2 = (QuestItem*)&(*iter);
+ if(ffaitem)
+ *ffaitem = ffaitem2;
+ is_looted = ffaitem2->is_looted;
+ break;
+ }
+ }
+ }
+ else if(item->conditionId)
+ {
+ QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow());
+ if (itr != PlayerNonQuestNonFFAConditionalItems.end())
+ {
+ for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
+ {
+ if(iter->index==lootSlot)
+ {
+ QuestItem *conditem2 = (QuestItem*)&(*iter);
+ if(conditem)
+ *conditem = conditem2;
+ is_looted = conditem2->is_looted;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if(is_looted)
+ return NULL;
+
+ return item;
+}
+
+ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
+{
+ b << uint32(li.itemid);
+ b << uint32(li.count); // nr of items of this type
+ b << uint32(objmgr.GetItemPrototype(li.itemid)->DisplayInfoID);
+ b << uint32(li.randomSuffix);
+ b << uint32(li.randomPropertyId);
+ //b << uint8(0); // slot type - will send after this function call
+ return b;
+}
+
+ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
+{
+ Loot &l = lv.loot;
+
+ uint8 itemsShown = 0;
+
+ //gold
+ b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0);
+
+ size_t count_pos = b.wpos(); // pos of item count byte
+ b << uint8(0); // item count placeholder
+
+ switch (lv.permission)
+ {
+ case GROUP_PERMISSION:
+ {
+ // You are not the items proprietary, so you can only see
+ // blocked rolled items and quest items, and !ffa items
+ for (uint8 i = 0; i < l.items.size(); ++i)
+ {
+ if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
+ {
+ uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1;
+
+ b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here
+ b << uint8(slot_type); // 0 - get 1 - look only
+ ++itemsShown;
+ }
+ }
+ break;
+ }
+ case ALL_PERMISSION:
+ case MASTER_PERMISSION:
+ {
+ uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0;
+ for (uint8 i = 0; i < l.items.size(); ++i)
+ {
+ if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
+ {
+ b << uint8(i) << l.items[i]; //only send one-player loot items now, free for all will be sent later
+ b << uint8(slot_type); // 0 - get 2 - master selection
+ ++itemsShown;
+ }
+ }
+ break;
+ }
+ case NONE_PERMISSION:
+ default:
+ return b; // nothing output more
+ }
+
+ if (lv.qlist)
+ {
+ for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi)
+ {
+ LootItem &item = l.quest_items[qi->index];
+ if (!qi->is_looted && !item.is_looted)
+ {
+ b << uint8(l.items.size() + (qi - lv.qlist->begin()));
+ b << item;
+ b << uint8(0); // allow loot
+ ++itemsShown;
+ }
+ }
+ }
+
+ if (lv.ffalist)
+ {
+ for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi)
+ {
+ LootItem &item = l.items[fi->index];
+ if (!fi->is_looted && !item.is_looted)
+ {
+ b << uint8(fi->index) << item;
+ b << uint8(0); // allow loot
+ ++itemsShown;
+ }
+ }
+ }
+
+ if (lv.conditionallist)
+ {
+ for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci)
+ {
+ LootItem &item = l.items[ci->index];
+ if (!ci->is_looted && !item.is_looted)
+ {
+ b << uint8(ci->index) << item;
+ b << uint8(0); // allow loot
+ ++itemsShown;
+ }
+ }
+ }
+
+ //update number of items shown
+ b.put<uint8>(count_pos,itemsShown);
+
+ return b;
+}
+
+//
+// --------- LootTemplate::LootGroup ---------
+//
+
+// Adds an entry to the group (at loading stage)
+void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
+{
+ if (item.chance != 0)
+ ExplicitlyChanced.push_back(item);
+ else
+ EqualChanced.push_back(item);
+}
+
+// Rolls an item from the group, returns NULL if all miss their chances
+LootStoreItem const * LootTemplate::LootGroup::Roll() const
+{
+ if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked
+ {
+ float Roll = rand_chance();
+
+ for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) //check each explicitly chanced entry in the template and modify its chance based on quality.
+ {
+ if(ExplicitlyChanced[i].chance>=100.f)
+ return &ExplicitlyChanced[i];
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid);
+ float qualityMultiplier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
+ Roll -= ExplicitlyChanced[i].chance * qualityMultiplier;
+ if (Roll < 0)
+ return &ExplicitlyChanced[i];
+ }
+ }
+ if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part
+ return &EqualChanced[irand(0, EqualChanced.size()-1)];
+
+ return NULL; // Empty drop from the group
+}
+
+// True if group includes at least 1 quest drop entry
+bool LootTemplate::LootGroup::HasQuestDrop() const
+{
+ for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
+ if (i->needs_quest)
+ return true;
+ for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
+ if (i->needs_quest)
+ return true;
+ return false;
+}
+
+// True if group includes at least 1 quest drop entry for active quests of the player
+bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
+{
+ for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
+ if (player->HasQuestForItem(i->itemid))
+ return true;
+ for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
+ if (player->HasQuestForItem(i->itemid))
+ return true;
+ return false;
+}
+
+// Rolls an item from the group (if any takes its chance) and adds the item to the loot
+void LootTemplate::LootGroup::Process(Loot& loot) const
+{
+ LootStoreItem const * item = Roll();
+ if (item != NULL)
+ loot.AddItem(*item);
+}
+
+// Overall chance for the group without equal chanced items
+float LootTemplate::LootGroup::RawTotalChance() const
+{
+ float result = 0;
+
+ for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
+ if ( !i->needs_quest )
+ result += i->chance;
+
+ return result;
+}
+
+// Overall chance for the group
+float LootTemplate::LootGroup::TotalChance() const
+{
+ float result = RawTotalChance();
+
+ if (!EqualChanced.empty() && result < 100.0f)
+ return 100.0f;
+
+ return result;
+}
+
+void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const
+{
+ float chance = RawTotalChance();
+ if (chance > 101.0f) // TODO: replace with 100% when DBs will be ready
+ {
+ sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance);
+ }
+
+ if(chance >= 100.0f && !EqualChanced.empty())
+ {
+ sLog.outErrorDb("Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance);
+ }
+}
+
+void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
+{
+ for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
+ {
+ if(ieItr->mincountOrRef < 0)
+ {
+ if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ else if(ref_set)
+ ref_set->erase(-ieItr->mincountOrRef);
+ }
+ }
+
+ for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
+ {
+ if(ieItr->mincountOrRef < 0)
+ {
+ if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ else if(ref_set)
+ ref_set->erase(-ieItr->mincountOrRef);
+ }
+ }
+}
+
+//
+// --------- LootTemplate ---------
+//
+
+// Adds an entry to the group (at loading stage)
+void LootTemplate::AddEntry(LootStoreItem& item)
+{
+ if (item.group > 0 && item.mincountOrRef > 0) // Group
+ {
+ if (item.group >= Groups.size())
+ Groups.resize(item.group); // Adds new group the the loot template if needed
+ Groups[item.group-1].AddEntry(item); // Adds new entry to the group
+ }
+ else // Non-grouped entries and references are stored together
+ Entries.push_back(item);
+}
+
+// Rolls for every item in the template and adds the rolled items the the loot
+void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const
+{
+ if (groupId) // Group reference uses own processing of the group
+ {
+ if (groupId > Groups.size())
+ return; // Error message already printed at loading stage
+
+ Groups[groupId-1].Process(loot);
+ return;
+ }
+
+ // Rolling non-grouped items
+ for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ )
+ {
+ if ( !i->Roll() )
+ continue; // Bad luck for the entry
+
+ if (i->mincountOrRef < 0) // References processing
+ {
+ LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef);
+
+ if(!Referenced)
+ continue; // Error message already printed at loading stage
+
+ for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator
+ Referenced->Process(loot, store, i->group); // Ref processing
+ }
+ else // Plain entries (not a reference, not grouped)
+ loot.AddItem(*i); // Chance is already checked, just add
+ }
+
+ // Now processing groups
+ for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; i++ )
+ i->Process(loot);
+}
+
+// True if template includes at least 1 quest drop entry
+bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const
+{
+ if (groupId) // Group reference
+ {
+ if (groupId > Groups.size())
+ return false; // Error message [should be] already printed at loading stage
+ return Groups[groupId-1].HasQuestDrop();
+ }
+
+ for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i )
+ {
+ if (i->mincountOrRef < 0) // References
+ {
+ LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
+ if( Referenced ==store.end() )
+ continue; // Error message [should be] already printed at loading stage
+ if (Referenced->second->HasQuestDrop(store, i->group) )
+ return true;
+ }
+ else if ( i->needs_quest )
+ return true; // quest drop found
+ }
+
+ // Now processing groups
+ for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; i++ )
+ if (i->HasQuestDrop())
+ return true;
+
+ return false;
+}
+
+// True if template includes at least 1 quest drop for an active quest of the player
+bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const
+{
+ if (groupId) // Group reference
+ {
+ if (groupId > Groups.size())
+ return false; // Error message already printed at loading stage
+ return Groups[groupId-1].HasQuestDropForPlayer(player);
+ }
+
+ // Checking non-grouped entries
+ for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ )
+ {
+ if (i->mincountOrRef < 0) // References processing
+ {
+ LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
+ if (Referenced == store.end() )
+ continue; // Error message already printed at loading stage
+ if (Referenced->second->HasQuestDropForPlayer(store, player, i->group) )
+ return true;
+ }
+ else if ( player->HasQuestForItem(i->itemid) )
+ return true; // active quest drop found
+ }
+
+ // Now checking groups
+ for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i )
+ if (i->HasQuestDrop())
+ return true;
+
+ return false;
+}
+
+// Checks integrity of the template
+void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
+{
+ // Checking group chances
+ for (uint32 i=0; i < Groups.size(); ++i)
+ Groups[i].Verify(lootstore,id,i+1);
+
+ // TODO: References validity checks
+}
+
+void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
+{
+ for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
+ {
+ if(ieItr->mincountOrRef < 0)
+ {
+ if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ else if(ref_set)
+ ref_set->erase(-ieItr->mincountOrRef);
+ }
+ }
+
+ for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
+ grItr->CheckLootRefs(store,ref_set);
+}
+
+void LoadLootTemplates_Creature()
+{
+ LootIdSet ids_set, ids_setUsed;
+ LootTemplates_Creature.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
+ {
+ if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
+ {
+ if(uint32 lootid = cInfo->lootid)
+ {
+ if(!ids_set.count(lootid))
+ LootTemplates_Creature.ReportNotExistedId(lootid);
+ else
+ ids_setUsed.insert(lootid);
+ }
+ }
+ }
+ for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
+ ids_set.erase(*itr);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Creature.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Disenchant()
+{
+ LootIdSet ids_set, ids_setUsed;
+ LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
+ {
+ if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
+ {
+ if(uint32 lootid = proto->DisenchantID)
+ {
+ if(!ids_set.count(lootid))
+ LootTemplates_Disenchant.ReportNotExistedId(lootid);
+ else
+ ids_setUsed.insert(lootid);
+ }
+ }
+ }
+ for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
+ ids_set.erase(*itr);
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Disenchant.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Fishing()
+{
+ LootIdSet ids_set;
+ LootTemplates_Fishing.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i )
+ {
+ if(AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i))
+ if(ids_set.count(areaEntry->ID))
+ ids_set.erase(areaEntry->ID);
+ }
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Fishing.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Gameobject()
+{
+ LootIdSet ids_set, ids_setUsed;
+ LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i )
+ {
+ if(GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
+ {
+ if(uint32 lootid = GameObject::GetLootId(gInfo))
+ {
+ if(!ids_set.count(lootid))
+ LootTemplates_Gameobject.ReportNotExistedId(lootid);
+ else
+ ids_setUsed.insert(lootid);
+ }
+ }
+ }
+ for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
+ ids_set.erase(*itr);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Gameobject.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Item()
+{
+ LootIdSet ids_set;
+ LootTemplates_Item.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
+ if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
+ if(ids_set.count(proto->ItemId))
+ ids_set.erase(proto->ItemId);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Item.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Pickpocketing()
+{
+ LootIdSet ids_set, ids_setUsed;
+ LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
+ {
+ if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
+ {
+ if(uint32 lootid = cInfo->pickpocketLootId)
+ {
+ if(!ids_set.count(lootid))
+ LootTemplates_Pickpocketing.ReportNotExistedId(lootid);
+ else
+ ids_setUsed.insert(lootid);
+ }
+ }
+ }
+ for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
+ ids_set.erase(*itr);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Pickpocketing.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Prospecting()
+{
+ LootIdSet ids_set;
+ LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
+ if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
+ if(ids_set.count(proto->ItemId))
+ ids_set.erase(proto->ItemId);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Prospecting.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_QuestMail()
+{
+ LootIdSet ids_set;
+ LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates();
+ for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr )
+ if(ids_set.count(itr->first))
+ ids_set.erase(itr->first);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_QuestMail.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Skinning()
+{
+ LootIdSet ids_set, ids_setUsed;
+ LootTemplates_Skinning.LoadAndCollectLootIds(ids_set);
+
+ // remove real entries and check existence loot
+ for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
+ {
+ if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
+ {
+ if(uint32 lootid = cInfo->SkinLootId)
+ {
+ if(!ids_set.count(lootid))
+ LootTemplates_Skinning.ReportNotExistedId(lootid);
+ else
+ ids_setUsed.insert(lootid);
+ }
+ }
+ }
+ for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
+ ids_set.erase(*itr);
+
+ // output error for any still listed (not referenced from appropriate table) ids
+ LootTemplates_Skinning.ReportUnusedIds(ids_set);
+}
+
+void LoadLootTemplates_Reference()
+{
+ LootIdSet ids_set;
+ LootTemplates_Reference.LoadAndCollectLootIds(ids_set);
+
+ // check references and remove used
+ LootTemplates_Creature.CheckLootRefs(&ids_set);
+ LootTemplates_Fishing.CheckLootRefs(&ids_set);
+ LootTemplates_Gameobject.CheckLootRefs(&ids_set);
+ LootTemplates_Item.CheckLootRefs(&ids_set);
+ LootTemplates_Pickpocketing.CheckLootRefs(&ids_set);
+ LootTemplates_Skinning.CheckLootRefs(&ids_set);
+ LootTemplates_Disenchant.CheckLootRefs(&ids_set);
+ LootTemplates_Prospecting.CheckLootRefs(&ids_set);
+ LootTemplates_QuestMail.CheckLootRefs(&ids_set);
+ LootTemplates_Reference.CheckLootRefs(&ids_set);
+
+ // output error for any still listed ids (not referenced from any loot table)
+ LootTemplates_Reference.ReportUnusedIds(ids_set);
+}
diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h
new file mode 100644
index 00000000000..fe063a447fc
--- /dev/null
+++ b/src/game/LootMgr.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_LOOTMGR_H
+#define MANGOS_LOOTMGR_H
+
+#include "ItemEnchantmentMgr.h"
+#include "ByteBuffer.h"
+#include "Utilities/LinkedReference/RefManager.h"
+
+#include <map>
+#include <vector>
+
+enum RollType
+{
+ ROLL_PASS = 0,
+ ROLL_NEED = 1,
+ ROLL_GREED = 2
+};
+
+#define MAX_NR_LOOT_ITEMS 16
+// note: the client cannot show more than 16 items total
+#define MAX_NR_QUEST_ITEMS 32
+// unrelated to the number of quest items shown, just for reserve
+
+enum LootMethod
+{
+ FREE_FOR_ALL = 0,
+ ROUND_ROBIN = 1,
+ MASTER_LOOT = 2,
+ GROUP_LOOT = 3,
+ NEED_BEFORE_GREED = 4
+};
+
+enum PermissionTypes
+{
+ ALL_PERMISSION = 0,
+ GROUP_PERMISSION = 1,
+ MASTER_PERMISSION = 2,
+ NONE_PERMISSION = 3
+};
+
+class Player;
+class LootStore;
+
+struct LootStoreItem
+{
+ uint32 itemid; // id of the item
+ float chance; // always positive, chance to drop for both quest and non-quest items, chance to be used for refs
+ int32 mincountOrRef; // mincount for drop items (positive) or minus referenced TemplateleId (negative)
+ uint8 group :8;
+ uint8 maxcount :8; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative)
+ uint16 conditionId :16; // additional loot condition Id
+ bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB)
+
+ // Constructor, converting ChanceOrQuestChance -> (chance, needs_quest)
+ // displayid is filled in IsValid() which must be called after
+ LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, int8 _group, uint8 _conditionId, int32 _mincountOrRef, uint8 _maxcount)
+ : itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef),
+ group(_group), maxcount(_maxcount), conditionId(_conditionId),
+ needs_quest(_chanceOrQuestChance < 0) {}
+
+ bool Roll() const; // Checks if the entry takes it's chance (at loot generation)
+ bool IsValid(LootStore const& store, uint32 entry) const;
+ // Checks correctness of values
+};
+
+struct LootItem
+{
+ uint32 itemid;
+ uint32 randomSuffix;
+ int32 randomPropertyId;
+ uint16 conditionId :16; // allow compiler pack structure
+ uint8 count : 8;
+ bool is_looted : 1;
+ bool is_blocked : 1;
+ bool freeforall : 1; // free for all
+ bool is_underthreshold : 1;
+ bool is_counted : 1;
+ bool needs_quest : 1; // quest drop
+
+ // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
+ // Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0)
+ explicit LootItem(LootStoreItem const& li);
+
+ // Basic checks for player/item compatibility - if false no chance to see the item in the loot
+ bool AllowedForPlayer(Player const * player) const;
+};
+
+struct QuestItem
+{
+ uint8 index; // position in quest_items;
+ bool is_looted;
+
+ QuestItem()
+ : index(0), is_looted(false) {}
+
+ QuestItem(uint8 _index, bool _islooted = false)
+ : index(_index), is_looted(_islooted) {}
+};
+
+struct Loot;
+class LootTemplate;
+
+typedef std::vector<QuestItem> QuestItemList;
+typedef std::map<uint32, QuestItemList *> QuestItemMap;
+typedef std::vector<LootStoreItem> LootStoreItemList;
+typedef HM_NAMESPACE::hash_map<uint32, LootTemplate*> LootTemplateMap;
+
+typedef std::set<uint32> LootIdSet;
+
+class LootStore
+{
+ public:
+ explicit LootStore(char const* name, char const* entryName) : m_name(name), m_entryName(entryName) {}
+ virtual ~LootStore() { Clear(); }
+
+ void Verify() const;
+
+ void LoadAndCollectLootIds(LootIdSet& ids_set);
+ void CheckLootRefs(LootIdSet* ref_set = NULL) const;// check existence reference and remove it from ref_set
+ void ReportUnusedIds(LootIdSet const& ids_set) const;
+ void ReportNotExistedId(uint32 id) const;
+
+ bool HaveLootFor(uint32 loot_id) const { return m_LootTemplates.find(loot_id) != m_LootTemplates.end(); }
+ bool HaveQuestLootFor(uint32 loot_id) const;
+ bool HaveQuestLootForPlayer(uint32 loot_id,Player* player) const;
+
+ LootTemplate const* GetLootFor(uint32 loot_id) const;
+
+ char const* GetName() const { return m_name; }
+ char const* GetEntryName() const { return m_entryName; }
+ protected:
+ void LoadLootTable();
+ void Clear();
+ private:
+ LootTemplateMap m_LootTemplates;
+ char const* m_name;
+ char const* m_entryName;
+};
+
+class LootTemplate
+{
+ class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
+ typedef std::vector<LootGroup> LootGroups;
+
+ public:
+ // Adds an entry to the group (at loading stage)
+ void AddEntry(LootStoreItem& item);
+ // Rolls for every item in the template and adds the rolled items the the loot
+ void Process(Loot& loot, LootStore const& store, uint8 GroupId = 0) const;
+
+ // True if template includes at least 1 quest drop entry
+ bool HasQuestDrop(LootTemplateMap const& store, uint8 GroupId = 0) const;
+ // True if template includes at least 1 quest drop for an active quest of the player
+ bool HasQuestDropForPlayer(LootTemplateMap const& store, Player const * player, uint8 GroupId = 0) const;
+
+ // Checks integrity of the template
+ void Verify(LootStore const& store, uint32 Id) const;
+ void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const;
+ private:
+ LootStoreItemList Entries; // not grouped only
+ LootGroups Groups; // groups have own (optimised) processing, grouped entries go there
+};
+
+//=====================================================
+
+class LootValidatorRef : public Reference<Loot, LootValidatorRef>
+{
+ public:
+ LootValidatorRef() {}
+ void targetObjectDestroyLink() {}
+ void sourceObjectDestroyLink() {}
+};
+
+//=====================================================
+
+class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef>
+{
+ public:
+ typedef LinkedListHead::Iterator< LootValidatorRef > iterator;
+
+ LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); }
+ LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); }
+
+ iterator begin() { return iterator(getFirst()); }
+ iterator end() { return iterator(NULL); }
+ iterator rbegin() { return iterator(getLast()); }
+ iterator rend() { return iterator(NULL); }
+};
+
+//=====================================================
+
+struct Loot
+{
+ QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; }
+ QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; }
+ QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; }
+
+ QuestItemList* FillFFALoot(Player* player);
+ QuestItemList* FillQuestLoot(Player* player);
+ QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player);
+
+ std::vector<LootItem> items;
+ std::vector<LootItem> quest_items;
+ uint32 gold;
+ uint8 unlootedCount;
+
+ Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0) {}
+ ~Loot() { clear(); }
+
+ // if loot becomes invalid this reference is used to inform the listener
+ void addLootValidatorRef(LootValidatorRef* pLootValidatorRef)
+ {
+ i_LootValidatorRefManager.insertFirst(pLootValidatorRef);
+ }
+
+ // void clear();
+ void clear()
+ {
+ items.clear(); gold = 0; PlayersLooting.clear();
+ for (QuestItemMap::iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr)
+ delete itr->second;
+ for (QuestItemMap::iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr)
+ delete itr->second;
+ for (QuestItemMap::iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr)
+ delete itr->second;
+
+ PlayerQuestItems.clear();
+ PlayerFFAItems.clear();
+ PlayerNonQuestNonFFAConditionalItems.clear();
+
+ items.clear();
+ quest_items.clear();
+ gold = 0;
+ unlootedCount = 0;
+ i_LootValidatorRefManager.clearReferences();
+ }
+
+ bool empty() const { return items.empty() && gold == 0; }
+ bool isLooted() const { return gold == 0 && unlootedCount == 0; }
+
+ void NotifyItemRemoved(uint8 lootIndex);
+ void NotifyQuestItemRemoved(uint8 questIndex);
+ void NotifyMoneyRemoved();
+ void AddLooter(uint64 GUID) { PlayersLooting.insert(GUID); }
+ void RemoveLooter(uint64 GUID) { PlayersLooting.erase(GUID); }
+
+ void generateMoneyLoot(uint32 minAmount, uint32 maxAmount);
+ void FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner);
+
+ // Inserts the item into the loot (called by LootTemplate processors)
+ void AddItem(LootStoreItem const & item);
+
+ LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL);
+ private:
+ std::set<uint64> PlayersLooting;
+ QuestItemMap PlayerQuestItems;
+ QuestItemMap PlayerFFAItems;
+ QuestItemMap PlayerNonQuestNonFFAConditionalItems;
+
+ // All rolls are registered here. They need to know, when the loot is not valid anymore
+ LootValidatorRefManager i_LootValidatorRefManager;
+
+};
+
+struct LootView
+{
+ Loot &loot;
+ QuestItemList *qlist;
+ QuestItemList *ffalist;
+ QuestItemList *conditionallist;
+ Player *viewer;
+ PermissionTypes permission;
+ LootView(Loot &_loot, QuestItemList *_qlist, QuestItemList *_ffalist, QuestItemList *_conditionallist, Player *_viewer,PermissionTypes _permission = ALL_PERMISSION)
+ : loot(_loot), qlist(_qlist), ffalist(_ffalist), conditionallist(_conditionallist), viewer(_viewer), permission(_permission) {}
+};
+
+extern LootStore LootTemplates_Creature;
+extern LootStore LootTemplates_Fishing;
+extern LootStore LootTemplates_Gameobject;
+extern LootStore LootTemplates_Item;
+extern LootStore LootTemplates_Pickpocketing;
+extern LootStore LootTemplates_Skinning;
+extern LootStore LootTemplates_Disenchant;
+extern LootStore LootTemplates_Prospecting;
+extern LootStore LootTemplates_QuestMail;
+
+void LoadLootTemplates_Creature();
+void LoadLootTemplates_Fishing();
+void LoadLootTemplates_Gameobject();
+void LoadLootTemplates_Item();
+void LoadLootTemplates_Pickpocketing();
+void LoadLootTemplates_Skinning();
+void LoadLootTemplates_Disenchant();
+void LoadLootTemplates_Prospecting();
+void LoadLootTemplates_QuestMail();
+void LoadLootTemplates_Reference();
+
+inline void LoadLootTables()
+{
+ LoadLootTemplates_Creature();
+ LoadLootTemplates_Fishing();
+ LoadLootTemplates_Gameobject();
+ LoadLootTemplates_Item();
+ LoadLootTemplates_Pickpocketing();
+ LoadLootTemplates_Skinning();
+ LoadLootTemplates_Disenchant();
+ LoadLootTemplates_Prospecting();
+ LoadLootTemplates_QuestMail();
+ LoadLootTemplates_Reference();
+}
+
+ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li);
+ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
+#endif
diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp
new file mode 100644
index 00000000000..a97ed9470a7
--- /dev/null
+++ b/src/game/Mail.cpp
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Mail.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "Unit.h"
+#include "Language.h"
+#include "Database/DBCStores.h"
+
+void MailItem::deleteItem( bool inDB )
+{
+ if(item)
+ {
+ if(inDB)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
+
+ delete item;
+ item=NULL;
+ }
+}
+
+void WorldSession::HandleSendMail(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1+1+1+4+4+1+4+4+8+1);
+
+ uint64 mailbox, unk3;
+ std::string receiver, subject, body;
+ uint32 unk1, unk2, money, COD;
+ uint8 unk4;
+ recv_data >> mailbox;
+ recv_data >> receiver;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+1+1+4+4+1+4+4+8+1);
+
+ recv_data >> subject;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+1+4+4+1+4+4+8+1);
+
+ recv_data >> body;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+4+4+8+1);
+
+ recv_data >> unk1; // stationery?
+ recv_data >> unk2; // 0x00000000
+
+ MailItemsInfo mi;
+
+ uint8 items_count;
+ recv_data >> items_count; // attached items count
+
+ if(items_count > 12) // client limit
+ return;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1);
+
+ if(items_count)
+ {
+ for(uint8 i = 0; i < items_count; ++i)
+ {
+ uint8 item_slot;
+ uint64 item_guid;
+ recv_data >> item_slot;
+ recv_data >> item_guid;
+ mi.AddItem(GUID_LOPART(item_guid), item_slot);
+ }
+ }
+
+ recv_data >> money >> COD; // money and cod
+ recv_data >> unk3; // const 0
+ recv_data >> unk4; // const 0
+
+ items_count = mi.size(); // this is the real size after the duplicates have been removed
+
+ if (receiver.empty())
+ return;
+
+ Player* pl = _player;
+
+ uint64 rc = 0;
+ if(normalizePlayerName(receiver))
+ rc = objmgr.GetPlayerGUIDByName(receiver);
+
+ if (!rc)
+ {
+ sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
+ pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
+ pl->SendMailResult(0, 0, MAIL_ERR_RECIPIENT_NOT_FOUND);
+ return;
+ }
+
+ sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
+
+ if(pl->GetGUID() == rc)
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_CANNOT_SEND_TO_SELF);
+ return;
+ }
+
+ uint32 reqmoney = money + 30;
+ if (items_count)
+ reqmoney = money + (30 * items_count);
+
+ if (pl->GetMoney() < reqmoney)
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY);
+ return;
+ }
+
+ Player *receive = objmgr.GetPlayer(rc);
+
+ uint32 rc_team = 0;
+ uint8 mails_count = 0; //do not allow to send to one player more than 100 mails
+
+ if(receive)
+ {
+ rc_team = receive->GetTeam();
+ mails_count = receive->GetMailSize();
+ }
+ else
+ {
+ rc_team = objmgr.GetPlayerTeamByGUID(rc);
+ QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc));
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ mails_count = fields[0].GetUInt32();
+ delete result;
+ }
+ }
+ //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
+ if (mails_count > 100)
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+ // test the receiver's Faction...
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER)
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM);
+ return;
+ }
+
+ if (items_count)
+ {
+ for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+
+ if(!mailItem.item_guidlow)
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+
+ mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM));
+ // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
+ if(!mailItem.item || !mailItem.item->CanBeTraded())
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+ if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
+ {
+ pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+ }
+ }
+ pl->SendMailResult(0, 0, MAIL_OK);
+
+ uint32 itemTextId = 0;
+ if (!body.empty())
+ {
+ itemTextId = objmgr.CreateItemText( body );
+ }
+
+ pl->ModifyMoney( -int32(reqmoney) );
+
+ bool needItemDelay = false;
+
+ if(items_count > 0 || money > 0)
+ {
+ uint32 rc_account = 0;
+ if(receive)
+ rc_account = receive->GetSession()->GetAccountId();
+ else
+ rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
+
+ if (items_count > 0)
+ {
+ for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+ if(!mailItem.item)
+ continue;
+
+ mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0;
+
+ if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ {
+ sLog.outCommand("GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)",
+ GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account);
+ }
+
+ pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true);
+ CharacterDatabase.BeginTransaction();
+ mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory
+ mailItem.item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
+ // owner in data will set at mail receive and item extracting
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), mailItem.item->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+ }
+
+ // if item send to character at another account, then apply item delivery delay
+ needItemDelay = pl->GetSession()->GetAccountId() != rc_account;
+ }
+
+ if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ sLog.outCommand("GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
+ GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
+ }
+ }
+
+ // If theres is an item, there is a one hour delivery delay if sent to another account's character.
+ uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay);
+
+ CharacterDatabase.BeginTransaction();
+ pl->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+}
+
+//called when mail is read
+void WorldSession::HandleMarkAsRead(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ Player *pl = _player;
+ Mail *m = pl->GetMail(mailId);
+ if (m)
+ {
+ if (pl->unReadMails)
+ --pl->unReadMails;
+ m->checked = m->checked | MAIL_CHECK_MASK_READ;
+ // m->expire_time = time(NULL) + (30 * DAY); // Expire time do not change at reading mail
+ pl->m_mailsUpdated = true;
+ m->state = MAIL_STATE_CHANGED;
+ }
+}
+
+//called when client deletes mail
+void WorldSession::HandleMailDelete(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ Player* pl = _player;
+ pl->m_mailsUpdated = true;
+ Mail *m = pl->GetMail(mailId);
+ if(m)
+ m->state = MAIL_STATE_DELETED;
+ pl->SendMailResult(mailId, MAIL_DELETED, 0);
+}
+
+void WorldSession::HandleReturnToSender(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ Player *pl = _player;
+ Mail *m = pl->GetMail(mailId);
+ if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ {
+ pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+ //we can return mail now
+ //so firstly delete the old one
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId);
+ // needed?
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
+ CharacterDatabase.CommitTransaction();
+ pl->RemoveMail(mailId);
+
+ MailItemsInfo mi;
+
+ if(m->HasItems())
+ {
+ for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ {
+ Item *item = pl->GetMItem(itr2->item_guid);
+ if(item)
+ mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ else
+ {
+ //WTF?
+ }
+
+ pl->RemoveMItem(itr2->item_guid);
+ }
+ }
+
+ SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, 0, m->mailTemplateId);
+
+ delete m; //we can deallocate old mail
+ pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, 0);
+}
+
+void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, std::string subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint32 COD, uint16 mailTemplateId )
+{
+ if(messageType != MAIL_NORMAL) // return only to players
+ {
+ mi->deleteIncludedItems(true);
+ return;
+ }
+
+ Player *receiver = objmgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
+
+ uint32 rc_account = 0;
+ if(!receiver)
+ rc_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
+
+ if(!receiver && !rc_account) // sender not exist
+ {
+ mi->deleteIncludedItems(true);
+ return;
+ }
+
+ // preper mail and send in other case
+ bool needItemDelay = false;
+
+ if(mi && !mi->empty())
+ {
+ // if item send to character at another account, then apply item delivery delay
+ needItemDelay = sender_acc != rc_account;
+
+ // set owner to new receiver (to prevent delete item with sender char deleting)
+ CharacterDatabase.BeginTransaction();
+ for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+ mailItem.item->SaveToDB(); // item not in inventory and can be save standalone
+ // owner in data will set at mail receive and item extracting
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, mailItem.item->GetGUIDLow());
+ }
+ CharacterDatabase.CommitTransaction();
+ }
+
+ // If theres is an item, there is a one hour delivery delay.
+ uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(receiver, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, sender_guid, receiver_guid, subject, itemTextId, mi, money, 0, MAIL_CHECK_MASK_RETURNED,deliver_delay,mailTemplateId);
+}
+
+//called when player takes item attached in mail
+void WorldSession::HandleTakeItem(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+ uint32 itemId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ recv_data >> itemId; // item guid low?
+ Player* pl = _player;
+
+ Mail* m = pl->GetMail(mailId);
+ if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ {
+ pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+
+ // prevent cheating with skip client money check
+ if(pl->GetMoney() < m->COD)
+ {
+ pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY);
+ return;
+ }
+
+ Item *it = pl->GetMItem(itemId);
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, it, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ m->RemoveItem(itemId);
+ m->removedItems.push_back(itemId);
+
+ if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail
+ {
+ uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER);
+ Player *receive = objmgr.GetPlayer(sender_guid);
+
+ uint32 sender_accId = 0;
+
+ if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ {
+ std::string sender_name;
+ if(receive)
+ {
+ sender_accId = receive->GetSession()->GetAccountId();
+ sender_name = receive->GetName();
+ }
+ else
+ {
+ // can be calculated early
+ sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
+
+ if(!objmgr.GetPlayerNameByGUID(sender_guid,sender_name))
+ sender_name = objmgr.GetMangosStringForDBCLocale(LANG_UNKNOWN);
+ }
+ sLog.outCommand("GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)",
+ GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount(),m->COD,sender_name.c_str(),sender_accId);
+ }
+ else if(!receive)
+ sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
+
+ // check player existanse
+ if(receive || sender_accId)
+ {
+ WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, m->receiver, m->sender, m->subject, 0, NULL, m->COD, 0, MAIL_CHECK_MASK_COD_PAYMENT);
+ }
+
+ pl->ModifyMoney( -int32(m->COD) );
+ }
+ m->COD = 0;
+ m->state = MAIL_STATE_CHANGED;
+ pl->m_mailsUpdated = true;
+ pl->RemoveMItem(it->GetGUIDLow());
+
+ uint32 count = it->GetCount(); // save counts before store and possible merge with deleting
+ pl->MoveItemToInventory(dest,it,true);
+
+ CharacterDatabase.BeginTransaction();
+ pl->SaveInventoryAndGoldToDB();
+ pl->_SaveMail();
+ CharacterDatabase.CommitTransaction();
+
+ pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
+ }
+ else
+ pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_BAG_FULL, msg);
+}
+
+void WorldSession::HandleTakeMoney(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ Player *pl = _player;
+
+ Mail* m = pl->GetMail(mailId);
+ if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ {
+ pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+
+ pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, 0);
+
+ pl->ModifyMoney(m->money);
+ m->money = 0;
+ m->state = MAIL_STATE_CHANGED;
+ pl->m_mailsUpdated = true;
+
+ // save money and mail to prevent cheating
+ CharacterDatabase.BeginTransaction();
+ pl->SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,pl->GetMoney(),pl->GetGUID());
+ pl->_SaveMail();
+ CharacterDatabase.CommitTransaction();
+}
+
+//called when player lists his received mails
+void WorldSession::HandleGetMail(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 mailbox;
+ recv_data >> mailbox;
+
+ //GameObject* obj = ObjectAccessor::GetGameObject(_player, mailbox);
+ //if(!obj || !obj->IsMailBox())
+ // return;
+
+ Player* pl = _player;
+
+ //load players mails, and mailed items
+ if(!pl->m_mailsLoaded)
+ pl ->_LoadMail();
+
+ // client can't work with packets > max int16 value
+ const uint32 maxPacketSize = 32767;
+
+ uint32 mails_count = 0; // real send to client mails amount
+
+ WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size
+ data << uint8(0); // mail's count
+ time_t cur_time = time(NULL);
+
+ for(PlayerMails::iterator itr = pl->GetmailBegin(); itr != pl->GetmailEnd(); ++itr)
+ {
+ // skip deleted or not delivered (deliver delay not expired) mails
+ if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time)
+ continue;
+
+ uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12)
+
+ size_t next_mail_size = 2+4+1+8+4*8+((*itr)->subject.size()+1)+1+item_count*(1+4+4+6*3*4+4+4+1+4+4+4);
+
+ if(data.wpos()+next_mail_size > maxPacketSize)
+ break;
+
+ data << (uint16) 0x0040; // unknown 2.3.0, different values
+ data << (uint32) (*itr)->messageID; // Message ID
+ data << (uint8) (*itr)->messageType; // Message Type
+
+ switch((*itr)->messageType)
+ {
+ case MAIL_NORMAL: // sender guid
+ data << uint64(MAKE_NEW_GUID((*itr)->sender, 0, HIGHGUID_PLAYER));
+ break;
+ case MAIL_CREATURE:
+ case MAIL_GAMEOBJECT:
+ case MAIL_AUCTION:
+ data << (uint32) (*itr)->sender; // creature/gameobject entry, auction id
+ break;
+ case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI
+ break;
+ }
+
+ data << (uint32) (*itr)->COD; // COD
+ data << (uint32) (*itr)->itemTextId; // sure about this
+ data << (uint32) 0; // unknown
+ data << (uint32) (*itr)->stationery; // stationery (Stationery.dbc)
+ data << (uint32) (*itr)->money; // Gold
+ data << (uint32) 0x04; // unknown, 0x4 - auction, 0x10 - normal
+ // Time
+ data << (float) ((*itr)->expire_time-time(NULL))/DAY;
+ data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc)
+ data << (*itr)->subject; // Subject string - once 00, when mail type = 3
+
+ data << (uint8) item_count;
+
+ for(uint8 i = 0; i < item_count; ++i)
+ {
+ Item *item = pl->GetMItem((*itr)->items[i].item_guid);
+ // item index (0-6?)
+ data << (uint8) i;
+ // item guid low?
+ data << (uint32) (item ? item->GetGUIDLow() : 0);
+ // entry
+ data << (uint32) (item ? item->GetEntry() : 0);
+ for(uint8 j = 0; j < 6; ++j)
+ {
+ // unsure
+ data << (uint32) (item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0);
+ // unsure
+ data << (uint32) (item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0);
+ // unsure
+ data << (uint32) (item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0);
+ }
+ // can be negative
+ data << (uint32) (item ? item->GetItemRandomPropertyId() : 0);
+ // unk
+ data << (uint32) (item ? item->GetItemSuffixFactor() : 0);
+ // stack count
+ data << (uint8) (item ? item->GetCount() : 0);
+ // charges
+ data << (uint32) (item ? item->GetSpellCharges() : 0);
+ // durability
+ data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0);
+ // durability
+ data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0);
+ }
+
+ mails_count += 1;
+ }
+
+ data.put<uint8>(0, mails_count); // set real send mails to client
+ SendPacket(&data);
+
+ // recalculate m_nextMailDelivereTime and unReadMails
+ _player->UpdateNextMailTimeAndUnreads();
+}
+
+///this function is called when client needs mail message body, or when player clicks on item which has ITEM_FIELD_ITEM_TEXT_ID > 0
+void WorldSession::HandleItemTextQuery(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+4);
+
+ uint32 itemTextId;
+ uint32 mailId; //this value can be item id in bag, but it is also mail id
+ uint32 unk; //maybe something like state - 0x70000000
+
+ recv_data >> itemTextId >> mailId >> unk;
+
+ //some check needed, if player has item with guid mailId, or has mail with id mailId
+
+ sLog.outDebug("CMSG_ITEM_TEXT_QUERY itemguid: %u, mailId: %u, unk: %u", itemTextId, mailId, unk);
+
+ WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4+10));// guess size
+ data << itemTextId;
+ data << objmgr.GetItemText( itemTextId );
+ SendPacket(&data);
+}
+
+//used when player copies mail body to his inventory
+void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 mailbox;
+ uint32 mailId;
+
+ recv_data >> mailbox >> mailId;
+
+ Player *pl = _player;
+
+ Mail* m = pl->GetMail(mailId);
+ if(!m || !m->itemTextId || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ {
+ pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+
+ Item *bodyItem = new Item; // This is not bag and then can be used new Item.
+ if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl))
+ {
+ delete bodyItem;
+ return;
+ }
+
+ bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID , m->itemTextId );
+ bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender);
+
+ sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId);
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, bodyItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ m->itemTextId = 0;
+ m->state = MAIL_STATE_CHANGED;
+ pl->m_mailsUpdated = true;
+
+ pl->StoreItem(dest, bodyItem, true);
+ //bodyItem->SetState(ITEM_NEW, pl); is set automatically
+ pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, 0);
+ }
+ else
+ {
+ pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_BAG_FULL, msg);
+ delete bodyItem;
+ }
+}
+
+//TODO Fix me! ... this void has probably bad condition, but good data are sent
+void WorldSession::HandleMsgQueryNextMailtime(WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
+
+ if(!_player->m_mailsLoaded)
+ _player->_LoadMail();
+
+ if( _player->unReadMails > 0 )
+ {
+ data << (uint32) 0; // float
+ data << (uint32) 0; // count
+ uint32 count = 0;
+ 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;
+
+ if(count > 2)
+ {
+ count = 2;
+ break;
+ }
+
+ 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
+ }
+ }
+ data.put<uint32>(4, count);
+ }
+ else
+ {
+ data << (uint32) 0xC7A8C000;
+ data << (uint32) 0x00000000;
+ }
+ SendPacket(&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)
+{
+ uint32 mailId = objmgr.GenerateMailID();
+
+ time_t deliver_time = time(NULL) + deliver_delay;
+
+ //expire time if COD 3 days, if no COD 30 days, if auction sale pending 1 hour
+ uint32 expire_delay;
+ if(messageType == MAIL_AUCTION && !mi && !money) // auction mail without any items and money
+ expire_delay = HOUR;
+ else
+ expire_delay = (COD > 0) ? 3*DAY : 30*DAY;
+
+ time_t expire_time = deliver_time + expire_delay;
+
+ if(mailTemplateId && !sMailTemplateStore.LookupEntry(mailTemplateId))
+ {
+ sLog.outError( "WorldSession::SendMailTo - Mail have not existed MailTemplateId (%u), remove at send", mailTemplateId);
+ mailTemplateId = 0;
+ }
+
+ if(receiver)
+ {
+ receiver->AddNewMailDeliverTime(deliver_time);
+
+ if ( receiver->IsMailsLoaded() )
+ {
+ Mail * m = new Mail;
+ m->messageID = mailId;
+ m->messageType = messageType;
+ m->stationery = stationery;
+ m->mailTemplateId = mailTemplateId;
+ m->sender = sender_guidlow_or_entry;
+ m->receiver = receiver->GetGUIDLow();
+ m->subject = subject;
+ m->itemTextId = itemTextId;
+
+ if(mi)
+ m->AddAllItems(*mi);
+
+ m->expire_time = expire_time;
+ m->deliver_time = deliver_time;
+ m->money = money;
+ m->COD = COD;
+ m->checked = checked;
+ m->state = MAIL_STATE_UNCHANGED;
+
+ receiver->AddMail(m); //to insert new mail to beginning of maillist
+
+ if(mi)
+ {
+ for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+ if(mailItem.item)
+ receiver->AddMItem(mailItem.item);
+ }
+ }
+ }
+ else if(mi)
+ mi->deleteIncludedItems();
+ }
+ else if(mi)
+ mi->deleteIncludedItems();
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.escape_string(subject);
+ CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" I64FMTD "','" I64FMTD "', '%u', '%u', '%d')",
+ mailId, messageType, stationery, mailTemplateId, sender_guidlow_or_entry, receiver_guidlow, subject.c_str(), itemTextId, (mi && !mi->empty() ? 1 : 0), (uint64)expire_time, (uint64)deliver_time, money, COD, checked);
+
+ if(mi)
+ {
+ for(MailItemMap::const_iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
+ {
+ MailItem const& mailItem = mailItemIter->second;
+ CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, mailItem.item_guidlow, mailItem.item_template,receiver_guidlow);
+ }
+ }
+ CharacterDatabase.CommitTransaction();
+}
diff --git a/src/game/Mail.h b/src/game/Mail.h
new file mode 100644
index 00000000000..bec54eb41f7
--- /dev/null
+++ b/src/game/Mail.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef MANGOS_MAIL_H
+#define MANGOS_MAIL_H
+
+#include "Common.h"
+#include <map>
+
+class Item;
+
+#define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889
+#define MAX_MAIL_ITEMS 12
+
+enum MAIL_RESPONSE
+{
+ MAIL_OK = 0,
+ MAIL_MONEY_TAKEN = 1,
+ MAIL_ITEM_TAKEN = 2,
+ MAIL_RETURNED_TO_SENDER = 3,
+ MAIL_DELETED = 4,
+ MAIL_MADE_PERMANENT = 5
+};
+
+enum MAIL_ERRORS
+{
+ MAIL_ERR_BAG_FULL = 1,
+ MAIL_ERR_CANNOT_SEND_TO_SELF = 2,
+ MAIL_ERR_NOT_ENOUGH_MONEY = 3,
+ MAIL_ERR_RECIPIENT_NOT_FOUND = 4,
+ MAIL_ERR_NOT_YOUR_TEAM = 5,
+ MAIL_ERR_INTERNAL_ERROR = 6,
+ MAIL_ERR_DISABLED_FOR_TRIAL_ACC = 14,
+ MAIL_ERR_RECIPIENT_CAP_REACHED = 15,
+ MAIL_ERR_CANT_SEND_WRAPPED_COD = 16,
+ MAIL_ERR_MAIL_AND_CHAT_SUSPENDED = 17
+};
+
+enum MailCheckMask
+{
+ MAIL_CHECK_MASK_NONE = 0,
+ MAIL_CHECK_MASK_READ = 1,
+ MAIL_CHECK_MASK_AUCTION = 4,
+ MAIL_CHECK_MASK_COD_PAYMENT = 8,
+ MAIL_CHECK_MASK_RETURNED = 16
+};
+
+enum MailMessageType
+{
+ MAIL_NORMAL = 0,
+ MAIL_AUCTION = 2,
+ MAIL_CREATURE = 3, // client send CMSG_CREATURE_QUERY on this mailmessagetype
+ MAIL_GAMEOBJECT = 4, // client send CMSG_GAMEOBJECT_QUERY on this mailmessagetype
+ MAIL_ITEM = 5, // client send CMSG_ITEM_QUERY on this mailmessagetype
+};
+
+enum MailState
+{
+ MAIL_STATE_UNCHANGED = 1,
+ MAIL_STATE_CHANGED = 2,
+ MAIL_STATE_DELETED = 3
+};
+
+enum MailAuctionAnswers
+{
+ AUCTION_OUTBIDDED = 0,
+ AUCTION_WON = 1,
+ AUCTION_SUCCESSFUL = 2,
+ AUCTION_EXPIRED = 3,
+ AUCTION_CANCELLED_TO_BIDDER = 4,
+ AUCTION_CANCELED = 5,
+ AUCTION_SALE_PENDING = 6
+};
+
+// gathered from Stationery.dbc
+enum MailStationery
+{
+ MAIL_STATIONERY_UNKNOWN = 0x01,
+ MAIL_STATIONERY_NORMAL = 0x29,
+ MAIL_STATIONERY_GM = 0x3D,
+ MAIL_STATIONERY_AUCTION = 0x3E,
+ MAIL_STATIONERY_VAL = 0x40,
+ MAIL_STATIONERY_CHR = 0x41
+};
+
+struct MailItemInfo
+{
+ uint32 item_guid;
+ uint32 item_template;
+};
+
+struct MailItem
+{
+ MailItem() : item_slot(0), item_guidlow(0), item_template(0), item(NULL) {}
+
+ uint8 item_slot; // slot in mail
+ uint32 item_guidlow; // item guid (low part)
+ uint32 item_template; // item entry
+ Item *item; // item pointer
+
+ void deleteItem(bool inDB = false);
+};
+
+typedef std::map<uint32, MailItem> MailItemMap;
+
+class MailItemsInfo
+{
+ public:
+ MailItemMap::const_iterator begin() const { return i_MailItemMap.begin(); }
+ MailItemMap::const_iterator end() const { return i_MailItemMap.end(); }
+ MailItemMap::iterator begin() { return i_MailItemMap.begin(); }
+ MailItemMap::iterator end() { return i_MailItemMap.end(); }
+
+ void AddItem(uint32 guidlow, uint32 _template, Item *item, uint8 slot = 0)
+ {
+ MailItem mailItem;
+ mailItem.item_slot = slot;
+ mailItem.item_guidlow = guidlow;
+ mailItem.item_template = _template;
+ mailItem.item = item;
+ i_MailItemMap[guidlow] = mailItem;
+ }
+
+ void AddItem(uint32 guidlow, uint8 slot = 0)
+ {
+ MailItem mailItem;
+ mailItem.item_guidlow = guidlow;
+ mailItem.item_slot = slot;
+ i_MailItemMap[guidlow] = mailItem;
+ }
+
+ uint8 size() const { return i_MailItemMap.size(); }
+ bool empty() const { return i_MailItemMap.empty(); }
+
+ void deleteIncludedItems(bool inDB = false)
+ {
+ for(MailItemMap::iterator mailItemIter = begin(); mailItemIter != end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+ mailItem.deleteItem(inDB);
+ }
+ }
+ private:
+ MailItemMap i_MailItemMap; // Keep the items in a map to avoid duplicate guids (which can happen), store only low part of guid
+};
+
+struct Mail
+{
+ uint32 messageID;
+ uint8 messageType;
+ uint8 stationery;
+ uint16 mailTemplateId;
+ uint32 sender;
+ uint32 receiver;
+ std::string subject;
+ uint32 itemTextId;
+ std::vector<MailItemInfo> items;
+ std::vector<uint32> removedItems;
+ time_t expire_time;
+ time_t deliver_time;
+ uint32 money;
+ uint32 COD;
+ uint32 checked;
+ MailState state;
+
+ void AddItem(uint32 itemGuidLow, uint32 item_template)
+ {
+ MailItemInfo mii;
+ mii.item_guid = itemGuidLow;
+ mii.item_template = item_template;
+ items.push_back(mii);
+ }
+
+ void AddAllItems(MailItemsInfo& pMailItemsInfo)
+ {
+ for(MailItemMap::iterator mailItemIter = pMailItemsInfo.begin(); mailItemIter != pMailItemsInfo.end(); ++mailItemIter)
+ {
+ MailItem& mailItem = mailItemIter->second;
+ AddItem(mailItem.item_guidlow, mailItem.item_template);
+ }
+ }
+
+ bool RemoveItem(uint32 itemId)
+ {
+ for(std::vector<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr)
+ {
+ if(itr->item_guid == itemId)
+ {
+ items.erase(itr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool HasItems() const { return !items.empty(); }
+};
+#endif
diff --git a/src/game/Makefile.am b/src/game/Makefile.am
new file mode 100644
index 00000000000..cffd1f7b093
--- /dev/null
+++ b/src/game/Makefile.am
@@ -0,0 +1,278 @@
+# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+#
+# 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
+
+## Process this file with automake to produce Makefile.in
+
+## Sub-directories to parse
+
+## CPP flags for includes, defines, etc.
+AM_CPPFLAGS =
+
+## Build MaNGOS game library as convenience library.
+# All libraries will be convenience libraries. Might be changed to shared
+# later.
+noinst_LIBRARIES = libgame.a
+
+libgame_a_CPPFLAGS = \
+$(MYSQL_INCLUDES) \
+$(POSTGRE_INCLUDES) \
+-I$(top_srcdir)/dep/include \
+-I$(top_srcdir)/src/framework \
+-I$(top_srcdir)/src/shared \
+-I$(top_srcdir)/src/shared/vmap
+
+# libmangossgame library will later be reused by ...
+libgame_a_SOURCES = \
+$(srcdir)/AccountMgr.cpp \
+$(srcdir)/AccountMgr.h \
+$(srcdir)/AddonHandler.cpp \
+$(srcdir)/AddonHandler.h \
+$(srcdir)/AggressorAI.cpp \
+$(srcdir)/AggressorAI.h \
+$(srcdir)/AnimalRandomMovementGenerator.h \
+$(srcdir)/ArenaTeam.cpp \
+$(srcdir)/ArenaTeam.h \
+$(srcdir)/ArenaTeamHandler.cpp \
+$(srcdir)/AuctionHouse.cpp \
+$(srcdir)/AuctionHouseObject.h \
+$(srcdir)/Bag.cpp \
+$(srcdir)/Bag.h \
+$(srcdir)/BattleGround.cpp \
+$(srcdir)/BattleGroundAA.cpp \
+$(srcdir)/BattleGroundAB.cpp \
+$(srcdir)/BattleGroundAV.cpp \
+$(srcdir)/BattleGroundBE.cpp \
+$(srcdir)/BattleGroundEY.cpp \
+$(srcdir)/BattleGroundNA.cpp \
+$(srcdir)/BattleGroundRL.cpp \
+$(srcdir)/BattleGroundWS.cpp \
+$(srcdir)/BattleGround.h \
+$(srcdir)/BattleGroundAA.h \
+$(srcdir)/BattleGroundAB.h \
+$(srcdir)/BattleGroundAV.h \
+$(srcdir)/BattleGroundBE.h \
+$(srcdir)/BattleGroundEY.h \
+$(srcdir)/BattleGroundNA.h \
+$(srcdir)/BattleGroundRL.h \
+$(srcdir)/BattleGroundWS.h \
+$(srcdir)/BattleGroundHandler.cpp \
+$(srcdir)/BattleGroundMgr.cpp \
+$(srcdir)/BattleGroundMgr.h \
+$(srcdir)/Cell.h \
+$(srcdir)/CellImpl.h \
+$(srcdir)/Channel.cpp \
+$(srcdir)/Channel.h \
+$(srcdir)/ChannelHandler.cpp \
+$(srcdir)/ChannelMgr.h \
+$(srcdir)/CharacterHandler.cpp \
+$(srcdir)/Chat.cpp \
+$(srcdir)/Chat.h \
+$(srcdir)/ChatHandler.cpp \
+$(srcdir)/CombatHandler.cpp \
+$(srcdir)/ConfusedMovementGenerator.cpp \
+$(srcdir)/ConfusedMovementGenerator.h \
+$(srcdir)/Corpse.cpp \
+$(srcdir)/Corpse.h \
+$(srcdir)/CreatureAI.cpp \
+$(srcdir)/CreatureAI.h \
+$(srcdir)/CreatureAIImpl.h \
+$(srcdir)/CreatureAIRegistry.cpp \
+$(srcdir)/CreatureAIRegistry.h \
+$(srcdir)/CreatureAISelector.cpp \
+$(srcdir)/CreatureAISelector.h \
+$(srcdir)/Creature.cpp \
+$(srcdir)/Creature.h \
+$(srcdir)/debugcmds.cpp \
+$(srcdir)/DestinationHolder.cpp \
+$(srcdir)/DestinationHolder.h \
+$(srcdir)/DestinationHolderImp.h \
+$(srcdir)/DuelHandler.cpp \
+$(srcdir)/DynamicObject.cpp \
+$(srcdir)/DynamicObject.h \
+$(srcdir)/FleeingMovementGenerator.cpp \
+$(srcdir)/FleeingMovementGenerator.h \
+$(srcdir)/Formulas.h \
+$(srcdir)/GameEvent.cpp \
+$(srcdir)/GameEvent.h \
+$(srcdir)/GameObject.cpp \
+$(srcdir)/GameObject.h \
+$(srcdir)/GlobalEvents.cpp \
+$(srcdir)/GlobalEvents.h \
+$(srcdir)/GossipDef.cpp \
+$(srcdir)/GossipDef.h \
+$(srcdir)/GridDefines.h \
+$(srcdir)/GridNotifiers.cpp \
+$(srcdir)/GridNotifiers.h \
+$(srcdir)/GridNotifiersImpl.h \
+$(srcdir)/GridStates.cpp \
+$(srcdir)/GridStates.h \
+$(srcdir)/Group.cpp \
+$(srcdir)/Group.h \
+$(srcdir)/GroupHandler.cpp \
+$(srcdir)/GuardAI.cpp \
+$(srcdir)/GuardAI.h \
+$(srcdir)/Guild.cpp \
+$(srcdir)/Guild.h \
+$(srcdir)/GuildHandler.cpp \
+$(srcdir)/HateMatrix.h \
+$(srcdir)/HomeMovementGenerator.cpp \
+$(srcdir)/HomeMovementGenerator.h \
+$(srcdir)/HostilRefManager.cpp \
+$(srcdir)/HostilRefManager.h \
+$(srcdir)/IdleMovementGenerator.cpp \
+$(srcdir)/IdleMovementGenerator.h \
+$(srcdir)/InstanceData.cpp \
+$(srcdir)/InstanceData.h \
+$(srcdir)/InstanceSaveMgr.cpp \
+$(srcdir)/InstanceSaveMgr.h \
+$(srcdir)/Item.cpp \
+$(srcdir)/Item.h \
+$(srcdir)/ItemEnchantmentMgr.cpp \
+$(srcdir)/ItemEnchantmentMgr.h \
+$(srcdir)/ItemHandler.cpp \
+$(srcdir)/ItemPrototype.h \
+$(srcdir)/Language.h \
+$(srcdir)/Level0.cpp \
+$(srcdir)/Level1.cpp \
+$(srcdir)/Level2.cpp \
+$(srcdir)/Level3.cpp \
+$(srcdir)/LFGHandler.cpp \
+$(srcdir)/LootHandler.cpp \
+$(srcdir)/LootMgr.cpp \
+$(srcdir)/LootMgr.h \
+$(srcdir)/Mail.cpp \
+$(srcdir)/Mail.h \
+$(srcdir)/Map.cpp \
+$(srcdir)/Map.h \
+$(srcdir)/MapInstanced.cpp \
+$(srcdir)/MapInstanced.h \
+$(srcdir)/MapManager.cpp \
+$(srcdir)/MapManager.h \
+$(srcdir)/MiscHandler.cpp \
+$(srcdir)/MotionMaster.cpp \
+$(srcdir)/MotionMaster.h \
+$(srcdir)/MovementGenerator.cpp \
+$(srcdir)/MovementGenerator.h \
+$(srcdir)/MovementGeneratorImpl.h \
+$(srcdir)/MovementHandler.cpp \
+$(srcdir)/NPCHandler.cpp \
+$(srcdir)/NPCHandler.h \
+$(srcdir)/NullCreatureAI.cpp \
+$(srcdir)/NullCreatureAI.h \
+$(srcdir)/ObjectAccessor.cpp \
+$(srcdir)/ObjectAccessor.h \
+$(srcdir)/Object.cpp \
+$(srcdir)/ObjectDefines.h \
+$(srcdir)/ObjectGridLoader.cpp \
+$(srcdir)/ObjectGridLoader.h \
+$(srcdir)/Object.h \
+$(srcdir)/ObjectMgr.cpp \
+$(srcdir)/ObjectMgr.h \
+$(srcdir)/ObjectPosSelector.cpp \
+$(srcdir)/ObjectPosSelector.h \
+$(srcdir)/Opcodes.cpp \
+$(srcdir)/Opcodes.h \
+$(srcdir)/Path.h \
+$(srcdir)/PetAI.cpp \
+$(srcdir)/PetAI.h \
+$(srcdir)/Pet.cpp \
+$(srcdir)/Pet.h \
+$(srcdir)/PetHandler.cpp \
+$(srcdir)/PetitionsHandler.cpp \
+$(srcdir)/Player.cpp \
+$(srcdir)/Player.h \
+$(srcdir)/PlayerDump.cpp \
+$(srcdir)/PlayerDump.h \
+$(srcdir)/PointMovementGenerator.cpp \
+$(srcdir)/PointMovementGenerator.h \
+$(srcdir)/QueryHandler.cpp \
+$(srcdir)/QuestDef.cpp \
+$(srcdir)/QuestDef.h \
+$(srcdir)/QuestHandler.cpp \
+$(srcdir)/RandomMovementGenerator.cpp \
+$(srcdir)/RandomMovementGenerator.h \
+$(srcdir)/ReactorAI.cpp \
+$(srcdir)/ReactorAI.h \
+$(srcdir)/ScriptCalls.cpp \
+$(srcdir)/ScriptCalls.h \
+$(srcdir)/SharedDefines.h \
+$(srcdir)/SkillHandler.cpp \
+$(srcdir)/SpellAuraDefines.h \
+$(srcdir)/SpellAuras.cpp \
+$(srcdir)/SpellAuras.h \
+$(srcdir)/Spell.cpp \
+$(srcdir)/SpellEffects.cpp \
+$(srcdir)/Spell.h \
+$(srcdir)/SkillDiscovery.cpp \
+$(srcdir)/SkillDiscovery.h \
+$(srcdir)/SkillExtraItems.cpp \
+$(srcdir)/SkillExtraItems.h \
+$(srcdir)/SpellHandler.cpp \
+$(srcdir)/SocialMgr.cpp \
+$(srcdir)/SocialMgr.h \
+$(srcdir)/SpellMgr.cpp \
+$(srcdir)/SpellMgr.h \
+$(srcdir)/StatSystem.cpp \
+$(srcdir)/TargetedMovementGenerator.cpp \
+$(srcdir)/TargetedMovementGenerator.h \
+$(srcdir)/TaxiHandler.cpp \
+$(srcdir)/TemporarySummon.cpp \
+$(srcdir)/TemporarySummon.h \
+$(srcdir)/tools.cpp \
+$(srcdir)/Tools.h \
+$(srcdir)/TotemAI.cpp \
+$(srcdir)/TotemAI.h \
+$(srcdir)/Totem.cpp \
+$(srcdir)/Totem.h \
+$(srcdir)/TradeHandler.cpp \
+$(srcdir)/Transports.cpp \
+$(srcdir)/Transports.h \
+$(srcdir)/ThreatManager.cpp \
+$(srcdir)/ThreatManager.h \
+$(srcdir)/Traveller.h \
+$(srcdir)/Unit.cpp \
+$(srcdir)/Unit.h \
+$(srcdir)/UnitEvents.h \
+$(srcdir)/UpdateData.cpp \
+$(srcdir)/UpdateData.h \
+$(srcdir)/UpdateFields.h \
+$(srcdir)/UpdateMask.h \
+$(srcdir)/VoiceChatHandler.cpp \
+$(srcdir)/WaypointManager.cpp \
+$(srcdir)/WaypointManager.h \
+$(srcdir)/WaypointMovementGenerator.cpp \
+$(srcdir)/WaypointMovementGenerator.h \
+$(srcdir)/Weather.cpp \
+$(srcdir)/Weather.h \
+$(srcdir)/World.cpp \
+$(srcdir)/World.h \
+$(srcdir)/WorldLog.cpp \
+$(srcdir)/WorldLog.h \
+$(srcdir)/WorldSession.cpp \
+$(srcdir)/WorldSession.h \
+$(srcdir)/WorldSocket.cpp \
+$(srcdir)/WorldSocket.h \
+$(srcdir)/WorldSocketMgr.cpp \
+$(srcdir)/WorldSocketMgr.h \
+$(srcdir)/FollowerReference.cpp \
+$(srcdir)/FollowerReference.h \
+$(srcdir)/FollowerRefManager.h \
+$(srcdir)/GroupReference.cpp \
+$(srcdir)/GroupReference.h \
+$(srcdir)/GroupRefManager.h
+
+## Additional files to include when running 'make dist'
+# Nothing yet.
diff --git a/src/game/Map.cpp b/src/game/Map.cpp
new file mode 100644
index 00000000000..df03bb96842
--- /dev/null
+++ b/src/game/Map.cpp
@@ -0,0 +1,1795 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Player.h"
+#include "GridNotifiers.h"
+#include "WorldSession.h"
+#include "Log.h"
+#include "GridStates.h"
+#include "CellImpl.h"
+#include "InstanceData.h"
+#include "Map.h"
+#include "GridNotifiersImpl.h"
+#include "Config/ConfigEnv.h"
+#include "Transports.h"
+#include "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include "ScriptCalls.h"
+#include "Group.h"
+
+#include "MapManager.h"
+#include "MapInstanced.h"
+#include "InstanceSaveMgr.h"
+#include "VMapFactory.h"
+
+#define DEFAULT_GRID_EXPIRY 300
+#define MAX_GRID_LOAD_TIME 50
+
+// magic *.map header
+const char MAP_MAGIC[] = "MAP_2.00";
+
+GridState* si_GridStates[MAX_GRID_STATE];
+
+Map::~Map()
+{
+ UnloadAll(true);
+}
+
+bool Map::ExistMap(uint32 mapid,int x,int y)
+{
+ int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
+ char* tmp = new char[len];
+ snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y);
+
+ FILE *pf=fopen(tmp,"rb");
+
+ if(!pf)
+ {
+ sLog.outError("Check existing of map file '%s': not exist!",tmp);
+ delete[] tmp;
+ return false;
+ }
+
+ char magic[8];
+ fread(magic,1,8,pf);
+ if(strncmp(MAP_MAGIC,magic,8))
+ {
+ sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
+ delete [] tmp;
+ fclose(pf); //close file before return
+ return false;
+ }
+
+ delete [] tmp;
+ fclose(pf);
+
+ return true;
+}
+
+bool Map::ExistVMap(uint32 mapid,int x,int y)
+{
+ if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
+ {
+ if(vmgr->isMapLoadingEnabled())
+ {
+ // x and y are swaped !! => fixed now
+ bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, x,y);
+ if(!exists)
+ {
+ std::string name = vmgr->getDirFileName(mapid,x,y);
+ sLog.outError("VMap file '%s' is missing or point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath()+"vmaps/"+name).c_str());
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void Map::LoadVMap(int x,int y)
+{
+ // x and y are swaped !!
+ int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), x,y);
+ switch(vmapLoadResult)
+ {
+ case VMAP::VMAP_LOAD_RESULT_OK:
+ sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
+ break;
+ case VMAP::VMAP_LOAD_RESULT_ERROR:
+ sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
+ break;
+ case VMAP::VMAP_LOAD_RESULT_IGNORED:
+ DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
+ break;
+ }
+}
+
+void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y)
+{
+ if( instanceid != 0 )
+ {
+ if(GridMaps[x][y])
+ return;
+
+ Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(mapid));
+
+ // load gridmap for base map
+ if (!baseMap->GridMaps[x][y])
+ baseMap->EnsureGridCreated(GridPair(63-x,63-y));
+
+//+++ if (!baseMap->GridMaps[x][y]) don't check for GridMaps[gx][gy], we need the management for vmaps
+// return;
+
+ ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y));
+ baseMap->SetUnloadFlag(GridPair(63-x,63-y), false);
+ GridMaps[x][y] = baseMap->GridMaps[x][y];
+ return;
+ }
+
+ //map already load, delete it before reloading (Is it neccessary? Do we really need the abilty the reload maps during runtime?)
+ if(GridMaps[x][y])
+ {
+ sLog.outDetail("Unloading already loaded map %u before reloading.",mapid);
+ delete (GridMaps[x][y]);
+ GridMaps[x][y]=NULL;
+ }
+
+ // map file name
+ char *tmp=NULL;
+ // Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 !
+ 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(),mapid,x,y);
+ sLog.outDetail("Loading map %s",tmp);
+ // loading data
+ FILE *pf=fopen(tmp,"rb");
+ if(!pf)
+ {
+ delete [] tmp;
+ return;
+ }
+
+ char magic[8];
+ fread(magic,1,8,pf);
+ if(strncmp(MAP_MAGIC,magic,8))
+ {
+ sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
+ delete [] tmp;
+ fclose(pf); //close file before return
+ return;
+ }
+ delete [] tmp;
+
+ GridMap * buf= new GridMap;
+ fread(buf,1,sizeof(GridMap),pf);
+ fclose(pf);
+
+ GridMaps[x][y] = buf;
+}
+
+void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y)
+{
+ LoadMap(mapid,instanceid,x,y);
+ if(instanceid == 0)
+ LoadVMap(x, y); // Only load the data for the base map
+}
+
+void Map::InitStateMachine()
+{
+ si_GridStates[GRID_STATE_INVALID] = new InvalidState;
+ si_GridStates[GRID_STATE_ACTIVE] = new ActiveState;
+ si_GridStates[GRID_STATE_IDLE] = new IdleState;
+ si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
+}
+
+void Map::DeleteStateMachine()
+{
+ delete si_GridStates[GRID_STATE_INVALID];
+ delete si_GridStates[GRID_STATE_ACTIVE];
+ delete si_GridStates[GRID_STATE_IDLE];
+ delete si_GridStates[GRID_STATE_REMOVAL];
+}
+
+Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
+ : i_id(id), i_gridExpiry(expiry), i_mapEntry (sMapStore.LookupEntry(id)),
+ i_InstanceId(InstanceId), i_spawnMode(SpawnMode), m_unloadTimer(0)
+{
+ for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
+ {
+ for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j)
+ {
+ //z code
+ GridMaps[idx][j] =NULL;
+ setNGrid(NULL, idx, j);
+ }
+ }
+}
+
+// Template specialization of utility methods
+template<class T>
+void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
+{
+ (*grid)(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj, obj->GetGUID());
+}
+
+template<>
+void Map::AddToGrid(Player* obj, NGridType *grid, Cell const& cell)
+{
+ (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
+}
+
+template<>
+void Map::AddToGrid(Corpse *obj, NGridType *grid, Cell const& cell)
+{
+ // add to world object registry in grid
+ if(obj->GetType()!=CORPSE_BONES)
+ {
+ (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
+ }
+ // add to grid object store
+ else
+ {
+ (*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID());
+ }
+}
+
+template<>
+void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
+{
+ // add to world object registry in grid
+ if(obj->isPet())
+ {
+ (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID());
+ obj->SetCurrentCell(cell);
+ }
+ // add to grid object store
+ else
+ {
+ (*grid)(cell.CellX(), cell.CellY()).AddGridObject<Creature>(obj, obj->GetGUID());
+ obj->SetCurrentCell(cell);
+ }
+}
+
+template<class T>
+void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
+{
+ (*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject<T>(obj, obj->GetGUID());
+}
+
+template<>
+void Map::RemoveFromGrid(Player* obj, NGridType *grid, Cell const& cell)
+{
+ (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
+}
+
+template<>
+void Map::RemoveFromGrid(Corpse *obj, NGridType *grid, Cell const& cell)
+{
+ // remove from world object registry in grid
+ if(obj->GetType()!=CORPSE_BONES)
+ {
+ (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
+ }
+ // remove from grid object store
+ else
+ {
+ (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID());
+ }
+}
+
+template<>
+void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell)
+{
+ // remove from world object registry in grid
+ if(obj->isPet())
+ {
+ (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID());
+ }
+ // remove from grid object store
+ else
+ {
+ (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject<Creature>(obj, obj->GetGUID());
+ }
+}
+
+template<class T>
+void Map::DeleteFromWorld(T* obj)
+{
+ // Note: In case resurrectable corpse and pet its removed from gloabal lists in own destructors
+ delete obj;
+}
+
+template<class T>
+void Map::AddNotifier(T* , Cell const& , CellPair const& )
+{
+}
+
+template<>
+void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair)
+{
+ PlayerRelocationNotify(obj,cell,cellpair);
+}
+
+template<>
+void Map::AddNotifier(Creature* obj, Cell const& cell, CellPair const& cellpair)
+{
+ CreatureRelocationNotify(obj,cell,cellpair);
+}
+
+void
+Map::EnsureGridCreated(const GridPair &p)
+{
+ if(!getNGrid(p.x_coord, p.y_coord))
+ {
+ Guard guard(*this);
+ if(!getNGrid(p.x_coord, p.y_coord))
+ {
+ 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);
+
+ // build a linkage between this map and NGridType
+ buildNGridLinkage(getNGrid(p.x_coord, p.y_coord));
+
+ getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE);
+
+ //z coord
+ int gx=63-p.x_coord;
+ int gy=63-p.y_coord;
+
+ if(!GridMaps[gx][gy])
+ Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy);
+ }
+ }
+}
+
+void
+Map::EnsureGridLoadedForPlayer(const Cell &cell, Player *player, bool add_player)
+{
+ EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
+ NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+
+ assert(grid != NULL);
+ if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
+ {
+ if( player != NULL )
+ {
+ 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);
+ }
+ else
+ {
+ DEBUG_LOG("Player nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id);
+ }
+
+ ObjectGridLoader loader(*grid, this, cell);
+ loader.LoadN();
+ setGridObjectDataLoaded(true, cell.GridX(), cell.GridY());
+
+ // Add resurrectable corpses to world object list in grid
+ ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
+
+ ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f);
+ grid->SetGridState(GRID_STATE_ACTIVE);
+
+ if( add_player && player != NULL )
+ (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(player, player->GetGUID());
+ }
+ else if( player && add_player )
+ AddToGrid(player,grid,cell);
+}
+
+void
+Map::LoadGrid(const Cell& cell, bool no_unload)
+{
+ EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
+ NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+
+ assert(grid != NULL);
+ if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
+ {
+ ObjectGridLoader loader(*grid, this, cell);
+ loader.LoadN();
+
+ // Add resurrectable corpses to world object list in grid
+ ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
+
+ setGridObjectDataLoaded(true,cell.GridX(), cell.GridY());
+ if(no_unload)
+ getNGrid(cell.GridX(), cell.GridY())->setUnloadFlag(false);
+ }
+ LoadVMap(63-cell.GridX(),63-cell.GridY());
+}
+
+bool Map::Add(Player *player)
+{
+ player->SetInstanceId(this->GetInstanceId());
+
+ // update player state for other player and visa-versa
+ CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+ Cell cell(p);
+ EnsureGridLoadedForPlayer(cell, player, true);
+ player->AddToWorld();
+
+ SendInitSelf(player);
+ SendInitTransports(player);
+
+ UpdatePlayerVisibility(player,cell,p);
+ UpdateObjectsVisibilityFor(player,cell,p);
+
+ AddNotifier(player,cell,p);
+ return true;
+}
+
+template<class T>
+void
+Map::Add(T *obj)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+
+ assert(obj);
+
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::Add: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
+ NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+ assert( grid != NULL );
+
+ AddToGrid(obj,grid,cell);
+ obj->AddToWorld();
+
+ DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY());
+
+ UpdateObjectVisibility(obj,cell,p);
+
+ AddNotifier(obj,cell,p);
+}
+
+void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self)
+{
+ CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
+ return;
+
+ MaNGOS::MessageDeliverer post_man(*player, msg, to_self);
+ TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > message(post_man);
+ CellLock<ReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *this);
+}
+
+void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
+ return;
+
+ MaNGOS::ObjectMessageDeliverer post_man(msg);
+ TypeContainerVisitor<MaNGOS::ObjectMessageDeliverer, WorldTypeMapContainer > message(post_man);
+ CellLock<ReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *this);
+}
+
+void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only)
+{
+ CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
+ return;
+
+ MaNGOS::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only);
+ TypeContainerVisitor<MaNGOS::MessageDistDeliverer , WorldTypeMapContainer > message(post_man);
+ CellLock<ReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *this);
+}
+
+void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
+ return;
+
+ MaNGOS::ObjectMessageDistDeliverer post_man(*obj, msg,dist);
+ TypeContainerVisitor<MaNGOS::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man);
+ CellLock<ReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *this);
+}
+
+bool Map::loaded(const GridPair &p) const
+{
+ return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) );
+}
+
+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(); )
+ {
+ 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);
+ }
+}
+
+void InstanceMap::Update(const uint32& t_diff)
+{
+ Map::Update(t_diff);
+
+ if(i_data)
+ i_data->Update(t_diff);
+}
+
+
+void Map::Remove(Player *player, bool remove)
+{
+ CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
+ {
+ // invalid coordinates
+ player->RemoveFromWorld();
+
+ if( remove )
+ DeleteFromWorld(player);
+
+ return;
+ }
+
+ Cell cell(p);
+
+ if( !getNGrid(cell.data.Part.grid_x, cell.data.Part.grid_y) )
+ {
+ sLog.outError("Map::Remove() i_grids was NULL x:%d, y:%d",cell.data.Part.grid_x,cell.data.Part.grid_y);
+ return;
+ }
+
+ DEBUG_LOG("Remove player %s from grid[%u,%u]", player->GetName(), cell.GridX(), cell.GridY());
+ NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+ assert(grid != NULL);
+
+ player->RemoveFromWorld();
+ RemoveFromGrid(player,grid,cell);
+
+ SendRemoveTransports(player);
+
+ UpdateObjectsVisibilityFor(player,cell,p);
+
+ if( remove )
+ DeleteFromWorld(player);
+}
+
+bool Map::RemoveBones(uint64 guid, float x, float y)
+{
+ if (IsRemovalGrid(x, y))
+ {
+ Corpse * corpse = ObjectAccessor::Instance().GetObjectInWorld(GetId(), x, y, guid, (Corpse*)NULL);
+ if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES)
+ corpse->DeleteBonesFromWorld();
+ else
+ return false;
+ }
+ return true;
+}
+
+template<class T>
+void
+Map::Remove(T *obj, bool remove)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("Map::Remove: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
+ return;
+ }
+
+ Cell cell(p);
+ if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
+ return;
+
+ DEBUG_LOG("Remove object " I64FMTD " from grid[%u,%u]", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y);
+ NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+ assert( grid != NULL );
+
+ obj->RemoveFromWorld();
+ RemoveFromGrid(obj,grid,cell);
+
+ UpdateObjectVisibility(obj,cell,p);
+
+ if( remove )
+ {
+ // if option set then object already saved at this moment
+ if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ obj->SaveRespawnTime();
+ DeleteFromWorld(obj);
+ }
+}
+
+void
+Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation)
+{
+ assert(player);
+
+ CellPair old_val = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+ CellPair new_val = MaNGOS::ComputeCellPair(x, y);
+
+ Cell old_cell(old_val);
+ Cell new_cell(new_val);
+ new_cell |= old_cell;
+ bool same_cell = (new_cell == old_cell);
+
+ player->Relocate(x, y, z, orientation);
+
+ if( old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell) )
+ {
+ DEBUG_LOG("Player %s relocation grid[%u,%u]cell[%u,%u]->grid[%u,%u]cell[%u,%u]", player->GetName(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+
+ // update player position for group at taxi flight
+ if(player->GetGroup() && player->isInFlight())
+ player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
+
+ NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY());
+ RemoveFromGrid(player, oldGrid,old_cell);
+ if( !old_cell.DiffGrid(new_cell) )
+ AddToGrid(player, oldGrid,new_cell);
+
+ if( old_cell.DiffGrid(new_cell) )
+ EnsureGridLoadedForPlayer(new_cell, player, true);
+ }
+
+ // if move then update what player see and who seen
+ UpdatePlayerVisibility(player,new_cell,new_val);
+ UpdateObjectsVisibilityFor(player,new_cell,new_val);
+ PlayerRelocationNotify(player,new_cell,new_val);
+ NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
+ if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
+ {
+ ResetGridExpiry(*newGrid, 0.1f);
+ newGrid->SetGridState(GRID_STATE_ACTIVE);
+ }
+}
+
+void
+Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang)
+{
+ assert(CheckGridIntegrity(creature,false));
+
+ Cell old_cell = creature->GetCurrentCell();
+
+ CellPair new_val = MaNGOS::ComputeCellPair(x, y);
+ Cell new_cell(new_val);
+
+ // delay creature move for grid/cell to grid/cell moves
+ if( old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell) )
+ {
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+ AddCreatureToMoveList(creature,x,y,z,ang);
+ // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
+ }
+ else
+ {
+ creature->Relocate(x, y, z, ang);
+ CreatureRelocationNotify(creature,new_cell,new_val);
+ }
+ assert(CheckGridIntegrity(creature,true));
+}
+
+void Map::AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang)
+{
+ if(!c)
+ return;
+
+ i_creaturesToMove[c] = CreatureMover(x,y,z,ang);
+}
+
+void Map::MoveAllCreaturesInMoveList()
+{
+ while(!i_creaturesToMove.empty())
+ {
+ // get data and remove element;
+ CreatureMoveList::iterator iter = i_creaturesToMove.begin();
+ Creature* c = iter->first;
+ CreatureMover cm = iter->second;
+ i_creaturesToMove.erase(iter);
+
+ // calculate cells
+ CellPair new_val = MaNGOS::ComputeCellPair(cm.x, cm.y);
+ Cell new_cell(new_val);
+
+ // do move or do move to respawn or remove creature if previous all fail
+ if(CreatureCellRelocation(c,new_cell))
+ {
+ // update pos
+ c->Relocate(cm.x, cm.y, cm.z, cm.ang);
+ CreatureRelocationNotify(c,new_cell,new_cell.cellPair());
+ }
+ else
+ {
+ // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
+ // creature coordinates will be updated and notifiers send
+ if(!CreatureRespawnRelocation(c))
+ {
+ // ... or unload (if respawn grid also not loaded)
+ #ifdef MANGOS_DEBUG
+ 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);
+ }
+ }
+ }
+}
+
+bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
+{
+ Cell const& old_cell = c->GetCurrentCell();
+ if(!old_cell.DiffGrid(new_cell) ) // in same grid
+ {
+ // if in same cell then none do
+ if(old_cell.DiffCell(new_cell))
+ {
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+
+ if( !old_cell.DiffGrid(new_cell) )
+ {
+ RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
+ AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
+ c->SetCurrentCell(new_cell);
+ }
+ }
+ else
+ {
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) move in same grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
+ #endif
+ }
+ }
+ else // in diff. grids
+ if(loaded(GridPair(new_cell.GridX(), new_cell.GridY())))
+ {
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+
+ RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
+ {
+ EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY()));
+ AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
+ }
+ }
+ else
+ {
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+ return false;
+ }
+
+ return true;
+}
+
+bool Map::CreatureRespawnRelocation(Creature *c)
+{
+ float resp_x, resp_y, resp_z, resp_o;
+ c->GetRespawnCoord(resp_x, resp_y, resp_z, &resp_o);
+
+ CellPair resp_val = MaNGOS::ComputeCellPair(resp_x, resp_y);
+ Cell resp_cell(resp_val);
+
+ c->CombatStop();
+ c->GetMotionMaster()->Clear();
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
+ sLog.outDebug("Creature (GUID: %u Entry: %u) will moved from grid[%u,%u]cell[%u,%u] to respawn grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY());
+ #endif
+
+ // teleport it to respawn point (like normal respawn if player see)
+ if(CreatureCellRelocation(c,resp_cell))
+ {
+ c->Relocate(resp_x, resp_y, resp_z, resp_o);
+ c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators
+ CreatureRelocationNotify(c,resp_cell,resp_cell.cellPair());
+ return true;
+ }
+ else
+ return false;
+}
+
+bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool pForce)
+{
+ NGridType *grid = getNGrid(x, y);
+ assert( grid != NULL);
+
+ {
+ if(!pForce && ObjectAccessor::Instance().PlayersNearGrid(x, y, i_id, i_InstanceId) )
+ return false;
+
+ DEBUG_LOG("Unloading grid[%u,%u] for map %u", x,y, i_id);
+ ObjectGridUnloader unloader(*grid);
+
+ // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
+ // Must know real mob position before move
+ DoDelayedMovesAndRemoves();
+
+ // move creatures to respawn grids if this is diff.grid or to remove list
+ unloader.MoveToRespawnN();
+
+ // Finish creature moves, remove and delete all creatures with delayed remove before unload
+ DoDelayedMovesAndRemoves();
+
+ unloader.UnloadN();
+ delete getNGrid(x, y);
+ setNGrid(NULL, x, y);
+ }
+ int gx=63-x;
+ int gy=63-y;
+
+ // delete grid map, but don't delete if it is from parent map (and thus only reference)
+ //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps
+ {
+ if (i_InstanceId == 0)
+ {
+ if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]);
+ // x and y are swaped
+ VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx);
+ }
+ else
+ ((MapInstanced*)(MapManager::Instance().GetBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy));
+ GridMaps[gx][gy] = NULL;
+ }
+ DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id);
+ return true;
+}
+
+void Map::UnloadAll(bool pForce)
+{
+ // clear all delayed moves, useless anyway do this moves before map unload.
+ i_creaturesToMove.clear();
+
+ for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
+ {
+ NGridType &grid(*i->getSource());
+ ++i;
+ UnloadGrid(grid.getX(), grid.getY(), pForce); // deletes the grid and removes it from the GridRefManager
+ }
+}
+
+float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
+{
+ GridPair p = MaNGOS::ComputeGridPair(x, y);
+
+ // half opt method
+ int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x
+ int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
+
+ float lx=MAP_RESOLUTION*(32 -x/SIZE_OF_GRIDS - gx);
+ float ly=MAP_RESOLUTION*(32 -y/SIZE_OF_GRIDS - gy);
+
+ // ensure GridMap is loaded
+ const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
+
+ // find raw .map surface under Z coordinates
+ float mapHeight;
+ if(GridMap* gmap = GridMaps[gx][gy])
+ {
+ int lx_int = (int)lx;
+ int ly_int = (int)ly;
+
+ float zi[4];
+ // Probe 4 nearest points (except border cases)
+ zi[0] = gmap->Z[lx_int][ly_int];
+ zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0];
+ zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0];
+ zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0];
+ // Recalculate them like if their x,y positions were in the range 0,1
+ float b[4];
+ b[0] = zi[0];
+ b[1] = zi[1]-zi[0];
+ b[2] = zi[2]-zi[0];
+ b[3] = zi[0]-zi[1]-zi[2]+zi[3];
+ // Normalize the dx and dy to be in range 0..1
+ float fact_x = lx - lx_int;
+ float fact_y = ly - ly_int;
+ // Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url]
+ float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y);
+
+ // look from a bit higher pos to find the floor, ignore under surface case
+ if(z + 2.0f > _mapheight)
+ mapHeight = _mapheight;
+ else
+ mapHeight = VMAP_INVALID_HEIGHT_VALUE;
+ }
+ else
+ mapHeight = VMAP_INVALID_HEIGHT_VALUE;
+
+ float vmapHeight;
+ if(pUseVmaps)
+ {
+ VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
+ if(vmgr->isHeightCalcEnabled())
+ {
+ // look from a bit higher pos to find the floor
+ vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f);
+ }
+ else
+ vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
+ }
+ else
+ vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
+
+ // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
+ // vmapheight set for any under Z value or <= INVALID_HEIGHT
+
+ if( vmapHeight > INVALID_HEIGHT )
+ {
+ if( mapHeight > INVALID_HEIGHT )
+ {
+ // we have mapheight and vmapheight and must select more appropriate
+
+ // we are already under the surface or vmap height above map heigt
+ // or if the distance of the vmap height is less the land height distance
+ if( z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z) )
+ return vmapHeight;
+ else
+ return mapHeight; // better use .map surface height
+
+ }
+ else
+ return vmapHeight; // we have only vmapHeight (if have)
+ }
+ else
+ {
+ if(!pUseVmaps)
+ return mapHeight; // explicitly use map data (if have)
+ else if(mapHeight > INVALID_HEIGHT && (z < mapHeight + 2 || z == MAX_HEIGHT))
+ return mapHeight; // explicitly use map data if original z < mapHeight but map found (z+2 > mapHeight)
+ else
+ return VMAP_INVALID_HEIGHT_VALUE; // we not have any height
+ }
+}
+
+uint16 Map::GetAreaFlag(float x, float y ) const
+{
+ //local x,y coords
+ float lx,ly;
+ int gx,gy;
+ GridPair p = MaNGOS::ComputeGridPair(x, y);
+
+ // half opt method
+ gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
+ gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
+
+ lx=16*(32 -x/SIZE_OF_GRIDS - gx);
+ ly=16*(32 -y/SIZE_OF_GRIDS - gy);
+ //DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord);
+
+ // ensure GridMap is loaded
+ const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
+
+ if(GridMaps[gx][gy])
+ return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)];
+ // this used while not all *.map files generated (instances)
+ else
+ return GetAreaFlagByMapId(i_id);
+}
+
+uint8 Map::GetTerrainType(float x, float y ) const
+{
+ //local x,y coords
+ float lx,ly;
+ int gx,gy;
+
+ // half opt method
+ gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
+ gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
+
+ lx=16*(32 -x/SIZE_OF_GRIDS - gx);
+ ly=16*(32 -y/SIZE_OF_GRIDS - gy);
+
+ // ensure GridMap is loaded
+ const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
+
+ if(GridMaps[gx][gy])
+ return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)];
+ else
+ return 0;
+
+}
+
+float Map::GetWaterLevel(float x, float y ) const
+{
+ //local x,y coords
+ float lx,ly;
+ int gx,gy;
+
+ // half opt method
+ gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
+ gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
+
+ lx=128*(32 -x/SIZE_OF_GRIDS - gx);
+ ly=128*(32 -y/SIZE_OF_GRIDS - gy);
+
+ // ensure GridMap is loaded
+ const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
+
+ if(GridMaps[gx][gy])
+ return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)];
+ else
+ return 0;
+}
+
+uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id)
+{
+ AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
+
+ if (entry)
+ return entry->ID;
+ else
+ return 0;
+}
+
+uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id)
+{
+ AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
+
+ if( entry )
+ return ( entry->zone != 0 ) ? entry->zone : entry->ID;
+ else
+ return 0;
+}
+
+bool Map::IsInWater(float x, float y, float pZ) const
+{
+ // This method is called too often to use vamps for that (4. parameter = false).
+ // The pZ pos is taken anyway for future use
+ float z = GetHeight(x,y,pZ,false); // use .map base surface height
+
+ // underground or instance without vmap
+ if(z <= INVALID_HEIGHT)
+ return false;
+
+ float water_z = GetWaterLevel(x,y);
+ uint8 flag = GetTerrainType(x,y);
+ return (z < (water_z-2)) && (flag & 0x01);
+}
+
+bool Map::IsUnderWater(float x, float y, float z) const
+{
+ float water_z = GetWaterLevel(x,y);
+ uint8 flag = GetTerrainType(x,y);
+ return (z < (water_z-2)) && (flag & 0x01);
+}
+
+bool Map::CheckGridIntegrity(Creature* c, bool moved) const
+{
+ Cell const& cur_cell = c->GetCurrentCell();
+
+ CellPair xy_val = MaNGOS::ComputeCellPair(c->GetPositionX(), c->GetPositionY());
+ Cell xy_cell(xy_val);
+ if(xy_cell != cur_cell)
+ {
+ sLog.outError("ERROR: %s (GUID: %u) X: %f Y: %f (%s) in grid[%u,%u]cell[%u,%u] instead grid[%u,%u]cell[%u,%u]",
+ (c->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature"),c->GetGUIDLow(),
+ c->GetPositionX(),c->GetPositionY(),(moved ? "final" : "original"),
+ cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(),
+ xy_cell.GridX(), xy_cell.GridY(), xy_cell.CellX(), xy_cell.CellY());
+ return true; // not crash at error, just output error in debug mode
+ }
+
+ return true;
+}
+
+const char* Map::GetMapName() const
+{
+ return i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
+}
+
+void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair)
+{
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+ MaNGOS::VisibleChangesNotifier notifier(*obj);
+ TypeContainerVisitor<MaNGOS::VisibleChangesNotifier, WorldTypeMapContainer > player_notifier(notifier);
+ CellLock<GridReadGuard> cell_lock(cell, cellpair);
+ cell_lock->Visit(cell_lock, player_notifier, *this);
+}
+
+void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair )
+{
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::PlayerNotifier pl_notifier(*player);
+ TypeContainerVisitor<MaNGOS::PlayerNotifier, WorldTypeMapContainer > player_notifier(pl_notifier);
+
+ CellLock<ReadGuard> cell_lock(cell, cellpair);
+ cell_lock->Visit(cell_lock, player_notifier, *this);
+}
+
+void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
+{
+ MaNGOS::VisibleNotifier notifier(*player);
+
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+ TypeContainerVisitor<MaNGOS::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
+ CellLock<GridReadGuard> cell_lock(cell, cellpair);
+ cell_lock->Visit(cell_lock, world_notifier, *this);
+ cell_lock->Visit(cell_lock, grid_notifier, *this);
+
+ // send data
+ notifier.Notify();
+}
+
+void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair )
+{
+ CellLock<ReadGuard> cell_lock(cell, cellpair);
+ MaNGOS::PlayerRelocationNotifier relocationNotifier(*player);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, GridTypeMapContainer > p2grid_relocation(relocationNotifier);
+ TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, WorldTypeMapContainer > p2world_relocation(relocationNotifier);
+
+ cell_lock->Visit(cell_lock, p2grid_relocation, *this);
+ cell_lock->Visit(cell_lock, p2world_relocation, *this);
+}
+
+void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair)
+{
+ CellLock<ReadGuard> cell_lock(cell, cellpair);
+ MaNGOS::CreatureRelocationNotifier relocationNotifier(*creature);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate(); // not trigger load unloaded grids at notifier call
+
+ TypeContainerVisitor<MaNGOS::CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocationNotifier);
+ TypeContainerVisitor<MaNGOS::CreatureRelocationNotifier, GridTypeMapContainer > c2grid_relocation(relocationNotifier);
+
+ cell_lock->Visit(cell_lock, c2world_relocation, *this);
+ cell_lock->Visit(cell_lock, c2grid_relocation, *this);
+}
+
+void Map::SendInitSelf( Player * player )
+{
+ sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());
+
+ UpdateData data;
+
+ bool hasTransport = false;
+
+ // attach to player data current transport data
+ if(Transport* transport = player->GetTransport())
+ {
+ hasTransport = true;
+ transport->BuildCreateUpdateBlockForPlayer(&data, player);
+ }
+
+ // build data for self presence in world at own client (one time for map)
+ player->BuildCreateUpdateBlockForPlayer(&data, player);
+
+ // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
+ if(Transport* transport = player->GetTransport())
+ {
+ for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
+ {
+ if(player!=(*itr) && player->HaveAtClient(*itr))
+ {
+ hasTransport = true;
+ (*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
+ }
+ }
+ }
+
+ WorldPacket packet;
+ data.BuildPacket(&packet, hasTransport);
+ player->GetSession()->SendPacket(&packet);
+}
+
+void Map::SendInitTransports( Player * player )
+{
+ // Hack to send out transports
+ MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
+
+ // no transports at map
+ if (tmap.find(player->GetMapId()) == tmap.end())
+ return;
+
+ UpdateData transData;
+
+ MapManager::TransportSet& tset = tmap[player->GetMapId()];
+
+ bool hasTransport = false;
+
+ for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
+ {
+ if((*i) != player->GetTransport()) // send data for current transport in other place
+ {
+ hasTransport = true;
+ (*i)->BuildCreateUpdateBlockForPlayer(&transData, player);
+ }
+ }
+
+ WorldPacket packet;
+ transData.BuildPacket(&packet, hasTransport);
+ player->GetSession()->SendPacket(&packet);
+}
+
+void Map::SendRemoveTransports( Player * player )
+{
+ // Hack to send out transports
+ MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
+
+ // no transports at map
+ if (tmap.find(player->GetMapId()) == tmap.end())
+ return;
+
+ UpdateData transData;
+
+ MapManager::TransportSet& tset = tmap[player->GetMapId()];
+
+ // except used transport
+ for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
+ if(player->GetTransport() != (*i))
+ (*i)->BuildOutOfRangeUpdateBlock(&transData);
+
+ WorldPacket packet;
+ transData.BuildPacket(&packet);
+ player->GetSession()->SendPacket(&packet);
+}
+
+inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
+{
+ if(x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS)
+ {
+ sLog.outError("map::setNGrid() Invalid grid coordinates found: %d, %d!",x,y);
+ assert(false);
+ }
+ i_grids[x][y] = grid;
+}
+
+void Map::DoDelayedMovesAndRemoves()
+{
+ MoveAllCreaturesInMoveList();
+ RemoveAllObjectsInRemoveList();
+}
+
+void Map::AddObjectToRemoveList(WorldObject *obj)
+{
+ assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
+
+ i_objectsToRemove.insert(obj);
+ //sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId());
+}
+
+void Map::RemoveAllObjectsInRemoveList()
+{
+ if(i_objectsToRemove.empty())
+ return;
+
+ //sLog.outDebug("Object remover 1 check.");
+ while(!i_objectsToRemove.empty())
+ {
+ WorldObject* obj = *i_objectsToRemove.begin();
+ i_objectsToRemove.erase(i_objectsToRemove.begin());
+
+ switch(obj->GetTypeId())
+ {
+ case TYPEID_CORPSE:
+ {
+ Corpse* corpse = ObjectAccessor::Instance().GetCorpse(*obj, obj->GetGUID());
+ if (!corpse)
+ sLog.outError("ERROR: Try delete corpse/bones %u that not in map", obj->GetGUIDLow());
+ else
+ Remove(corpse,true);
+ break;
+ }
+ case TYPEID_DYNAMICOBJECT:
+ Remove((DynamicObject*)obj,true);
+ break;
+ case TYPEID_GAMEOBJECT:
+ Remove((GameObject*)obj,true);
+ break;
+ case TYPEID_UNIT:
+ Remove((Creature*)obj,true);
+ break;
+ default:
+ sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId());
+ break;
+ }
+ }
+ //sLog.outDebug("Object remover 2 check.");
+}
+
+bool Map::CanUnload(const uint32 &diff)
+{
+ if(!m_unloadTimer) return false;
+ if(m_unloadTimer < diff) return true;
+ m_unloadTimer -= diff;
+ return false;
+}
+
+template void Map::Add(Corpse *);
+template void Map::Add(Creature *);
+template void Map::Add(GameObject *);
+template void Map::Add(DynamicObject *);
+
+template void Map::Remove(Corpse *,bool);
+template void Map::Remove(Creature *,bool);
+template void Map::Remove(GameObject *, bool);
+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), i_data(NULL),
+ m_resetAfterUnload(false), m_unloadWhenEmpty(false)
+{
+ // the timer is started by default, and stopped when the first player joins
+ // this make sure it gets unloaded if for some reason no player joins
+ m_unloadTimer = std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
+}
+
+InstanceMap::~InstanceMap()
+{
+ if(i_data)
+ {
+ delete i_data;
+ i_data = NULL;
+ }
+}
+
+/*
+ Do map specific checks to see if the player can enter
+*/
+bool InstanceMap::CanEnter(Player *player)
+{
+ if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
+ {
+ sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
+ assert(false);
+ return false;
+ }
+
+ // cannot enter if the instance is full (player cap), GMs don't count
+ InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId());
+ if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers)
+ {
+ sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName());
+ player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
+ return false;
+ }
+
+ // cannot enter while players in the instance are in combat
+ Group *pGroup = player->GetGroup();
+ if(pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->isAlive() && player->GetMapId() != GetId())
+ {
+ player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
+ return false;
+ }
+
+ return Map::CanEnter(player);
+}
+
+/*
+ Do map specific checks and add the player to the map if successful.
+*/
+bool InstanceMap::Add(Player *player)
+{
+ // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
+ // GMs still can teleport player in instance.
+ // Is it needed?
+
+ {
+ Guard guard(*this);
+ if(!CanEnter(player))
+ return false;
+
+ // get or create an instance save for the map
+ InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
+ if(!mapSave)
+ {
+ sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId());
+ mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true);
+ }
+
+ // check for existing instance binds
+ InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode());
+ if(playerBind && playerBind->perm)
+ {
+ // cannot enter other instances if bound permanently
+ if(playerBind->save != mapSave)
+ {
+ sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset());
+ assert(false);
+ }
+ }
+ else
+ {
+ Group *pGroup = player->GetGroup();
+ if(pGroup)
+ {
+ // solo saves should be reset when entering a group
+ InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode());
+ if(playerBind)
+ {
+ sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
+ if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
+ assert(false);
+ }
+ // bind to the group or keep using the group save
+ if(!groupBind)
+ pGroup->BindToInstance(mapSave, false);
+ else
+ {
+ // cannot jump to a different instance without resetting it
+ if(groupBind->save != mapSave)
+ {
+ sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
+ if(mapSave)
+ sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
+ else
+ sLog.outError("MapSave NULL");
+ if(groupBind->save)
+ sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
+ else
+ sLog.outError("GroupBind save NULL");
+ assert(false);
+ }
+ // if the group/leader is permanently bound to the instance
+ // players also become permanently bound when they enter
+ if(groupBind->perm)
+ {
+ WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
+ data << uint32(0);
+ player->GetSession()->SendPacket(&data);
+ player->BindToInstance(mapSave, true);
+ }
+ }
+ }
+ else
+ {
+ // set up a solo bind or continue using it
+ if(!playerBind)
+ player->BindToInstance(mapSave, false);
+ else
+ // cannot jump to a different instance without resetting it
+ assert(playerBind->save == mapSave);
+ }
+ }
+
+ if(i_data) i_data->OnPlayerEnter(player);
+ SetResetSchedule(false);
+
+ i_Players.push_back(player);
+ player->SendInitWorldStates();
+ sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName());
+ // initialize unload state
+ m_unloadTimer = 0;
+ m_resetAfterUnload = false;
+ m_unloadWhenEmpty = false;
+ }
+
+ // this will acquire the same mutex so it cannot be in the previous block
+ Map::Add(player);
+ return true;
+}
+
+void InstanceMap::Remove(Player *player, bool remove)
+{
+ sLog.outDetail("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
+ i_Players.remove(player);
+ SetResetSchedule(true);
+ if(!m_unloadTimer && i_Players.empty())
+ m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
+ Map::Remove(player, remove);
+}
+
+void InstanceMap::CreateInstanceData(bool load)
+{
+ if(i_data != NULL)
+ return;
+
+ InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId());
+ if (mInstance)
+ {
+ i_script = mInstance->script;
+ i_data = Script->CreateInstanceData(this);
+ }
+
+ if(!i_data)
+ return;
+
+ if(load)
+ {
+ // TODO: make a global storage for this
+ QueryResult* result = CharacterDatabase.PQuery("SELECT data FROM instance WHERE map = '%u' AND id = '%u'", GetId(), i_InstanceId);
+ if (result)
+ {
+ Field* fields = result->Fetch();
+ const char* data = fields[0].GetString();
+ if(data)
+ {
+ sLog.outDebug("Loading instance data for `%s` with id %u", i_script.c_str(), i_InstanceId);
+ i_data->Load(data);
+ }
+ delete result;
+ }
+ }
+ else
+ {
+ sLog.outDebug("New instance data, \"%s\" ,initialized!",i_script.c_str());
+ i_data->Initialize();
+ }
+}
+
+/*
+ Returns true if there are no players in the instance
+*/
+bool InstanceMap::Reset(uint8 method)
+{
+ // note: since the map may not be loaded when the instance needs to be reset
+ // the instance must be deleted from the DB by InstanceSaveManager
+
+ if(!i_Players.empty())
+ {
+ if(method == INSTANCE_RESET_ALL)
+ {
+ // notify the players to leave the instance so it can be reset
+ for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ (*itr)->SendResetFailedNotify(GetId());
+ }
+ else
+ {
+ if(method == INSTANCE_RESET_GLOBAL)
+ {
+ // set the homebind timer for players inside (1 minute)
+ for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ (*itr)->m_InstanceValid = false;
+ }
+
+ // the unload timer is not started
+ // instead the map will unload immediately after the players have left
+ m_unloadWhenEmpty = true;
+ m_resetAfterUnload = true;
+ }
+ }
+ else
+ {
+ // unloaded at next update
+ m_unloadTimer = MIN_UNLOAD_DELAY;
+ m_resetAfterUnload = true;
+ }
+
+ return i_Players.empty();
+}
+
+uint32 InstanceMap::GetPlayersCountExceptGMs() const
+{
+ uint32 count = 0;
+ for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ if(!(*itr)->isGameMaster())
+ ++count;
+ return count;
+}
+
+void InstanceMap::PermBindAllPlayers(Player *player)
+{
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
+ if(!save)
+ {
+ sLog.outError("Cannot bind players, no instance save available for map!\n");
+ return;
+ }
+
+ Group *group = player->GetGroup();
+ // group members outside the instance group don't get bound
+ for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ {
+ if(*itr)
+ {
+ // players inside an instance cannot be bound to other instances
+ // some players may already be permanently bound, in this case nothing happens
+ InstancePlayerBind *bind = (*itr)->GetBoundInstance(save->GetMapId(), save->GetDifficulty());
+ if(!bind || !bind->perm)
+ {
+ (*itr)->BindToInstance(save, true);
+ WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
+ data << uint32(0);
+ (*itr)->GetSession()->SendPacket(&data);
+ }
+
+ // if the leader is not in the instance the group will not get a perm bind
+ if(group && group->GetLeaderGUID() == (*itr)->GetGUID())
+ group->BindToInstance(save, true);
+ }
+ }
+}
+
+time_t InstanceMap::GetResetTime()
+{
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
+ return save ? save->GetDifficulty() : DIFFICULTY_NORMAL;
+}
+
+void InstanceMap::UnloadAll(bool pForce)
+{
+ if(!i_Players.empty())
+ {
+ sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!");
+ for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ if(*itr) (*itr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
+ }
+
+ if(m_resetAfterUnload == true)
+ objmgr.DeleteRespawnTimeForInstance(GetInstanceId());
+
+ Map::UnloadAll(pForce);
+}
+
+void InstanceMap::SendResetWarnings(uint32 timeLeft)
+{
+ for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ (*itr)->SendInstanceResetWarning(GetId(), timeLeft);
+}
+
+void InstanceMap::SetResetSchedule(bool on)
+{
+ // only for normal instances
+ // the reset time is only scheduled when there are no payers inside
+ // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
+ if(i_Players.empty() && !IsRaid() && !IsHeroic())
+ {
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
+ if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
+ else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId()));
+ }
+}
+
+void InstanceMap::SendToPlayers(WorldPacket const* data) const
+{
+ for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
+ (*itr)->GetSession()->SendPacket(data);
+}
+
+/* ******* Battleground Instance Maps ******* */
+
+BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId)
+ : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL)
+{
+}
+
+BattleGroundMap::~BattleGroundMap()
+{
+}
+
+bool BattleGroundMap::CanEnter(Player * player)
+{
+ if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
+ {
+ sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow());
+ assert(false);
+ return false;
+ }
+
+ if(player->GetBattleGroundId() != GetInstanceId())
+ return false;
+
+ // player number limit is checked in bgmgr, no need to do it here
+
+ return Map::CanEnter(player);
+}
+
+bool BattleGroundMap::Add(Player * player)
+{
+ {
+ Guard guard(*this);
+ if(!CanEnter(player))
+ return false;
+ i_Players.push_back(player);
+ // reset instance validity, battleground maps do not homebind
+ player->m_InstanceValid = true;
+ }
+ return Map::Add(player);
+}
+
+void BattleGroundMap::Remove(Player *player, bool remove)
+{
+ sLog.outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
+ i_Players.remove(player);
+ Map::Remove(player, remove);
+}
+
+void BattleGroundMap::SetUnload()
+{
+ m_unloadTimer = MIN_UNLOAD_DELAY;
+}
+
+void BattleGroundMap::UnloadAll(bool pForce)
+{
+ while(!i_Players.empty())
+ {
+ PlayerList::iterator itr = i_Players.begin();
+ Player * plr = *itr;
+ if(plr) (plr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
+ // 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
+ i_Players.remove(plr);
+ }
+
+ Map::UnloadAll(pForce);
+}
diff --git a/src/game/Map.h b/src/game/Map.h
new file mode 100644
index 00000000000..bc723e4bca5
--- /dev/null
+++ b/src/game/Map.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MAP_H
+#define MANGOS_MAP_H
+
+#include "Platform/Define.h"
+#include "Policies/ThreadingModel.h"
+#include "zthread/Lockable.h"
+#include "zthread/Mutex.h"
+#include "zthread/FairReadWriteLock.h"
+#include "Database/DBCStructure.h"
+#include "GridDefines.h"
+#include "Cell.h"
+#include "Object.h"
+#include "Timer.h"
+#include "SharedDefines.h"
+#include "GameSystem/GridRefManager.h"
+
+#include <bitset>
+#include <list>
+
+class Unit;
+class WorldPacket;
+class InstanceData;
+class Group;
+class InstanceSave;
+
+namespace ZThread
+{
+ class Lockable;
+ class ReadWriteLock;
+}
+
+typedef ZThread::FairReadWriteLock GridRWLock;
+
+template<class MUTEX, class LOCK_TYPE>
+struct RGuard
+{
+ RGuard(MUTEX &l) : i_lock(l.getReadLock()) {}
+ MaNGOS::GeneralLock<LOCK_TYPE> i_lock;
+};
+
+template<class MUTEX, class LOCK_TYPE>
+struct WGuard
+{
+ WGuard(MUTEX &l) : i_lock(l.getWriteLock()) {}
+ MaNGOS::GeneralLock<LOCK_TYPE> i_lock;
+};
+
+typedef RGuard<GridRWLock, ZThread::Lockable> GridReadGuard;
+typedef WGuard<GridRWLock, ZThread::Lockable> GridWriteGuard;
+typedef MaNGOS::SingleThreaded<GridRWLock>::Lock NullGuard;
+
+typedef struct
+{
+ uint16 area_flag[16][16];
+ uint8 terrain_type[16][16];
+ float liquid_level[128][128];
+ float Z[MAP_RESOLUTION][MAP_RESOLUTION];
+}GridMap;
+
+struct CreatureMover
+{
+ CreatureMover() : x(0), y(0), z(0), ang(0) {}
+ CreatureMover(float _x, float _y, float _z, float _ang) : x(_x), y(_y), z(_z), ang(_ang) {}
+
+ float x, y, z, ang;
+};
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+struct InstanceTemplate
+{
+ uint32 map;
+ uint32 parent;
+ uint32 levelMin;
+ uint32 levelMax;
+ uint32 maxPlayers;
+ uint32 reset_delay;
+ float startLocX;
+ float startLocY;
+ float startLocZ;
+ float startLocO;
+ char const* script;
+};
+
+enum LevelRequirementVsMode
+{
+ LEVELREQUIREMENT_HEROIC = 70
+};
+
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+typedef HM_NAMESPACE::hash_map<Creature*, CreatureMover> CreatureMoveList;
+
+#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface
+#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
+#define MIN_UNLOAD_DELAY 1 // immediate unload
+
+class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::ObjectLevelLockable<Map, ZThread::Mutex>
+{
+ public:
+ Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode);
+ virtual ~Map();
+
+ // currently unused for normal maps
+ virtual bool CanUnload(const uint32& diff);
+
+ virtual bool Add(Player *);
+ virtual void Remove(Player *, bool);
+ template<class T> void Add(T *);
+ template<class T> void Remove(T *, bool);
+
+ virtual void Update(const uint32&);
+
+ void MessageBroadcast(Player *, WorldPacket *, bool to_self);
+ void MessageBroadcast(WorldObject *, WorldPacket *);
+ void MessageDistBroadcast(Player *, WorldPacket *, float dist, bool to_self, bool own_team_only = false);
+ void MessageDistBroadcast(WorldObject *, WorldPacket *, float dist);
+
+ void PlayerRelocation(Player *, float x, float y, float z, float angl);
+ void CreatureRelocation(Creature *creature, float x, float y, float, float);
+
+ template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &visitor);
+
+ inline bool IsRemovalGrid(float x, float y) const
+ {
+ GridPair p = MaNGOS::ComputeGridPair(x, y);
+ return( !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL );
+ }
+
+ bool GetUnloadFlag(const GridPair &p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadFlag(); }
+ void SetUnloadFlag(const GridPair &p, bool unload) { getNGrid(p.x_coord, p.y_coord)->setUnloadFlag(unload); }
+ void LoadGrid(const Cell& cell, bool no_unload = false);
+ bool UnloadGrid(const uint32 &x, const uint32 &y, bool pForce);
+ virtual void UnloadAll(bool pForce);
+
+ void ResetGridExpiry(NGridType &grid, float factor = 1) const
+ {
+ grid.ResetTimeTracker((time_t)((float)i_gridExpiry*factor));
+ }
+
+ time_t GetGridExpiry(void) const { return i_gridExpiry; }
+ uint32 GetId(void) const { return i_id; }
+
+ static bool ExistMap(uint32 mapid, int x, int y);
+ static bool ExistVMap(uint32 mapid, int x, int y);
+ void LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x, int y);
+
+ static void InitStateMachine();
+ static void DeleteStateMachine();
+
+ // 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;
+ bool IsInWater(float x, float y, float z) const; // does not use z pos. This is for future use
+
+ uint16 GetAreaFlag(float x, float y ) const;
+ uint8 GetTerrainType(float x, float y ) const;
+ float GetWaterLevel(float x, float y ) const;
+ bool IsUnderWater(float x, float y, float z) const;
+
+ static uint32 GetAreaId(uint16 areaflag,uint32 map_id);
+ static uint32 GetZoneId(uint16 areaflag,uint32 map_id);
+
+ uint32 GetAreaId(float x, float y) const
+ {
+ return GetAreaId(GetAreaFlag(x,y),i_id);
+ }
+
+ uint32 GetZoneId(float x, float y) const
+ {
+ return GetZoneId(GetAreaFlag(x,y),i_id);
+ }
+
+ virtual void MoveAllCreaturesInMoveList();
+ virtual void RemoveAllObjectsInRemoveList();
+
+ bool CreatureRespawnRelocation(Creature *c); // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader
+
+ // assert print helper
+ bool CheckGridIntegrity(Creature* c, bool moved) const;
+
+ uint32 GetInstanceId() { return i_InstanceId; }
+ uint8 GetSpawnMode() { return (i_spawnMode); }
+ virtual bool CanEnter(Player* /*player*/) { return true; }
+ const char* GetMapName() const;
+
+ bool Instanceable() const { return i_mapEntry && i_mapEntry->Instanceable(); }
+ // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable
+ bool IsDungeon() const { return i_mapEntry && i_mapEntry->IsDungeon(); }
+ bool IsRaid() const { return i_mapEntry && i_mapEntry->IsRaid(); }
+ bool IsHeroic() const { return i_spawnMode == DIFFICULTY_HEROIC; }
+ bool IsBattleGround() const { return i_mapEntry && i_mapEntry->IsBattleGround(); }
+ bool IsBattleArena() const { return i_mapEntry && i_mapEntry->IsBattleArena(); }
+ bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); }
+
+ void AddObjectToRemoveList(WorldObject *obj);
+ void DoDelayedMovesAndRemoves();
+
+ virtual bool RemoveBones(uint64 guid, float x, float y);
+
+ void UpdateObjectVisibility(WorldObject* obj, Cell cell, CellPair cellpair);
+ void UpdatePlayerVisibility(Player* player, Cell cell, CellPair cellpair);
+ void UpdateObjectsVisibilityFor(Player* player, Cell cell, CellPair cellpair);
+
+ void resetMarkedCells() { marked_cells.reset(); }
+ bool isCellMarked(uint32 pCellId) { return marked_cells.test(pCellId); }
+ void markCell(uint32 pCellId) { marked_cells.set(pCellId); }
+ private:
+ void LoadVMap(int pX, int pY);
+ void LoadMap(uint32 mapid, uint32 instanceid, int x,int y);
+
+ void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
+ //uint64 CalculateGridMask(const uint32 &y) const;
+
+ void SendInitSelf( Player * player );
+
+ void SendInitTransports( Player * player );
+ void SendRemoveTransports( Player * player );
+
+ void PlayerRelocationNotify(Player* player, Cell cell, CellPair cellpair);
+ void CreatureRelocationNotify(Creature *creature, Cell newcell, CellPair newval);
+
+ bool CreatureCellRelocation(Creature *creature, Cell new_cell);
+
+ void AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang);
+ CreatureMoveList i_creaturesToMove;
+
+ bool loaded(const GridPair &) const;
+ void EnsureGridLoadedForPlayer(const Cell&, Player*, bool add_player);
+ void EnsureGridCreated(const GridPair &);
+
+ void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); }
+
+ template<class T> void AddType(T *obj);
+ template<class T> void RemoveType(T *obj, bool);
+
+ NGridType* getNGrid(uint32 x, uint32 y) const
+ {
+ return i_grids[x][y];
+ }
+
+ bool isGridObjectDataLoaded(uint32 x, uint32 y) const { return getNGrid(x,y)->isGridObjectDataLoaded(); }
+ void setGridObjectDataLoaded(bool pLoaded, uint32 x, uint32 y) { getNGrid(x,y)->setGridObjectDataLoaded(pLoaded); }
+
+ inline void setNGrid(NGridType* grid, uint32 x, uint32 y);
+
+ protected:
+ typedef MaNGOS::ObjectLevelLockable<Map, ZThread::Mutex>::Lock Guard;
+
+ MapEntry const* i_mapEntry;
+ uint8 i_spawnMode;
+ uint32 i_id;
+ uint32 i_InstanceId;
+ uint32 m_unloadTimer;
+
+ private:
+ typedef GridReadGuard ReadGuard;
+ typedef GridWriteGuard WriteGuard;
+
+ NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
+ GridMap *GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
+ std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP*TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells;
+
+ time_t i_gridExpiry;
+
+ std::set<WorldObject *> i_objectsToRemove;
+
+ // Type specific code for add/remove to/from grid
+ template<class T>
+ void AddToGrid(T*, NGridType *, Cell const&);
+
+ template<class T>
+ void AddNotifier(T*, Cell const&, CellPair const&);
+
+ template<class T>
+ void RemoveFromGrid(T*, NGridType *, Cell const&);
+
+ template<class T>
+ void DeleteFromWorld(T*);
+};
+
+enum InstanceResetMethod
+{
+ INSTANCE_RESET_ALL,
+ INSTANCE_RESET_CHANGE_DIFFICULTY,
+ INSTANCE_RESET_GLOBAL,
+ INSTANCE_RESET_GROUP_DISBAND,
+ INSTANCE_RESET_GROUP_JOIN,
+ INSTANCE_RESET_RESPAWN_DELAY
+};
+
+class MANGOS_DLL_SPEC InstanceMap : public Map
+{
+ public:
+ typedef std::list<Player *> PlayerList; // online players only
+
+ InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode);
+ ~InstanceMap();
+ bool Add(Player *);
+ void Remove(Player *, bool);
+ void Update(const uint32&);
+ void CreateInstanceData(bool load);
+ bool Reset(uint8 method);
+ std::string GetScript() { return i_script; }
+ InstanceData* GetInstanceData() { return i_data; }
+ void PermBindAllPlayers(Player *player);
+ PlayerList const& GetPlayers() const { return i_Players;}
+ void SendToPlayers(WorldPacket const* data) const;
+ time_t GetResetTime();
+ void UnloadAll(bool pForce);
+ bool CanEnter(Player* player);
+ uint32 GetPlayersCountExceptGMs() const;
+ uint32 HavePlayers() const { return !i_Players.empty(); }
+ void SendResetWarnings(uint32 timeLeft);
+ void SetResetSchedule(bool on);
+ private:
+ bool m_resetAfterUnload;
+ bool m_unloadWhenEmpty;
+ InstanceData* i_data;
+ std::string i_script;
+ // only online players that are inside the instance currently
+ // TODO ? - use the grid instead to access the players
+ PlayerList i_Players;
+};
+
+class MANGOS_DLL_SPEC BattleGroundMap : public Map
+{
+ public:
+ typedef std::list<Player *> PlayerList; // online players only
+
+ BattleGroundMap(uint32 id, time_t, uint32 InstanceId);
+ ~BattleGroundMap();
+
+ bool Add(Player *);
+ void Remove(Player *, bool);
+ bool CanEnter(Player* player);
+ void SetUnload();
+ void UnloadAll(bool pForce);
+ private:
+ PlayerList i_Players;
+};
+
+/*inline
+uint64
+Map::CalculateGridMask(const uint32 &y) const
+{
+ uint64 mask = 1;
+ mask <<= y;
+ return mask;
+}
+*/
+
+template<class LOCK_TYPE, class T, class CONTAINER>
+inline void
+Map::Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &visitor)
+{
+ const uint32 x = cell->GridX();
+ const uint32 y = cell->GridY();
+ const uint32 cell_x = cell->CellX();
+ const uint32 cell_y = cell->CellY();
+
+ if( !cell->NoCreate() || loaded(GridPair(x,y)) )
+ {
+ EnsureGridLoadedForPlayer(cell, NULL, false);
+ //LOCK_TYPE guard(i_info[x][y]->i_lock);
+ getNGrid(x, y)->Visit(cell_x, cell_y, visitor);
+ }
+}
+#endif
diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp
new file mode 100644
index 00000000000..2ff105c2f9d
--- /dev/null
+++ b/src/game/MapInstanced.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "MapInstanced.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "BattleGround.h"
+#include "VMapFactory.h"
+#include "InstanceSaveMgr.h"
+#include "World.h"
+
+MapInstanced::MapInstanced(uint32 id, time_t expiry, uint32 aInstanceId) : Map(id, expiry, 0, 0)
+{
+ // initialize instanced maps list
+ m_InstancedMaps.clear();
+ // fill with zero
+ memset(&GridMapReference, 0, MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_GRIDS*sizeof(uint16));
+}
+
+void MapInstanced::Update(const uint32& t)
+{
+ // take care of loaded GridMaps (when unused, unload it!)
+ Map::Update(t);
+
+ // update the instanced maps
+ InstancedMaps::iterator i = m_InstancedMaps.begin();
+
+ while (i != m_InstancedMaps.end())
+ {
+ if(i->second->CanUnload(t))
+ {
+ DestroyInstance(i); // iterator incremented
+ }
+ else
+ {
+ // update only here, because it may schedule some bad things before delete
+ i->second->Update(t);
+ ++i;
+ }
+ }
+}
+
+void MapInstanced::MoveAllCreaturesInMoveList()
+{
+ for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++)
+ {
+ i->second->MoveAllCreaturesInMoveList();
+ }
+
+ Map::MoveAllCreaturesInMoveList();
+}
+
+void MapInstanced::RemoveAllObjectsInRemoveList()
+{
+ for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++)
+ {
+ i->second->RemoveAllObjectsInRemoveList();
+ }
+
+ Map::RemoveAllObjectsInRemoveList();
+}
+
+bool MapInstanced::RemoveBones(uint64 guid, float x, float y)
+{
+ bool remove_result = false;
+
+ for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++)
+ {
+ remove_result = remove_result || i->second->RemoveBones(guid, x, y);
+ }
+
+ return remove_result || Map::RemoveBones(guid,x,y);
+}
+
+void MapInstanced::UnloadAll(bool pForce)
+{
+ // Unload instanced maps
+ for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++)
+ i->second->UnloadAll(pForce);
+
+ // Delete the maps only after everything is unloaded to prevent crashes
+ for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++)
+ delete i->second;
+
+ m_InstancedMaps.clear();
+
+ // Unload own grids (just dummy(placeholder) grids, neccesary to unload GridMaps!)
+ Map::UnloadAll(pForce);
+}
+
+/*
+- return the right instance for the object, based on its InstanceId
+- 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)
+{
+ uint32 CurInstanceId = obj->GetInstanceId();
+ Map* map = NULL;
+
+ if (obj->GetMapId() == GetId() && CurInstanceId != 0)
+ {
+ // the object wants to be put in a certain instance of this map
+ map = _FindMap(CurInstanceId);
+ if(!map)
+ {
+ // For players if the instanceId is set, it's assumed they are already in a map,
+ // hence the map must be loaded. For Creatures, GameObjects etc the map must exist
+ // prior to calling GetMap, they are not allowed to create maps for themselves.
+ sLog.outError("GetInstance: object %s(%d), typeId %d, in world %d, should be in map %d,%d but that's not loaded yet.", obj->GetName(), obj->GetGUIDLow(), obj->GetTypeId(), obj->IsInWorld(), obj->GetMapId(), obj->GetInstanceId());
+ assert(false);
+ }
+ return(map);
+ }
+ else
+ {
+ // instance not specified, find an existing or create a new one
+ if(obj->GetTypeId() != TYPEID_PLAYER)
+ {
+ sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u TypeID: %u) is in map %d,%d and requested base map instance of map %d, this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), obj->GetMapId(), obj->GetInstanceId(), GetId());
+ assert(false);
+ return NULL;
+ }
+ else
+ {
+ uint32 NewInstanceId = 0; // instanceId of the resulting map
+ Player* player = (Player*)obj;
+
+ // TODO: battlegrounds and arenas
+
+ InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty());
+ InstanceSave *pSave = pBind ? pBind->save : NULL;
+
+ // the player's permanet player bind is taken into consideration first
+ // then the player's group bind and finally the solo bind.
+ if(!pBind || !pBind->perm)
+ {
+ InstanceGroupBind *groupBind = NULL;
+ Group *group = player->GetGroup();
+ // use the player's difficulty setting (it may not be the same as the group's)
+ if(group && (groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty())))
+ pSave = groupBind->save;
+ }
+
+ if(pSave)
+ {
+ // solo/perm/group
+ NewInstanceId = pSave->GetInstanceId();
+ map = _FindMap(NewInstanceId);
+ // it is possible that the save exists but the map doesn't
+ if(!map)
+ map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty());
+ return map;
+ }
+ else
+ {
+ // if no instanceId via group members or instance saves is found
+ // the instance will be created for the first time
+ NewInstanceId = MapManager::Instance().GenerateInstanceId();
+ return CreateInstance(NewInstanceId, NULL, player->GetDifficulty());
+ }
+ }
+ }
+}
+
+InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty)
+{
+ // load/create a map
+ Guard guard(*this);
+
+ // make sure we have a valid map id
+ const MapEntry* entry = sMapStore.LookupEntry(GetId());
+ if(!entry)
+ {
+ sLog.outError("CreateInstance: no entry for map %d", GetId());
+ assert(false);
+ }
+ const InstanceTemplate * iTemplate = objmgr.GetInstanceTemplate(GetId());
+ if(!iTemplate)
+ {
+ sLog.outError("CreateInstance: no instance template for map %d", GetId());
+ assert(false);
+ }
+
+ // some instances only have one difficulty
+ if(!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");
+
+ InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty);
+ assert(map->IsDungeon());
+
+ bool load_data = save != NULL;
+ map->CreateInstanceData(load_data);
+
+ m_InstancedMaps[InstanceId] = map;
+ return map;
+}
+
+BattleGroundMap* MapInstanced::CreateBattleGround(uint32 InstanceId)
+{
+ // load/create a map
+ Guard guard(*this);
+
+ sLog.outDebug("MapInstanced::CreateBattleGround: map bg %d for %d created.", InstanceId, GetId());
+
+ BattleGroundMap *map = new BattleGroundMap(GetId(), GetGridExpiry(), InstanceId);
+ 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)
+{
+ itr->second->UnloadAll(true);
+ // 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))
+ {
+ VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ // in that case, unload grids of the base map, too
+ // so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
+ Map::UnloadAll(true);
+ }
+ // erase map
+ delete itr->second;
+ m_InstancedMaps.erase(itr++);
+}
+
diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h
new file mode 100644
index 00000000000..58ee6b7a9bc
--- /dev/null
+++ b/src/game/MapInstanced.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MAP_INSTANCED_H
+#define MANGOS_MAP_INSTANCED_H
+
+#include "Map.h"
+#include "InstanceSaveMgr.h"
+
+class MANGOS_DLL_DECL MapInstanced : public Map
+{
+ friend class MapManager;
+ public:
+ typedef HM_NAMESPACE::hash_map< uint32, Map* > InstancedMaps;
+
+ MapInstanced(uint32 id, time_t, uint32 aInstanceId);
+ ~MapInstanced() {}
+
+ // functions overwrite Map versions
+ void Update(const uint32&);
+ void MoveAllCreaturesInMoveList();
+ void RemoveAllObjectsInRemoveList();
+ bool RemoveBones(uint64 guid, float x, float y);
+ void UnloadAll(bool pForce);
+
+ Map* GetInstance(const WorldObject* obj);
+ Map* FindMap(uint32 InstanceId) { return _FindMap(InstanceId); }
+ void DestroyInstance(uint32 InstanceId);
+ void DestroyInstance(InstancedMaps::iterator &itr);
+ void AddGridMapReference(const GridPair &p) { ++GridMapReference[p.x_coord][p.y_coord]; }
+ void RemoveGridMapReference(const GridPair &p)
+ {
+ --GridMapReference[p.x_coord][p.y_coord];
+ if (!GridMapReference[p.x_coord][p.y_coord]) { SetUnloadFlag(GridPair(63-p.x_coord,63-p.y_coord), true); }
+ }
+
+ InstancedMaps &GetInstancedMaps() { return m_InstancedMaps; }
+
+ private:
+
+ InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty);
+ BattleGroundMap* CreateBattleGround(uint32 InstanceId);
+
+ InstancedMaps m_InstancedMaps;
+
+ Map* _FindMap(uint32 InstanceId)
+ {
+ InstancedMaps::iterator i = m_InstancedMaps.find(InstanceId);
+
+ return(i == m_InstancedMaps.end() ? NULL : i->second);
+ }
+
+ uint16 GridMapReference[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
+};
+#endif
diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp
new file mode 100644
index 00000000000..84c13333ddf
--- /dev/null
+++ b/src/game/MapManager.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "MapManager.h"
+#include "InstanceSaveMgr.h"
+#include "Policies/SingletonImp.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "ObjectAccessor.h"
+#include "Transports.h"
+#include "GridDefines.h"
+#include "MapInstanced.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+#include "CellImpl.h"
+#include "Corpse.h"
+#include "ObjectMgr.h"
+
+#define CLASS_LOCK MaNGOS::ClassLevelLockable<MapManager, ZThread::Mutex>
+INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK);
+INSTANTIATE_CLASS_MUTEX(MapManager, ZThread::Mutex);
+
+extern GridState* si_GridStates[]; // debugging code, should be deleted some day
+
+MapManager::MapManager() : i_gridCleanUpDelay(sWorld.getConfig(CONFIG_INTERVAL_GRIDCLEAN))
+{
+ i_timer.SetInterval(sWorld.getConfig(CONFIG_INTERVAL_MAPUPDATE));
+}
+
+MapManager::~MapManager()
+{
+ for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
+ delete iter->second;
+
+ for(TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i)
+ delete *i;
+
+ Map::DeleteStateMachine();
+}
+
+void
+MapManager::Initialize()
+{
+ Map::InitStateMachine();
+
+ // debugging code, should be deleted some day
+ {
+ for(int i=0;i<MAX_GRID_STATE; i++)
+ {
+ i_GridStates[i] = si_GridStates[i];
+ }
+ i_GridStateErrorCount = 0;
+ }
+
+ InitMaxInstanceId();
+}
+
+// debugging code, should be deleted some day
+void MapManager::checkAndCorrectGridStatesArray()
+{
+ bool ok = true;
+ for(int i=0;i<MAX_GRID_STATE; i++)
+ {
+ if(i_GridStates[i] != si_GridStates[i])
+ {
+ sLog.outError("MapManager::checkGridStates(), GridState: si_GridStates is currupt !!!");
+ ok = false;
+ si_GridStates[i] = i_GridStates[i];
+ }
+ #ifdef MANGOS_DEBUG
+ // inner class checking only when compiled with debug
+ if(!si_GridStates[i]->checkMagic())
+ {
+ ok = false;
+ si_GridStates[i]->setMagic();
+ }
+ #endif
+ }
+ if(!ok)
+ ++i_GridStateErrorCount;
+ if(i_GridStateErrorCount > 2)
+ assert(false); // force a crash. Too many errors
+}
+
+Map*
+MapManager::_GetBaseMap(uint32 id)
+{
+ Map *m = _findMap(id);
+
+ if( m == NULL )
+ {
+ Guard guard(*this);
+
+ const MapEntry* entry = sMapStore.LookupEntry(id);
+ if (entry && entry->IsDungeon())
+ {
+ m = new MapInstanced(id, i_gridCleanUpDelay, 0);
+ }
+ else
+ {
+ m = new Map(id, i_gridCleanUpDelay, 0, 0);
+ }
+ i_maps[id] = m;
+ }
+
+ assert(m != NULL);
+ return m;
+}
+
+Map* MapManager::GetMap(uint32 id, const WorldObject* 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 = _GetBaseMap(id);
+
+ if (m && obj && m->Instanceable()) m = ((MapInstanced*)m)->GetInstance(obj);
+
+ return m;
+}
+
+Map* MapManager::FindMap(uint32 mapid, uint32 instanceId)
+{
+ Map *map = FindMap(mapid);
+ if(!map) return NULL;
+ if(!map->Instanceable()) return instanceId == 0 ? map : NULL;
+ return ((MapInstanced*)map)->FindMap(instanceId);
+}
+
+/*
+ checks that do not require a map to be created
+ will send transfer error messages on fail
+*/
+bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
+{
+ const MapEntry *entry = sMapStore.LookupEntry(mapid);
+ if(!entry) return false;
+ const char *mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
+
+ if(entry->map_type == MAP_INSTANCE || entry->map_type == MAP_RAID)
+ {
+ if (entry->map_type == MAP_RAID)
+ {
+ // GMs can avoid raid limitations
+ if(!player->isGameMaster() && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_RAID))
+ {
+ // can only enter in a raid group
+ Group* group = player->GetGroup();
+ if (!group || !group->isRaidGroup())
+ {
+ // probably there must be special opcode, because client has this string constant in GlobalStrings.lua
+ // TODO: this is not a good place to send the message
+ player->GetSession()->SendAreaTriggerMessage("You must be in a raid group to enter %s instance", mapName);
+ sLog.outDebug("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName);
+ return false;
+ }
+ }
+ }
+
+ //The player has a heroic mode and tries to enter into instance which has no a heroic mode
+ if (!entry->SupportsHeroicMode() && player->GetDifficulty() == DIFFICULTY_HEROIC)
+ {
+ player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY2); //Send aborted message
+ return false;
+ }
+
+ if (!player->isAlive())
+ {
+ if(Corpse *corpse = player->GetCorpse())
+ {
+ // let enter in ghost mode in instance that connected to inner instance with corpse
+ uint32 instance_map = corpse->GetMapId();
+ do
+ {
+ if(instance_map==mapid)
+ break;
+
+ InstanceTemplate const* instance = objmgr.GetInstanceTemplate(instance_map);
+ instance_map = instance ? instance->parent : 0;
+ }
+ while (instance_map);
+
+ if (!instance_map)
+ {
+ player->GetSession()->SendAreaTriggerMessage("You cannot enter %s while in a ghost mode", mapName);
+ sLog.outDebug("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player->GetName(), mapName);
+ return false;
+ }
+ sLog.outDebug("MAP: Player '%s' has corpse in instance '%s' and can enter", player->GetName(), mapName);
+ }
+ else
+ {
+ sLog.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName());
+ }
+ }
+
+ // TODO: move this to a map dependent location
+ /*if(i_data && i_data->IsEncounterInProgress())
+ {
+ sLog.outDebug("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName());
+ player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
+ return(false);
+ }*/
+ return true;
+ }
+ else
+ return true;
+}
+
+void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId, uint8 mode)
+{
+ Map *m = _GetBaseMap(mapid);
+ if (m && m->Instanceable())
+ ((MapInstanced*)m)->DestroyInstance(instanceId);
+}
+
+void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y)
+{
+ bool remove_result = _GetBaseMap(mapid)->RemoveBones(guid, x, y);
+
+ if (!remove_result)
+ {
+ sLog.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid));
+ }
+}
+
+void
+MapManager::Update(time_t diff)
+{
+ i_timer.Update(diff);
+ if( !i_timer.Passed() )
+ return;
+
+ for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
+ {
+ checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day
+ iter->second->Update(i_timer.GetCurrent());
+ }
+
+ ObjectAccessor::Instance().Update(i_timer.GetCurrent());
+ for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter)
+ (*iter)->Update(i_timer.GetCurrent());
+
+ i_timer.SetCurrent(0);
+}
+
+void MapManager::DoDelayedMovesAndRemoves()
+{
+ for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
+ iter->second->DoDelayedMovesAndRemoves();
+}
+
+bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y)
+{
+ GridPair p = MaNGOS::ComputeGridPair(x,y);
+
+ int gx=63-p.x_coord;
+ int gy=63-p.y_coord;
+
+ return Map::ExistMap(mapid,gx,gy) && Map::ExistVMap(mapid,gx,gy);
+}
+
+bool MapManager::IsValidMAP(uint32 mapid)
+{
+ MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
+ return mEntry && (!mEntry->Instanceable() || objmgr.GetInstanceTemplate(mapid));
+}
+
+void MapManager::LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload)
+{
+ CellPair p = MaNGOS::ComputeCellPair(x,y);
+ Cell cell(p);
+ GetMap(mapid, obj)->LoadGrid(cell,no_unload);
+}
+
+void MapManager::UnloadAll()
+{
+ for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
+ iter->second->UnloadAll(true);
+
+ while(!i_maps.empty())
+ {
+ delete i_maps.begin()->second;
+ i_maps.erase(i_maps.begin());
+ }
+}
+
+void MapManager::InitMaxInstanceId()
+{
+ i_MaxInstanceId = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT MAX(id) FROM instance" );
+ if( result )
+ {
+ i_MaxInstanceId = result->Fetch()[0].GetUInt32();
+ delete result;
+ }
+}
+
+uint32 MapManager::GetNumInstances()
+{
+ uint32 ret = 0;
+ for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
+ {
+ Map *map = itr->second;
+ if(!map->Instanceable()) continue;
+ MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
+ for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
+ if(mitr->second->IsDungeon()) ret++;
+ }
+ return ret;
+}
+
+uint32 MapManager::GetNumPlayersInInstances()
+{
+ uint32 ret = 0;
+ for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
+ {
+ Map *map = itr->second;
+ if(!map->Instanceable()) continue;
+ MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
+ for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
+ if(mitr->second->IsDungeon())
+ ret += ((InstanceMap*)mitr->second)->GetPlayers().size();
+ }
+ return ret;
+}
diff --git a/src/game/MapManager.h b/src/game/MapManager.h
new file mode 100644
index 00000000000..833f14dfc62
--- /dev/null
+++ b/src/game/MapManager.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MAPMANAGER_H
+#define MANGOS_MAPMANAGER_H
+
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "zthread/Mutex.h"
+#include "Common.h"
+#include "Map.h"
+#include "GridStates.h"
+class Transport;
+
+class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::ClassLevelLockable<MapManager, ZThread::Mutex> >
+{
+
+ friend class MaNGOS::OperatorNew<MapManager>;
+ typedef HM_NAMESPACE::hash_map<uint32, Map*> MapMapType;
+ typedef std::pair<HM_NAMESPACE::hash_map<uint32, Map*>::iterator, bool> MapMapPair;
+
+ public:
+
+ Map* GetMap(uint32, const WorldObject* obj);
+ Map* FindMap(uint32 mapid) { return _findMap(mapid); }
+ Map* FindMap(uint32 mapid, uint32 instanceId);
+
+ // only const version for outer users
+ Map const* GetBaseMap(uint32 id) const { return const_cast<MapManager*>(this)->_GetBaseMap(id); }
+ void DeleteInstance(uint32 mapid, uint32 instanceId, uint8 mode);
+
+ inline uint16 GetAreaFlag(uint32 mapid, float x, float y) const
+ {
+ Map const* m = GetBaseMap(mapid);
+ return m->GetAreaFlag(x, y);
+ }
+ inline uint32 GetAreaId(uint32 mapid, float x, float y) { return Map::GetAreaId(GetAreaFlag(mapid, x, y),mapid); }
+ inline uint32 GetZoneId(uint32 mapid, float x, float y) { return Map::GetZoneId(GetAreaFlag(mapid, x, y),mapid); }
+
+ void Initialize(void);
+ void Update(time_t);
+
+ inline void SetGridCleanUpDelay(uint32 t)
+ {
+ if( t < MIN_GRID_DELAY )
+ i_gridCleanUpDelay = MIN_GRID_DELAY;
+ else
+ i_gridCleanUpDelay = t;
+ }
+
+ inline void SetMapUpdateInterval(uint32 t)
+ {
+ if( t > MIN_MAP_UPDATE_DELAY )
+ t = MIN_MAP_UPDATE_DELAY;
+
+ i_timer.SetInterval(t);
+ i_timer.Reset();
+ }
+
+ void LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload = false);
+ void UnloadAll();
+
+ static bool ExistMapAndVMap(uint32 mapid, float x, float y);
+ static bool IsValidMAP(uint32 mapid);
+
+ static bool IsValidMapCoord(uint32 mapid, float x,float y)
+ {
+ return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y);
+ }
+
+ static bool IsValidMapCoord(uint32 mapid, float x,float y,float z)
+ {
+ return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y,z);
+ }
+
+ static bool IsValidMapCoord(uint32 mapid, float x,float y,float z,float o)
+ {
+ return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y,z,o);
+ }
+
+ void DoDelayedMovesAndRemoves();
+
+ void LoadTransports();
+
+ typedef std::set<Transport *> TransportSet;
+ TransportSet m_Transports;
+
+ typedef std::map<uint32, TransportSet> TransportMap;
+ TransportMap m_TransportsByMap;
+
+ bool CanPlayerEnter(uint32 mapid, Player* player);
+ void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y);
+ inline uint32 GenerateInstanceId() { return ++i_MaxInstanceId; }
+ void InitMaxInstanceId();
+
+ /* statistics */
+ uint32 GetNumInstances();
+ uint32 GetNumPlayersInInstances();
+
+ private:
+ // debugging code, should be deleted some day
+ void checkAndCorrectGridStatesArray(); // just for debugging to find some memory overwrites
+ GridState* i_GridStates[MAX_GRID_STATE]; // shadow entries to the global array in Map.cpp
+ int i_GridStateErrorCount;
+ private:
+ MapManager();
+ ~MapManager();
+
+ MapManager(const MapManager &);
+ MapManager& operator=(const MapManager &);
+
+ Map* _GetBaseMap(uint32 id);
+ Map* _findMap(uint32 id) const
+ {
+ MapMapType::const_iterator iter = i_maps.find(id);
+ return (iter == i_maps.end() ? NULL : iter->second);
+ }
+
+ typedef MaNGOS::ClassLevelLockable<MapManager, ZThread::Mutex>::Lock Guard;
+ uint32 i_gridCleanUpDelay;
+ MapMapType i_maps;
+ IntervalTimer i_timer;
+
+ uint32 i_MaxInstanceId;
+};
+#endif
diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp
new file mode 100644
index 00000000000..092795c6fbe
--- /dev/null
+++ b/src/game/MiscHandler.cpp
@@ -0,0 +1,1761 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Player.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "Auth/BigNumber.h"
+#include "Auth/Sha1.h"
+#include "UpdateData.h"
+#include "LootMgr.h"
+#include "Chat.h"
+#include "ScriptCalls.h"
+#include <zlib/zlib.h>
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Object.h"
+#include "BattleGround.h"
+#include "SpellAuras.h"
+#include "Pet.h"
+#include "SocialMgr.h"
+
+void WorldSession::HandleRepopRequestOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_REPOP_REQUEST Message" );
+
+ if(GetPlayer()->isAlive()||GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ return;
+
+ // the world update order is sessions, players, creatures
+ // the netcode runs in parallel with all of these
+ // creatures can kill players
+ // so if the server is lagging enough the player can
+ // release spirit after he's killed but before he is updated
+ if(GetPlayer()->getDeathState() == JUST_DIED)
+ {
+ sLog.outDebug("HandleRepopRequestOpcode: got request after player %s(%d) was killed and before he was updated", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
+ GetPlayer()->KillPlayer();
+ }
+
+ //this is spirit release confirm?
+ GetPlayer()->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
+ GetPlayer()->BuildPlayerRepop();
+ GetPlayer()->RepopAtGraveyard();
+}
+
+void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1+1+4+4+4+4);
+
+ sLog.outDebug( "WORLD: Recvd CMSG_WHO Message" );
+ //recv_data.hexlike();
+
+ uint32 clientcount = 0;
+
+ uint32 level_min, level_max, racemask, classmask, zones_count, str_count;
+ uint32 zoneids[10]; // 10 is client limit
+ std::string player_name, guild_name;
+
+ recv_data >> level_min; // maximal player level, default 0
+ recv_data >> level_max; // minimal player level, default 100
+ recv_data >> player_name; // player name, case sensitive...
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+1+4+4+4+4);
+
+ recv_data >> guild_name; // guild name, case sensitive...
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+4);
+
+ recv_data >> racemask; // race mask
+ recv_data >> classmask; // class mask
+ recv_data >> zones_count; // zones count, client limit=10 (2.0.10)
+
+ if(zones_count > 10)
+ return; // can't be received from real client or broken packet
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4);
+
+ for(uint32 i = 0; i < zones_count; i++)
+ {
+ uint32 temp;
+ recv_data >> temp; // zone id, 0 if zone is unknown...
+ zoneids[i] = temp;
+ sLog.outDebug("Zone %u: %u", i, zoneids[i]);
+ }
+
+ recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10)
+
+ if(str_count > 4)
+ return; // can't be received from real client or broken packet
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4+(1*str_count));
+
+ sLog.outDebug("Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count);
+
+ std::wstring str[4]; // 4 is client limit
+ for(uint32 i = 0; i < str_count; i++)
+ {
+ // recheck (have one more byte)
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos());
+
+ std::string temp;
+ recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)?
+
+ if(!Utf8toWStr(temp,str[i]))
+ continue;
+
+ wstrToLower(str[i]);
+
+ sLog.outDebug("String %u: %s", i, str[i].c_str());
+ }
+
+ std::wstring wplayer_name;
+ std::wstring wguild_name;
+ if(!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name)))
+ return;
+ wstrToLower(wplayer_name);
+ wstrToLower(wguild_name);
+
+ // client send in case not set max level value 100 but mangos support 255 max level,
+ // update it to show GMs with characters after 100 level
+ if(level_max >= 100)
+ level_max = 255;
+
+ 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);
+
+ WorldPacket data( SMSG_WHO, 50 ); // guess size
+ data << clientcount; // clientcount place holder
+ data << clientcount; // clientcount place holder
+
+ //TODO: Guard Player map
+ HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if (security == SEC_PLAYER)
+ {
+ // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST
+ if (itr->second->GetTeam() != team && !allowTwoSideWhoList )
+ continue;
+
+ // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST
+ if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList))
+ continue;
+ }
+
+ // check if target is globally visible for player
+ if (!(itr->second->IsVisibleGloballyFor(_player)))
+ continue;
+
+ // check if target's level is in level range
+ uint32 lvl = itr->second->getLevel();
+ if (lvl < level_min || lvl > level_max)
+ continue;
+
+ // check if class matches classmask
+ uint32 class_ = itr->second->getClass();
+ if (!(classmask & (1 << class_)))
+ continue;
+
+ // check if race matches racemask
+ uint32 race = itr->second->getRace();
+ if (!(racemask & (1 << race)))
+ continue;
+
+ uint32 pzoneid = itr->second->GetZoneId();
+
+ bool z_show = true;
+ for(uint32 i = 0; i < zones_count; i++)
+ {
+ if(zoneids[i] == pzoneid)
+ {
+ z_show = true;
+ break;
+ }
+
+ z_show = false;
+ }
+ if (!z_show)
+ continue;
+
+ std::string pname = itr->second->GetName();
+ std::wstring wpname;
+ if(!Utf8toWStr(pname,wpname))
+ continue;
+ wstrToLower(wpname);
+
+ if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos))
+ continue;
+
+ std::string gname = objmgr.GetGuildNameById(itr->second->GetGuildId());
+ std::wstring wgname;
+ if(!Utf8toWStr(gname,wgname))
+ continue;
+ wstrToLower(wgname);
+
+ if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos))
+ continue;
+
+ std::string aname;
+ if(AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId()))
+ aname = areaEntry->area_name[GetSessionDbcLocale()];
+
+ bool s_show = true;
+ for(uint32 i = 0; i < str_count; i++)
+ {
+ if (!str[i].empty())
+ {
+ if (wgname.find(str[i]) != std::wstring::npos ||
+ wpname.find(str[i]) != std::wstring::npos ||
+ Utf8FitTo(aname, str[i]) )
+ {
+ s_show = true;
+ break;
+ }
+ s_show = false;
+ }
+ }
+ if (!s_show)
+ continue;
+
+ data << pname; // player name
+ data << gname; // guild name
+ data << uint32( lvl ); // player level
+ data << uint32( class_ ); // player class
+ data << uint32( race ); // player race
+ data << uint8(0); // new 2.4.0
+ data << uint32( pzoneid ); // player zone id
+
+ // 49 is maximum player count sent to client
+ if ((++clientcount) == 49)
+ break;
+ }
+
+ data.put( 0, clientcount ); //insert right count
+ data.put( sizeof(uint32), clientcount ); //insert right count
+
+ SendPacket(&data);
+ sLog.outDebug( "WORLD: Send SMSG_WHO Message" );
+}
+
+void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() );
+
+ if (uint64 lguid = GetPlayer()->GetLootGUID())
+ DoLootRelease(lguid);
+
+ //instant logout for admins, gm's, mod's
+ if( GetSecurity() > SEC_PLAYER )
+ {
+ LogoutPlayer(true);
+ return;
+ }
+
+ //Can not logout if...
+ if( GetPlayer()->isInCombat() || //...is in combat
+ GetPlayer()->duel || //...is in Duel
+ //...is jumping ...is falling
+ GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING))
+ {
+ WorldPacket data( SMSG_LOGOUT_RESPONSE, (2+4) ) ;
+ data << (uint8)0xC;
+ data << uint32(0);
+ data << uint8(0);
+ SendPacket( &data );
+ LogoutRequest(0);
+ return;
+ }
+
+ //instant logout in taverns/cities or on taxi
+ if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight())
+ {
+ LogoutPlayer(true);
+ return;
+ }
+
+ // not set flags if player can't free move to prevent lost state at logout cancel
+ if(GetPlayer()->CanFreeMove())
+ {
+ GetPlayer()->SetStandState(PLAYER_STATE_SIT);
+
+ WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data << (uint32)2;
+ SendPacket( &data );
+ GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+
+ WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 );
+ data << uint32(0);
+ data << uint8(0);
+ SendPacket( &data );
+ LogoutRequest(time(NULL));
+}
+
+void WorldSession::HandlePlayerLogoutOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_PLAYER_LOGOUT Message" );
+}
+
+void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_CANCEL Message" );
+
+ LogoutRequest(0);
+
+ WorldPacket data( SMSG_LOGOUT_CANCEL_ACK, 0 );
+ SendPacket( &data );
+
+ // not remove flags if can't free move - its not set in Logout request code.
+ if(GetPlayer()->CanFreeMove())
+ {
+ //!we can move again
+ data.Initialize( SMSG_FORCE_MOVE_UNROOT, 8 ); // guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data << uint32(0);
+ SendPacket( &data );
+
+ //! Stand Up
+ GetPlayer()->SetStandState(PLAYER_STATE_NONE);
+
+ //! DISABLE_ROTATE
+ GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+
+ sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" );
+}
+
+void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
+{
+ int len = text ? strlen(text) : 0;
+ WorldPacket data( SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4) );
+ data << uint32(status); // standard 0x0A, 0x06 if text present
+ if(status == 6)
+ {
+ data << text; // ticket text
+ data << uint8(0x7); // ticket category
+ data << float(0); // time from ticket creation?
+ data << float(0); // const
+ data << float(0); // const
+ data << uint8(0); // const
+ data << uint8(0); // const
+ }
+ SendPacket( &data );
+}
+
+void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ SendPacket( &data );
+
+ uint64 guid;
+ Field *fields;
+ guid = GetPlayer()->GetGUID();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(ticket_id) FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if (result)
+ {
+ int cnt;
+ fields = result->Fetch();
+ cnt = fields[0].GetUInt32();
+ delete result;
+
+ if ( cnt > 0 )
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT ticket_text FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
+ if(result2)
+ {
+ Field *fields2 = result2->Fetch();
+ SendGMTicketGetTicket(0x06,fields2[0].GetString());
+ delete result2;
+ }
+ }
+ else
+ SendGMTicketGetTicket(0x0A,0);
+ }
+}
+
+void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ std::string ticketText;
+ recv_data >> ticketText;
+
+ CharacterDatabase.escape_string(ticketText);
+ CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", ticketText.c_str(), _player->GetGUIDLow());
+}
+
+void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/ )
+{
+ uint32 guid = GetPlayer()->GetGUIDLow();
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1",guid);
+
+ WorldPacket data( SMSG_GMTICKET_DELETETICKET, 4 );
+ data << uint32(9);
+ SendPacket( &data );
+
+ SendGMTicketGetTicket(0x0A, 0);
+}
+
+void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4);
+
+ uint32 map;
+ float x, y, z;
+ std::string ticketText = "";
+ uint32 unk1, unk2;
+
+ recv_data >> map >> x >> y >> z; // last check 2.4.3
+ recv_data >> ticketText;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,4*4+(ticketText.size()+1)+2*4);
+
+ recv_data >> unk1 >> unk2;
+ // note: the packet might contain more data, but the exact structure of that is unknown
+
+ sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s, unk1 %u, unk2 %u", map, x, y, z, ticketText.c_str(), unk1, unk2);
+
+ CharacterDatabase.escape_string(ticketText);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM character_ticket WHERE guid = '%u'", _player->GetGUIDLow());
+
+ if (result)
+ {
+ int cnt;
+ Field *fields = result->Fetch();
+ cnt = fields[0].GetUInt32();
+ delete result;
+
+ if ( cnt > 0 )
+ {
+ WorldPacket data( SMSG_GMTICKET_CREATE, 4 );
+ data << uint32(1);
+ SendPacket( &data );
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_ticket (guid,ticket_text) VALUES ('%u', '%s')", _player->GetGUIDLow(), ticketText.c_str());
+
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ SendPacket( &data );
+
+ data.Initialize( SMSG_GMTICKET_CREATE, 4 );
+ data << uint32(2);
+ SendPacket( &data );
+ DEBUG_LOG("update the ticket\n");
+
+ //TODO: Guard player map
+ HashMapHolder<Player>::MapType &m = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if(itr->second->GetSession()->GetSecurity() >= SEC_GAMEMASTER && itr->second->isAcceptTickets())
+ ChatHandler(itr->second).PSendSysMessage(LANG_COMMAND_TICKETNEW,GetPlayer()->GetName());
+ }
+ }
+ }
+}
+
+void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data( SMSG_GMTICKET_SYSTEMSTATUS,4 );
+ data << uint32(1); // we can also disable ticket system by sending 0 value
+
+ SendPacket( &data );
+}
+
+void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data)
+{
+ // GM survey is shown after SMSG_GM_TICKET_STATUS_UPDATE with status = 3
+ CHECK_PACKET_SIZE(recv_data,4+4);
+ uint32 x;
+ recv_data >> x; // answer range? (6 = 0-5?)
+ sLog.outDebug("SURVEY: X = %u", x);
+
+ uint8 result[10];
+ memset(result, 0, sizeof(result));
+ for( int i = 0; i < 10; ++i)
+ {
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+4);
+ uint32 questionID;
+ recv_data >> questionID; // GMSurveyQuestions.dbc
+ if (!questionID)
+ break;
+
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1+1);
+ uint8 value;
+ std::string unk_text;
+ recv_data >> value; // answer
+ recv_data >> unk_text; // always empty?
+
+ result[i] = value;
+ sLog.outDebug("SURVEY: ID %u, value %u, text %s", questionID, value, unk_text.c_str());
+ }
+
+ CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1);
+ std::string comment;
+ recv_data >> comment; // addional comment
+ sLog.outDebug("SURVEY: comment %s", comment.c_str());
+
+ // TODO: chart this data in some way
+}
+
+void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
+{
+ // this opcode can be used in two ways: Either set explicit new status or toggle old status
+ if(recv_data.size() == 1)
+ {
+ bool newPvPStatus;
+ recv_data >> newPvPStatus;
+ GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus);
+ }
+ else
+ {
+ GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
+ }
+
+ if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
+ {
+ if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0)
+ GetPlayer()->UpdatePvP(true, true);
+ }
+ else
+ {
+ if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP())
+ GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off
+ }
+}
+
+void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 newZone;
+ recv_data >> newZone;
+
+ sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone);
+
+ if(newZone != _player->GetZoneId())
+ GetPlayer()->SendInitWorldStates(); // only if really enters to new zone, not just area change, works strange...
+
+ GetPlayer()->UpdateZone(newZone);
+}
+
+void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data )
+{
+ // When this packet send?
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid ;
+ recv_data >> guid;
+
+ _player->SetUInt32Value(UNIT_FIELD_TARGET,guid);
+
+ // update reputation list if need
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
+ if(!unit)
+ return;
+
+ _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
+}
+
+void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ _player->SetSelection(guid);
+
+ // update reputation list if need
+ Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
+ if(!unit)
+ return;
+
+ _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
+}
+
+void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug( "WORLD: Received CMSG_STAND_STATE_CHANGE" );
+ uint8 animstate;
+ recv_data >> animstate;
+
+ _player->SetStandState(animstate);
+}
+
+void WorldSession::HandleFriendListOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+ sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" );
+ uint32 unk;
+ recv_data >> unk;
+ sLog.outDebug("unk value is %u", unk);
+ _player->GetSocial()->SendSocialList();
+}
+
+void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" );
+
+ std::string friendName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
+ std::string friendNote;
+ FriendsResult friendResult = FRIEND_NOT_FOUND;
+ Player *pFriend = NULL;
+ uint64 friendGuid = 0;
+
+ recv_data >> friendName;
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, (friendName.size()+1)+1);
+
+ recv_data >> friendNote;
+
+ if(!normalizePlayerName(friendName))
+ return;
+
+ CharacterDatabase.escape_string(friendName); // prevent SQL injection - normal name don't must changed by this call
+
+ sLog.outDebug( "WORLD: %s asked to add friend : '%s'",
+ GetPlayer()->GetName(), friendName.c_str() );
+
+ friendGuid = objmgr.GetPlayerGUIDByName(friendName);
+
+ if(friendGuid)
+ {
+ pFriend = ObjectAccessor::FindPlayer(friendGuid);
+ if(pFriend==GetPlayer())
+ friendResult = FRIEND_SELF;
+ else if(GetPlayer()->GetTeam()!=objmgr.GetPlayerTeamByGUID(friendGuid) && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && GetSecurity() < SEC_MODERATOR)
+ friendResult = FRIEND_ENEMY;
+ else if(GetPlayer()->GetSocial()->HasFriend(GUID_LOPART(friendGuid)))
+ friendResult = FRIEND_ALREADY;
+ }
+
+ if (friendGuid && friendResult==FRIEND_NOT_FOUND)
+ {
+ if( pFriend && pFriend->IsInWorld() && pFriend->IsVisibleGloballyFor(GetPlayer()))
+ friendResult = FRIEND_ADDED_ONLINE;
+ else
+ friendResult = FRIEND_ADDED_OFFLINE;
+
+ if(!_player->GetSocial()->AddToSocialList(GUID_LOPART(friendGuid), false))
+ {
+ friendResult = FRIEND_LIST_FULL;
+ sLog.outDebug( "WORLD: %s's friend list is full.", GetPlayer()->GetName());
+ }
+
+ _player->GetSocial()->SetFriendNote(GUID_LOPART(friendGuid), friendNote);
+
+ sLog.outDebug( "WORLD: %s Guid found '%u'.", friendName.c_str(), GUID_LOPART(friendGuid));
+ }
+ else if(friendResult==FRIEND_ALREADY)
+ {
+ sLog.outDebug( "WORLD: %s Guid Already a Friend.", friendName.c_str() );
+ }
+ else if(friendResult==FRIEND_SELF)
+ {
+ sLog.outDebug( "WORLD: %s Guid can't add himself.", friendName.c_str() );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: %s Guid not found.", friendName.c_str() );
+ }
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), friendResult, GUID_LOPART(friendGuid), friendName, false);
+
+ sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 FriendGUID;
+
+ sLog.outDebug( "WORLD: Received CMSG_DEL_FRIEND" );
+
+ recv_data >> FriendGUID;
+
+ _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(FriendGUID), false);
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_REMOVED, GUID_LOPART(FriendGUID), "", false);
+
+ sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleAddIgnoreOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" );
+
+ std::string IgnoreName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
+ FriendsResult ignoreResult = FRIEND_IGNORE_NOT_FOUND;
+ uint64 IgnoreGuid = 0;
+
+ recv_data >> IgnoreName;
+
+ if(!normalizePlayerName(IgnoreName))
+ return;
+
+ CharacterDatabase.escape_string(IgnoreName); // prevent SQL injection - normal name don't must changed by this call
+
+ sLog.outDebug( "WORLD: %s asked to Ignore: '%s'",
+ GetPlayer()->GetName(), IgnoreName.c_str() );
+
+ IgnoreGuid = objmgr.GetPlayerGUIDByName(IgnoreName);
+
+ if(IgnoreGuid)
+ {
+ if(IgnoreGuid==GetPlayer()->GetGUID())
+ ignoreResult = FRIEND_IGNORE_SELF;
+ else
+ {
+ if( GetPlayer()->GetSocial()->HasIgnore(GUID_LOPART(IgnoreGuid)) )
+ ignoreResult = FRIEND_IGNORE_ALREADY;
+ }
+ }
+
+ if (IgnoreGuid && ignoreResult == FRIEND_IGNORE_NOT_FOUND)
+ {
+ ignoreResult = FRIEND_IGNORE_ADDED;
+
+ _player->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true);
+ }
+ else if(ignoreResult==FRIEND_IGNORE_ALREADY)
+ {
+ sLog.outDebug( "WORLD: %s Guid Already Ignored.", IgnoreName.c_str() );
+ }
+ else if(ignoreResult==FRIEND_IGNORE_SELF)
+ {
+ sLog.outDebug( "WORLD: %s Guid can't add himself.", IgnoreName.c_str() );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: %s Guid not found.", IgnoreName.c_str() );
+ }
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), "", false);
+
+ sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 IgnoreGUID;
+
+ sLog.outDebug( "WORLD: Received CMSG_DEL_IGNORE" );
+
+ recv_data >> IgnoreGUID;
+
+ _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(IgnoreGUID), true);
+
+ sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, GUID_LOPART(IgnoreGUID), "", false);
+
+ sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
+}
+
+void WorldSession::HandleSetFriendNoteOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+ uint64 guid;
+ std::string note;
+ recv_data >> guid >> note;
+ _player->GetSocial()->SetFriendNote(guid, note);
+}
+
+void WorldSession::HandleBugOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+1+4+1);
+
+ uint32 suggestion, contentlen;
+ std::string content;
+ uint32 typelen;
+ std::string type;
+
+ recv_data >> suggestion >> contentlen >> content;
+
+ //recheck
+ CHECK_PACKET_SIZE(recv_data,4+4+(content.size()+1)+4+1);
+
+ recv_data >> typelen >> type;
+
+ if( suggestion == 0 )
+ sLog.outDebug( "WORLD: Received CMSG_BUG [Bug Report]" );
+ else
+ sLog.outDebug( "WORLD: Received CMSG_BUG [Suggestion]" );
+
+ sLog.outDebug( type.c_str( ) );
+ sLog.outDebug( content.c_str( ) );
+
+ CharacterDatabase.escape_string(type);
+ CharacterDatabase.escape_string(content);
+ CharacterDatabase.PExecute ("INSERT INTO bugreport (type,content) VALUES('%s', '%s')", type.c_str( ), content.c_str( ));
+}
+
+void WorldSession::HandleCorpseReclaimOpcode(WorldPacket &recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE");
+ if (GetPlayer()->isAlive())
+ return;
+
+ // body not released yet
+ if(!GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ return;
+
+ Corpse *corpse = GetPlayer()->GetCorpse();
+
+ if (!corpse )
+ return;
+
+ // prevent resurrect before 30-sec delay after body release not finished
+ if(corpse->GetGhostTime() + GetPlayer()->GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP) > time(NULL))
+ return;
+
+ float dist = corpse->GetDistance2d(GetPlayer());
+ sLog.outDebug("Corpse 2D Distance: \t%f",dist);
+ if (dist > CORPSE_RECLAIM_RADIUS)
+ return;
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // resurrect
+ GetPlayer()->ResurrectPlayer(GetPlayer()->InBattleGround() ? 1.0f : 0.5f);
+
+ // spawn bones
+ GetPlayer()->SpawnCorpseBones();
+
+ GetPlayer()->SaveToDB();
+}
+
+void WorldSession::HandleResurrectResponseOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ sLog.outDetail("WORLD: Received CMSG_RESURRECT_RESPONSE");
+
+ if(GetPlayer()->isAlive())
+ return;
+
+ uint64 guid;
+ uint8 status;
+ recv_data >> guid;
+ recv_data >> status;
+
+ if(status == 0)
+ {
+ GetPlayer()->clearResurrectRequestData(); // reject
+ return;
+ }
+
+ if(!GetPlayer()->isRessurectRequestedBy(guid))
+ return;
+
+ GetPlayer()->ResurectUsingRequestData();
+ GetPlayer()->SaveToDB();
+}
+
+void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ sLog.outDebug("WORLD: Received CMSG_AREATRIGGER");
+
+ uint32 Trigger_ID;
+
+ recv_data >> Trigger_ID;
+ sLog.outDebug("Trigger ID:%u",Trigger_ID);
+
+ if(GetPlayer()->isInFlight())
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ if (GetPlayer()->GetMapId()!=atEntry->mapid)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (trigger map: %u player map: %u), ignore Area Trigger ID: %u", GetPlayer()->GetName(), atEntry->mapid, GetPlayer()->GetMapId(), GetPlayer()->GetGUIDLow(), Trigger_ID);
+ return;
+ }
+
+ // delta is safe radius
+ const float delta = 5.0f;
+ // check if player in the range of areatrigger
+ Player* pl = GetPlayer();
+
+ if (atEntry->radius > 0)
+ {
+ // if we have radius check it
+ float dist = pl->GetDistance(atEntry->x,atEntry->y,atEntry->z);
+ if(dist > atEntry->radius + delta)
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (radius: %f distance: %f), ignore Area Trigger ID: %u",
+ pl->GetName(), pl->GetGUIDLow(), atEntry->radius, dist, Trigger_ID);
+ return;
+ }
+ }
+ else
+ {
+ // we have only extent
+ float dx = pl->GetPositionX() - atEntry->x;
+ float dy = pl->GetPositionY() - atEntry->y;
+ float dz = pl->GetPositionZ() - atEntry->z;
+ double es = sin(atEntry->box_orientation);
+ double ec = cos(atEntry->box_orientation);
+ // calc rotated vector based on extent axis
+ double rotateDx = dx*ec - dy*es;
+ double rotateDy = dx*es + dy*ec;
+
+ if( (fabs(rotateDx) > atEntry->box_x/2 + delta) ||
+ (fabs(rotateDy) > atEntry->box_y/2 + delta) ||
+ (fabs(dz) > atEntry->box_z/2 + delta) )
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) too far (1/2 box X: %f 1/2 box Y: %u 1/2 box Z: %u rotate dX: %f rotate dY: %f dZ:%f), ignore Area Trigger ID: %u",
+ pl->GetName(), pl->GetGUIDLow(), atEntry->box_x/2, atEntry->box_y/2, atEntry->box_z/2, rotateDx, rotateDy, dz, Trigger_ID);
+ return;
+ }
+ }
+
+ if(Script->scriptAreaTrigger(GetPlayer(), atEntry))
+ return;
+
+ uint32 quest_id = objmgr.GetQuestForAreaTrigger( Trigger_ID );
+ if( quest_id && GetPlayer()->isAlive() && GetPlayer()->IsActiveQuest(quest_id) )
+ {
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( pQuest )
+ {
+ if(GetPlayer()->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
+ GetPlayer()->AreaExploredOrEventHappens( quest_id );
+ }
+ }
+
+ if(objmgr.IsTavernAreaTrigger(Trigger_ID))
+ {
+ // set resting flag we are in the inn
+ GetPlayer()->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ GetPlayer()->InnEnter(time(NULL), atEntry->mapid, atEntry->x, atEntry->y, atEntry->z);
+ GetPlayer()->SetRestType(REST_TYPE_IN_TAVERN);
+
+ if(sWorld.IsFFAPvPRealm())
+ GetPlayer()->RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ return;
+ }
+
+ if(GetPlayer()->InBattleGround())
+ {
+ BattleGround* bg = GetPlayer()->GetBattleGround();
+ if(bg)
+ if(bg->GetStatus() == STATUS_IN_PROGRESS)
+ bg->HandleAreaTrigger(GetPlayer(), Trigger_ID);
+
+ return;
+ }
+
+ // NULL if all values default (non teleport trigger)
+ AreaTrigger const* at = objmgr.GetAreaTrigger(Trigger_ID);
+ if(!at)
+ return;
+
+ if(!GetPlayer()->isGameMaster())
+ {
+ uint32 missingLevel = 0;
+ if(GetPlayer()->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
+ missingLevel = at->requiredLevel;
+
+ // must have one or the other, report the first one that's missing
+ uint32 missingItem = 0;
+ if(at->requiredItem)
+ {
+ if(!GetPlayer()->HasItemCount(at->requiredItem, 1) &&
+ (!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1)))
+ missingItem = at->requiredItem;
+ }
+ else if(at->requiredItem2 && !GetPlayer()->HasItemCount(at->requiredItem2, 1))
+ missingItem = at->requiredItem2;
+
+ uint32 missingKey = 0;
+ if(GetPlayer()->GetDifficulty() == DIFFICULTY_HEROIC)
+ {
+ if(at->heroicKey)
+ {
+ if(!GetPlayer()->HasItemCount(at->heroicKey, 1) &&
+ (!at->heroicKey2 || !GetPlayer()->HasItemCount(at->heroicKey2, 1)))
+ missingKey = at->heroicKey;
+ }
+ else if(at->heroicKey2 && !GetPlayer()->HasItemCount(at->heroicKey2, 1))
+ missingKey = at->heroicKey2;
+ }
+
+ uint32 missingQuest = 0;
+ if(at->requiredQuest && !GetPlayer()->GetQuestRewardStatus(at->requiredQuest))
+ missingQuest = at->requiredQuest;
+
+ if(missingLevel || missingItem || missingKey || missingQuest)
+ {
+ // TODO: all this is probably wrong
+ if(missingItem)
+ SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED_AND_ITEM), at->requiredLevel, objmgr.GetItemPrototype(missingItem)->Name1);
+ else if(missingKey)
+ GetPlayer()->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY2);
+ else if(missingQuest)
+ SendAreaTriggerMessage(at->requiredFailedText.c_str());
+ else if(missingLevel)
+ SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), missingLevel);
+ return;
+ }
+ }
+
+ GetPlayer()->TeleportTo(at->target_mapId,at->target_X,at->target_Y,at->target_Z,at->target_Orientation,TELE_TO_NOT_LEAVE_TRANSPORT);
+}
+
+void WorldSession::HandleUpdateAccountData(WorldPacket &/*recv_data*/)
+{
+ sLog.outDetail("WORLD: Received CMSG_UPDATE_ACCOUNT_DATA");
+ //recv_data.hexlike();
+}
+
+void WorldSession::HandleRequestAccountData(WorldPacket& /*recv_data*/)
+{
+ sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA");
+ //recv_data.hexlike();
+}
+
+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)
+ {
+ 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_SPELL)
+ {
+ sLog.outDetail( "MISC: Added Action %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);
+ }
+ else
+ sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button );
+ }
+}
+
+void WorldSession::HandleCompleteCinema( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Player is watching cinema" );
+}
+
+void WorldSession::HandleNextCinematicCamera( WorldPacket & /*recv_data*/ )
+{
+ DEBUG_LOG( "WORLD: Which movie to play" );
+}
+
+void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & /*recv_data*/ )
+{
+ /* WorldSession::Update( getMSTime() );*/
+ DEBUG_LOG( "WORLD: Time Lag/Synchronization Resent/Update" );
+
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+4);
+ uint64 guid;
+ uint32 time_skipped;
+ recv_data >> guid;
+ recv_data >> time_skipped;
+ sLog.outDebug( "WORLD: CMSG_MOVE_TIME_SKIPPED" );
+
+ /// TODO
+ must be need use in mangos
+ We substract server Lags to move time ( AntiLags )
+ for exmaple
+ GetPlayer()->ModifyLastMoveTime( -int32(time_skipped) );
+ */
+}
+
+void WorldSession::HandleFeatherFallAck(WorldPacket &/*recv_data*/)
+{
+ DEBUG_LOG("WORLD: CMSG_MOVE_FEATHER_FALL_ACK");
+}
+
+void WorldSession::HandleMoveUnRootAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
+
+ sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK" );
+ recv_data.hexlike();
+ uint64 guid;
+ uint64 unknown1;
+ uint32 unknown2;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> guid;
+ recv_data >> unknown1;
+ recv_data >> unknown2;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation;
+
+ // TODO for later may be we can use for anticheat
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("unknown1 " I64FMTD,unknown1);
+ DEBUG_LOG("unknown2 %u",unknown2);
+ DEBUG_LOG("X %f",PositionX);
+ DEBUG_LOG("Y %f",PositionY);
+ DEBUG_LOG("Z %f",PositionZ);
+ DEBUG_LOG("O %f",Orientation);
+ */
+}
+
+void WorldSession::HandleMoveRootAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
+
+ sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_ROOT_ACK" );
+ recv_data.hexlike();
+ uint64 guid;
+ uint64 unknown1;
+ uint32 unknown2;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> guid;
+ recv_data >> unknown1;
+ recv_data >> unknown2;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation;
+
+ // for later may be we can use for anticheat
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("unknown1 " I64FMTD,unknown1);
+ DEBUG_LOG("unknown1 %u",unknown2);
+ DEBUG_LOG("X %f",PositionX);
+ DEBUG_LOG("Y %f",PositionY);
+ DEBUG_LOG("Z %f",PositionZ);
+ DEBUG_LOG("O %f",Orientation);
+ */
+}
+
+void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
+ uint64 guid;
+ uint32 flags, time;
+
+ recv_data >> guid;
+ recv_data >> flags >> time;
+ DEBUG_LOG("Guid " I64FMTD,guid);
+ DEBUG_LOG("Flags %u, time %u",flags, time/1000);
+ */
+}
+
+void WorldSession::HandleSetActionBar(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ uint8 ActionBar;
+
+ recv_data >> ActionBar;
+
+ if(!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED)
+ {
+ if(ActionBar!=0)
+ sLog.outError("WorldSession::HandleSetActionBar in not logged state with value: %u, ignored",uint32(ActionBar));
+ return;
+ }
+
+ GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, ActionBar);
+}
+
+void WorldSession::HandleWardenDataOpcode(WorldPacket& /*recv_data*/)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ uint8 tmp;
+ recv_data >> tmp;
+ sLog.outDebug("Received opcode CMSG_WARDEN_DATA, not resolve.uint8 = %u",tmp);
+ */
+}
+
+void WorldSession::HandlePlayedTime(WorldPacket& /*recv_data*/)
+{
+ uint32 TotalTimePlayed = GetPlayer()->GetTotalPlayedTime();
+ uint32 LevelPlayedTime = GetPlayer()->GetLevelPlayedTime();
+
+ WorldPacket data(SMSG_PLAYED_TIME, 8);
+ data << TotalTimePlayed;
+ data << LevelPlayedTime;
+ SendPacket(&data);
+}
+
+void WorldSession::HandleInspectOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+ DEBUG_LOG("Inspected guid is " I64FMTD, guid);
+
+ _player->SetSelection(guid);
+
+ Player *plr = objmgr.GetPlayer(guid);
+ if(!plr) // wrong player
+ return;
+
+ uint32 talent_points = 0x3D;
+ uint32 guid_size = plr->GetPackGUID().size();
+ WorldPacket data(SMSG_INSPECT_TALENT, 4+talent_points);
+ data.append(plr->GetPackGUID());
+ data << uint32(talent_points);
+
+ // fill by 0 talents array
+ for(uint32 i = 0; i < talent_points; ++i)
+ data << uint8(0);
+
+ if(sWorld.getConfig(CONFIG_TALENTS_INSPECTING) || _player->isGameMaster())
+ {
+ // find class talent tabs (all players have 3 talent tabs)
+ uint32 const* talentTabIds = GetTalentTabPages(plr->getClass());
+
+ uint32 talentTabPos = 0; // pos of first talent rank in tab including all prev tabs
+ for(uint32 i = 0; i < 3; ++i)
+ {
+ uint32 talentTabId = talentTabIds[i];
+
+ // fill by real data
+ for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
+ {
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if(!talentInfo)
+ continue;
+
+ // skip another tab talents
+ if(talentInfo->TalentTab != talentTabId)
+ continue;
+
+ // find talent rank
+ uint32 curtalent_maxrank = 0;
+ for(uint32 k = 5; k > 0; --k)
+ {
+ if(talentInfo->RankID[k-1] && plr->HasSpell(talentInfo->RankID[k-1]))
+ {
+ curtalent_maxrank = k;
+ break;
+ }
+ }
+
+ // not learned talent
+ if(!curtalent_maxrank)
+ continue;
+
+ // 1 rank talent bit index
+ uint32 curtalent_index = talentTabPos + GetTalentInspectBitPosInTab(talentId);
+
+ uint32 curtalent_rank_index = curtalent_index+curtalent_maxrank-1;
+
+ // slot/offset in 7-bit bytes
+ uint32 curtalent_rank_slot7 = curtalent_rank_index / 7;
+ uint32 curtalent_rank_offset7 = curtalent_rank_index % 7;
+
+ // rank pos with skipped 8 bit
+ uint32 curtalent_rank_index2 = curtalent_rank_slot7 * 8 + curtalent_rank_offset7;
+
+ // slot/offset in 8-bit bytes with skipped high bit
+ uint32 curtalent_rank_slot = curtalent_rank_index2 / 8;
+ uint32 curtalent_rank_offset = curtalent_rank_index2 % 8;
+
+ // apply mask
+ uint32 val = data.read<uint8>(guid_size + 4 + curtalent_rank_slot);
+ val |= (1 << curtalent_rank_offset);
+ data.put<uint8>(guid_size + 4 + curtalent_rank_slot, val & 0xFF);
+ }
+
+ talentTabPos += GetTalentTabInspectBitSize(talentTabId);
+ }
+ }
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Player *player = objmgr.GetPlayer(guid);
+
+ if(!player)
+ {
+ sLog.outError("InspectHonorStats: WTF, player not found...");
+ return;
+ }
+
+ WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4);
+ data << uint64(player->GetGUID());
+ data << uint8(player->GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
+ data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
+ SendPacket(&data);
+}
+
+void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4+4+4+4+4+4);
+
+ // write in client console: worldport 469 452 6454 2536 180 or /console worldport 469 452 6454 2536 180
+ // Received opcode CMSG_WORLD_TELEPORT
+ // Time is ***, map=469, x=452.000000, y=6454.000000, z=2536.000000, orient=3.141593
+
+ //sLog.outDebug("Received opcode CMSG_WORLD_TELEPORT");
+
+ if(GetPlayer()->isInFlight())
+ {
+ sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore worldport command.",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow());
+ return;
+ }
+
+ uint32 time;
+ uint32 mapid;
+ float PositionX;
+ float PositionY;
+ float PositionZ;
+ float Orientation;
+
+ recv_data >> time; // time in m.sec.
+ recv_data >> mapid;
+ recv_data >> PositionX;
+ recv_data >> PositionY;
+ recv_data >> PositionZ;
+ recv_data >> Orientation; // o (3.141593 = 180 degrees)
+ DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation);
+
+ if (GetSecurity() >= SEC_ADMINISTRATOR)
+ GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation);
+ else
+ SendNotification("You do not have permission to perform that function");
+ sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName());
+}
+
+void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ sLog.outDebug("Received opcode CMSG_WHOIS");
+ std::string charname, acc, email, lastip, msg;
+ recv_data >> charname;
+
+ if (GetSecurity() < SEC_ADMINISTRATOR)
+ {
+ SendNotification("You do not have permission to perform that function");
+ return;
+ }
+
+ if(charname.empty())
+ {
+ SendNotification("Please provide character name");
+ return;
+ }
+
+ uint32 accid;
+ Field *fields;
+
+ Player *plr = objmgr.GetPlayer(charname.c_str());
+
+ if(plr)
+ accid = plr->GetSession()->GetAccountId();
+ else
+ {
+ SendNotification("Player %s not found or offline", charname.c_str());
+ return;
+ }
+
+ if(!accid)
+ {
+ SendNotification("Account for character %s not found", charname.c_str());
+ return;
+ }
+
+ QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid);
+ if(result)
+ {
+ fields = result->Fetch();
+ acc = fields[0].GetCppString();
+ if(acc.empty())
+ acc = "Unknown";
+ email = fields[1].GetCppString();
+ if(email.empty())
+ email = "Unknown";
+ lastip = fields[2].GetCppString();
+ if(lastip.empty())
+ lastip = "Unknown";
+ msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip;
+
+ WorldPacket data(SMSG_WHOIS, msg.size()+1);
+ data << msg;
+ _player->GetSession()->SendPacket(&data);
+ }
+ else
+ SendNotification("Account for character %s not found", charname.c_str());
+
+ delete result;
+ sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str());
+}
+
+void WorldSession::HandleReportSpamOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1+8);
+ sLog.outDebug("WORLD: CMSG_REPORT_SPAM");
+ recv_data.hexlike();
+
+ uint8 spam_type; // 0 - mail, 1 - chat
+ uint64 spammer_guid;
+ uint32 unk1, unk2, unk3, unk4 = 0;
+ std::string description = "";
+ recv_data >> spam_type; // unk 0x01 const, may be spam type (mail/chat)
+ recv_data >> spammer_guid; // player guid
+ switch(spam_type)
+ {
+ case 0:
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4);
+ recv_data >> unk1; // const 0
+ recv_data >> unk2; // probably mail id
+ recv_data >> unk3; // const 0
+ break;
+ case 1:
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4+1);
+ recv_data >> unk1; // probably language
+ recv_data >> unk2; // message type?
+ recv_data >> unk3; // probably channel id
+ recv_data >> unk4; // unk random value
+ recv_data >> description; // spam description string (messagetype, channel name, player name, message)
+ break;
+ }
+
+ // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam.
+ // if it's mail spam - ALL mails from this spammer automatically removed by client
+
+ // Complaint Received message
+ WorldPacket data(SMSG_COMPLAIN_RESULT, 1);
+ data << uint8(0);
+ SendPacket(&data);
+
+ sLog.outDebug("REPORT SPAM: type %u, guid %u, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", spam_type, GUID_LOPART(spammer_guid), unk1, unk2, unk3, unk4, description.c_str());
+}
+
+void WorldSession::HandleRealmStateRequestOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("CMSG_REALM_SPLIT");
+
+ uint32 unk;
+ std::string split_date = "01/01/01";
+ recv_data >> unk;
+
+ WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1);
+ data << unk;
+ data << uint32(0x00000000); // realm split state
+ // split states:
+ // 0x0 realm normal
+ // 0x1 realm split
+ // 0x2 realm split pending
+ data << split_date;
+ SendPacket(&data);
+ //sLog.outDebug("response sent %u", unk);
+}
+
+void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ sLog.outDebug("WORLD: CMSG_FAR_SIGHT");
+ //recv_data.hexlike();
+
+ uint8 unk;
+ recv_data >> unk;
+
+ switch(unk)
+ {
+ case 0:
+ //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0)
+ //SendPacket(&data);
+ //_player->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ sLog.outDebug("Removed FarSight from player %u", _player->GetGUIDLow());
+ break;
+ case 1:
+ sLog.outDebug("Added FarSight " I64FMTD " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow());
+ break;
+ }
+}
+
+void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("CMSG_SET_TITLE");
+
+ int32 title;
+ recv_data >> title;
+
+ // -1 at none
+ if(title > 0 && title < 64)
+ {
+ if(!GetPlayer()->HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << title))
+ return;
+ }
+ else
+ title = 0;
+
+ GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title);
+}
+
+void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4+4);
+
+ sLog.outDebug("CMSG_ALLOW_MOVE_ACK");
+
+ uint32 counter, time_;
+ recv_data >> counter >> time_;
+
+ // time_ seems always more than getMSTime()
+ uint32 diff = getMSTimeDiff(getMSTime(),time_);
+
+ sLog.outDebug("response sent: counter %u, time %u (HEX: %X), ms. time %u, diff %u", counter, time_, time_, getMSTime(), diff);
+}
+
+void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_RESET_INSTANCES");
+ Group *pGroup = _player->GetGroup();
+ if(pGroup)
+ {
+ if(pGroup->IsLeader(_player->GetGUID()))
+ pGroup->ResetInstances(INSTANCE_RESET_ALL, _player);
+ }
+ else
+ _player->ResetInstances(INSTANCE_RESET_ALL);
+}
+
+void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY");
+
+ uint32 mode;
+ recv_data >> mode;
+
+ if(mode == _player->GetDifficulty())
+ return;
+
+ if(mode > DIFFICULTY_HEROIC)
+ {
+ sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUIDLow(), mode);
+ return;
+ }
+
+ // cannot reset while in an instance
+ Map *map = _player->GetMap();
+ if(map && map->IsDungeon())
+ {
+ sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow());
+ return;
+ }
+
+ if(_player->getLevel() < LEVELREQUIREMENT_HEROIC)
+ return;
+ Group *pGroup = _player->GetGroup();
+ if(pGroup)
+ {
+ if(pGroup->IsLeader(_player->GetGUID()))
+ {
+ // the difficulty is set even if the instances can't be reset
+ //_player->SendDungeonDifficulty(true);
+ pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, _player);
+ pGroup->SetDifficulty(mode);
+ }
+ }
+ else
+ {
+ _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY);
+ _player->SetDifficulty(mode);
+ }
+}
+
+void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode());
+ recv_data.hexlike();
+ /*
+ New Unknown Opcode 837
+ STORAGE_SIZE: 60
+ 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00
+ 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42
+ BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF
+ 23 91 26 3F 00 00 60 41 | 00 00 00 00
+
+ New Unknown Opcode 837
+ STORAGE_SIZE: 44
+ 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
+ 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42
+ BA B8 1B 40 CE 06 00 00 | 00 00 80 3F
+ */
+}
+
+void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA");
+ //recv_data.hexlike();
+
+ //If player is not mounted, so go out :)
+ if (!_player->IsMounted()) // not blizz like; no any messages on blizz
+ {
+ ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED);
+ return;
+ }
+
+ if(_player->isInFlight()) // not blizz like; no any messages on blizz
+ {
+ ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT);
+ return;
+ }
+
+ _player->Unmount();
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+}
+
+void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 8+4+4);
+
+ // fly mode on/off
+ sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
+ //recv_data.hexlike();
+
+ uint64 guid;
+ uint32 unk;
+ uint32 flags;
+
+ recv_data >> guid >> unk >> flags;
+
+ _player->SetUnitMovementFlags(flags);
+ /*
+ on:
+ 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
+ 85 4E A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
+ 78 15 94 40 39 03 00 00 | 00 00 80 3F
+ off:
+ 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
+ 10 FD A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
+ 78 15 94 40 39 03 00 00 | 00 00 00 00
+ */
+}
+
+void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */)
+{
+ /*
+ sLog.outDebug("WORLD: CMSG_REQUEST_PET_INFO");
+ recv_data.hexlike();
+ */
+}
+
+void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 1);
+
+ uint8 mode;
+ recv_data >> mode;
+
+ sLog.outDebug("Client used \"/timetest %d\" command", mode);
+}
diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp
new file mode 100644
index 00000000000..cf910bf50b2
--- /dev/null
+++ b/src/game/MotionMaster.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "MotionMaster.h"
+#include "CreatureAISelector.h"
+#include "Creature.h"
+#include "Traveller.h"
+
+#include "ConfusedMovementGenerator.h"
+#include "FleeingMovementGenerator.h"
+#include "HomeMovementGenerator.h"
+#include "IdleMovementGenerator.h"
+#include "PointMovementGenerator.h"
+#include "TargetedMovementGenerator.h"
+#include "WaypointMovementGenerator.h"
+
+#include <cassert>
+
+inline bool isStatic(MovementGenerator *mv)
+{
+ return (mv == &si_idleMovement);
+}
+
+void
+MotionMaster::Initialize()
+{
+ // clear ALL movement generators (including default)
+ while(!empty())
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+
+ // set new default movement generator
+ if(i_owner->GetTypeId() == TYPEID_UNIT)
+ {
+ MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner);
+ push( movement == NULL ? &si_idleMovement : movement );
+ top()->Initialize(*i_owner);
+ }
+ else
+ push(&si_idleMovement);
+}
+
+MotionMaster::~MotionMaster()
+{
+ // clear ALL movement generators (including default)
+ while(!empty())
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+}
+
+void
+MotionMaster::UpdateMotion(const uint32 &diff)
+{
+ if( i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) )
+ return;
+ assert( !empty() );
+ if (!top()->Update(*i_owner, diff))
+ MovementExpired();
+}
+
+void
+MotionMaster::Clear(bool reset)
+{
+ while( !empty() && size() > 1 )
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if( !isStatic( curr ) )
+ delete curr;
+ }
+
+ if (reset)
+ {
+ assert( !empty() );
+ top()->Reset(*i_owner);
+ }
+}
+
+void
+MotionMaster::MovementExpired(bool reset)
+{
+ if( empty() || size() == 1 )
+ return;
+
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+
+ if( !isStatic(curr) )
+ delete curr;
+
+ assert( !empty() );
+ while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ {
+ // Should check if target is still valid? If not valid it will crash.
+ curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ delete curr;
+ }
+ if( empty() )
+ Initialize();
+ if (reset) top()->Reset(*i_owner);
+}
+
+void MotionMaster::MoveIdle()
+{
+ if( empty() || !isStatic( top() ) )
+ push( &si_idleMovement );
+}
+
+void
+MotionMaster::MoveTargetedHome()
+{
+ if(i_owner->hasUnitState(UNIT_STAT_FLEEING))
+ return;
+
+ Clear(false);
+
+ if(i_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)i_owner)->GetCharmerOrOwnerGUID())
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow());
+ Mutate(new HomeMovementGenerator<Creature>());
+ }
+ else if(i_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)i_owner)->GetCharmerOrOwnerGUID())
+ {
+ sLog.outError("Pet or controlled creature (Entry: %u GUID: %u) attempt targeted home",
+ i_owner->GetEntry(), i_owner->GetGUIDLow() );
+ }
+ else
+ {
+ sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow() );
+ }
+}
+
+void
+MotionMaster::MoveConfused()
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) move confused", i_owner->GetGUIDLow() );
+ Mutate(new ConfusedMovementGenerator<Player>());
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) move confused",
+ i_owner->GetEntry(), i_owner->GetGUIDLow() );
+ Mutate(new ConfusedMovementGenerator<Creature>());
+ }
+}
+
+void
+MotionMaster::MoveChase(Unit* target, float dist, float angle)
+{
+ // ignore movement request if target not exist
+ if(!target)
+ return;
+
+ i_owner->clearUnitState(UNIT_STAT_FOLLOW);
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) chase to %s (GUID: %u)",
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle));
+ }
+}
+
+void
+MotionMaster::MoveFollow(Unit* target, float dist, float angle)
+{
+ Clear();
+
+ // ignore movement request if target not exist
+ if(!target)
+ return;
+
+ i_owner->addUnitState(UNIT_STAT_FOLLOW);
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) follow to %s (GUID: %u)", i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() );
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle));
+ }
+}
+
+void
+MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), id, x, y, z );
+ Mutate(new PointMovementGenerator<Player>(id,x,y,z));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), id, x, y, z );
+ Mutate(new PointMovementGenerator<Creature>(id,x,y,z));
+ }
+}
+
+void
+MotionMaster::MoveFleeing(Unit* enemy)
+{
+ if(!enemy)
+ return;
+
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) flee from %s (GUID: %u)", i_owner->GetGUIDLow(),
+ enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() );
+ Mutate(new FleeingMovementGenerator<Player>(enemy->GetGUID()));
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ enemy->GetTypeId()==TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId()==TYPEID_PLAYER ? enemy->GetGUIDLow() : ((Creature*)enemy)->GetDBTableGUIDLow() );
+ Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID()));
+ }
+}
+
+void
+MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) taxi to (Path %u node %u)", i_owner->GetGUIDLow(), path, pathnode);
+ FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(path,pathnode);
+ Mutate(mgen);
+ }
+ else
+ {
+ sLog.outError("Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), path, pathnode );
+ }
+}
+
+void
+MotionMaster::MoveDistract(uint32 timer)
+{
+ if(i_owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) distracted (timer: %u)", i_owner->GetGUIDLow(), timer);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) (timer: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), timer);
+ }
+
+ DistractMovementGenerator* mgen = new DistractMovementGenerator(timer);
+ Mutate(mgen);
+}
+
+void MotionMaster::Mutate(MovementGenerator *m)
+{
+ if (!empty())
+ {
+ switch(top()->GetMovementGeneratorType())
+ {
+ // HomeMovement is not that important, delete it if meanwhile a new comes
+ case HOME_MOTION_TYPE:
+ // DistractMovement interrupted by any other movement
+ case DISTRACT_MOTION_TYPE:
+ MovementExpired(false);
+ }
+ }
+ m->Initialize(*i_owner);
+ push(m);
+}
+
+void MotionMaster::propagateSpeedChange()
+{
+ Impl::container_type::iterator it = Impl::c.begin();
+ for ( ;it != end(); ++it)
+ {
+ (*it)->unitSpeedChanged();
+ }
+}
+
+MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
+{
+ if(empty())
+ return IDLE_MOTION_TYPE;
+
+ return top()->GetMovementGeneratorType();
+}
+
+bool MotionMaster::GetDestination(float &x, float &y, float &z)
+{
+ if(empty())
+ return false;
+
+ return top()->GetDestination(x,y,z);
+}
diff --git a/src/game/MotionMaster.h b/src/game/MotionMaster.h
new file mode 100644
index 00000000000..f7a38e47618
--- /dev/null
+++ b/src/game/MotionMaster.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MOTIONMASTER_H
+#define MANGOS_MOTIONMASTER_H
+
+#include "Common.h"
+#include <stack>
+
+class MovementGenerator;
+class Unit;
+
+// Creature Entry ID used for waypoints show, visible only for GMs
+#define VISUAL_WAYPOINT 1
+
+// values 0 ... MAX_DB_MOTION_TYPE-1 used in DB
+enum MovementGeneratorType
+{
+ IDLE_MOTION_TYPE = 0, // IdleMovementGenerator.h
+ RANDOM_MOTION_TYPE = 1, // RandomMovementGenerator.h
+ WAYPOINT_MOTION_TYPE = 2, // WaypointMovementGenerator.h
+ MAX_DB_MOTION_TYPE = 3, // *** this and below motion types can't be set in DB.
+ ANIMAL_RANDOM_MOTION_TYPE = MAX_DB_MOTION_TYPE, // AnimalRandomMovementGenerator.h
+ CONFUSED_MOTION_TYPE = 4, // ConfusedMovementGenerator.h
+ TARGETED_MOTION_TYPE = 5, // TargetedMovementGenerator.h
+ HOME_MOTION_TYPE = 6, // HomeMovementGenerator.h
+ FLIGHT_MOTION_TYPE = 7, // WaypointMovementGenerator.h
+ POINT_MOTION_TYPE = 8, // PointMovementGenerator.h
+ FLEEING_MOTION_TYPE = 9, // FleeingMovementGenerator.h
+ DISTRACT_MOTION_TYPE = 10, // IdleMovementGenerator.h
+};
+
+class MANGOS_DLL_SPEC MotionMaster : private std::stack<MovementGenerator *>
+{
+ private:
+ typedef std::stack<MovementGenerator *> Impl;
+ public:
+
+ explicit MotionMaster(Unit *unit) : i_owner(unit) {}
+ ~MotionMaster();
+
+ void Initialize();
+
+ MovementGenerator* operator->(void) { return top(); }
+
+ using Impl::top;
+ using Impl::empty;
+
+ typedef Impl::container_type::const_iterator const_iterator;
+ const_iterator begin() const { return Impl::c.begin(); }
+ const_iterator end() const { return Impl::c.end(); }
+
+ void UpdateMotion(const uint32 &diff);
+ void Clear(bool reset = true);
+ void MovementExpired(bool reset = true);
+
+ void MoveIdle();
+ void MoveTargetedHome();
+ void MoveFollow(Unit* target, float dist, float angle);
+ void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
+ void MoveConfused();
+ void MoveFleeing(Unit* enemy);
+ void MovePoint(uint32 id, float x,float y,float z);
+ void MoveTaxiFlight(uint32 path, uint32 pathnode);
+ void MoveDistract(uint32 time);
+
+ MovementGeneratorType GetCurrentMovementGeneratorType() const;
+
+ void propagateSpeedChange();
+
+ bool GetDestination(float &x, float &y, float &z);
+ private:
+ void Mutate(MovementGenerator *m); // use Move* functions instead
+
+ Unit *i_owner;
+};
+#endif
diff --git a/src/game/MovementGenerator.cpp b/src/game/MovementGenerator.cpp
new file mode 100644
index 00000000000..b717f6cf443
--- /dev/null
+++ b/src/game/MovementGenerator.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "MovementGenerator.h"
+
+MovementGenerator::~MovementGenerator()
+{
+}
diff --git a/src/game/MovementGenerator.h b/src/game/MovementGenerator.h
new file mode 100644
index 00000000000..982de574c19
--- /dev/null
+++ b/src/game/MovementGenerator.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MOVEMENTGENERATOR_H
+#define MANGOS_MOVEMENTGENERATOR_H
+
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "Dynamic/ObjectRegistry.h"
+#include "Dynamic/FactoryHolder.h"
+#include "Common.h"
+#include "MotionMaster.h"
+
+class Unit;
+
+class MANGOS_DLL_SPEC MovementGenerator
+{
+ public:
+ virtual ~MovementGenerator();
+
+ virtual void Initialize(Unit &) = 0;
+ virtual void Finalize(Unit &) = 0;
+
+ virtual void Reset(Unit &) = 0;
+
+ virtual bool Update(Unit &, const uint32 &time_diff) = 0;
+
+ virtual MovementGeneratorType GetMovementGeneratorType() = 0;
+
+ virtual void unitSpeedChanged() { }
+
+ virtual bool GetDestination(float& /*x*/, float& /*y*/, float& /*z*/) const { return false; }
+};
+
+template<class T, class D>
+class MANGOS_DLL_SPEC MovementGeneratorMedium : public MovementGenerator
+{
+ public:
+ void Initialize(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Initialize(*((T*)&u));
+ }
+ void Finalize(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Finalize(*((T*)&u));
+ }
+ void Reset(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Reset(*((T*)&u));
+ }
+ bool Update(Unit &u, const uint32 &time_diff)
+ {
+ //u->AssertIsType<T>();
+ return (static_cast<D*>(this))->Update(*((T*)&u), time_diff);
+ }
+ public:
+ // will not link if not overridden in the generators
+ void Initialize(T &u);
+ void Finalize(T &u);
+ void Reset(T &u);
+ bool Update(T &u, const uint32 &time_diff);
+};
+
+struct SelectableMovement : public FactoryHolder<MovementGenerator,MovementGeneratorType>
+{
+ SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator,MovementGeneratorType>(mgt) {}
+};
+
+template<class REAL_MOVEMENT>
+struct MovementGeneratorFactory : public SelectableMovement
+{
+ MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}
+
+ MovementGenerator* Create(void *) const;
+};
+
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType> MovementGeneratorCreator;
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
+#endif
diff --git a/src/game/MovementGeneratorImpl.h b/src/game/MovementGeneratorImpl.h
new file mode 100644
index 00000000000..c3859a548f5
--- /dev/null
+++ b/src/game/MovementGeneratorImpl.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_MOVEMENTGENERATOR_IMPL_H
+#define MANGOS_MOVEMENTGENERATOR_IMPL_H
+
+#include "MovementGenerator.h"
+
+template<class MOVEMENT_GEN>
+inline MovementGenerator*
+MovementGeneratorFactory<MOVEMENT_GEN>::Create(void *data) const
+{
+ Creature* creature = reinterpret_cast<Creature *>(data);
+ return (new MOVEMENT_GEN(*creature));
+}
+#endif
diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp
new file mode 100644
index 00000000000..1efd4e3c3e7
--- /dev/null
+++ b/src/game/MovementHandler.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "Corpse.h"
+#include "Player.h"
+#include "MapManager.h"
+#include "Transports.h"
+#include "BattleGround.h"
+#include "WaypointMovementGenerator.h"
+#include "InstanceSaveMgr.h"
+
+void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: got MSG_MOVE_WORLDPORT_ACK." );
+ HandleMoveWorldportAckOpcode();
+}
+
+void WorldSession::HandleMoveWorldportAckOpcode()
+{
+ // get the teleport destination
+ WorldLocation &loc = GetPlayer()->GetTeleportDest();
+
+ // possible errors in the coordinate validity check
+ if(!MapManager::IsValidMapCoord(loc.mapid,loc.x,loc.y,loc.z,loc.o))
+ {
+ LogoutPlayer(false);
+ return;
+ }
+
+ // get the destination map entry, not the current one, this will fix homebind and reset greeting
+ MapEntry const* mEntry = sMapStore.LookupEntry(loc.mapid);
+ InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(loc.mapid);
+
+ // reset instance validity, except if going to an instance inside an instance
+ if(GetPlayer()->m_InstanceValid == false && !mInstance)
+ GetPlayer()->m_InstanceValid = true;
+
+ GetPlayer()->SetSemaphoreTeleport(false);
+
+ // relocate the player to the teleport destination
+ GetPlayer()->SetMapId(loc.mapid);
+ GetPlayer()->Relocate(loc.x, loc.y, loc.z, loc.o);
+
+ // 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);
+
+ // check this before Map::Add(player), because that will create the instance save!
+ bool reset_notify = (GetPlayer()->GetBoundInstance(GetPlayer()->GetMapId(), GetPlayer()->GetDifficulty()) == NULL);
+
+ 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(!MapManager::Instance().GetMap(GetPlayer()->GetMapId(), GetPlayer())->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.x, loc.y, loc.z, loc.o);
+ // teleport the player home
+ GetPlayer()->SetDontMove(false);
+ 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);
+ }
+ return;
+ }
+ GetPlayer()->SendInitialPacketsAfterAddToMap();
+
+ // flight fast teleport case
+ if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE)
+ {
+ if(!_player->InBattleGround())
+ {
+ // short preparations to continue flight
+ GetPlayer()->SetDontMove(false);
+ FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
+ flight->Initialize(*GetPlayer());
+ return;
+ }
+
+ // battleground state prepare, stop flight
+ GetPlayer()->GetMotionMaster()->MovementExpired();
+ GetPlayer()->m_taxi.ClearTaxiDestinations();
+ }
+
+ // resurrect character at enter into instance where his corpse exist after add to map
+ Corpse *corpse = GetPlayer()->GetCorpse();
+ if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId())
+ {
+ if( mEntry->IsDungeon() )
+ {
+ GetPlayer()->ResurrectPlayer(0.5f,false);
+ GetPlayer()->SpawnCorpseBones();
+ GetPlayer()->SaveToDB();
+ }
+ }
+
+ if(mEntry->IsRaid() && mInstance)
+ {
+ if(reset_notify)
+ {
+ uint32 timeleft = sInstanceSaveManager.GetResetTimeFor(GetPlayer()->GetMapId()) - time(NULL);
+ GetPlayer()->SendInstanceResetWarning(GetPlayer()->GetMapId(), timeleft); // greeting at the entrance of the resort raid instance
+ }
+ }
+
+ // mount allow check
+ if(!mEntry->IsMountAllowed())
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ // battleground state preper
+ if(_player->InBattleGround())
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(bg)
+ {
+ if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg
+ {
+ if(!bg->GetBgRaid(_player->GetTeam())) // first player joined
+ {
+ Group *group = new Group;
+ bg->SetBgRaid(_player->GetTeam(), group);
+ group->Create(_player->GetGUIDLow(), _player->GetName());
+ }
+ else // raid already exist
+ {
+ bg->GetBgRaid(_player->GetTeam())->AddMember(_player->GetGUID(), _player->GetName());
+ }
+ }
+ }
+ }
+
+ // honorless target
+ if(GetPlayer()->pvpInfo.inHostileArea)
+ GetPlayer()->CastSpell(GetPlayer(), 2479, true);
+
+ // resummon pet
+ if(GetPlayer()->m_temporaryUnsummonedPetNumber)
+ {
+ Pet* NewPet = new Pet;
+ if(!NewPet->LoadPetFromDB(GetPlayer(), 0, GetPlayer()->m_temporaryUnsummonedPetNumber, true))
+ delete NewPet;
+
+ GetPlayer()->m_temporaryUnsummonedPetNumber = 0;
+ }
+
+ GetPlayer()->SetDontMove(false);
+}
+
+void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4+1+4+4+4+4+4);
+
+ if(GetPlayer()->GetDontMove())
+ return;
+
+ /* extract packet */
+ MovementInfo movementInfo;
+ uint32 MovementFlags;
+
+ recv_data >> MovementFlags;
+ recv_data >> movementInfo.unk1;
+ recv_data >> movementInfo.time;
+ recv_data >> movementInfo.x;
+ recv_data >> movementInfo.y;
+ recv_data >> movementInfo.z;
+ recv_data >> movementInfo.o;
+
+ //Save movement flags
+ _player->SetUnitMovementFlags(MovementFlags);
+
+ if(MovementFlags & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4);
+
+ recv_data >> movementInfo.t_guid;
+ recv_data >> movementInfo.t_x;
+ recv_data >> movementInfo.t_y;
+ recv_data >> movementInfo.t_z;
+ recv_data >> movementInfo.t_o;
+ recv_data >> movementInfo.t_time;
+ }
+
+ if(MovementFlags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> movementInfo.s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up
+ }
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> movementInfo.fallTime; // duration of last jump (when in jump duration from jump begin to now)
+
+ if(MovementFlags & MOVEMENTFLAG_JUMPING)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4);
+
+ recv_data >> movementInfo.j_unk; // constant, but different when jumping in water and on land?
+ recv_data >> movementInfo.j_sinAngle; // sin of angle between orientation0 and players orientation
+ recv_data >> movementInfo.j_cosAngle; // cos of angle between orientation0 and players orientation
+ recv_data >> movementInfo.j_xyspeed; // speed of xy movement
+ }
+
+ if(MovementFlags & MOVEMENTFLAG_SPLINE)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> movementInfo.u_unk1; // unknown
+ }
+ /*----------------*/
+
+ 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());
+ KickPlayer();
+ return;
+ }
+
+ if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
+ return;
+
+ /* handle special cases */
+ if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ // transports size limited
+ // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
+ if( movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50 )
+ return;
+
+ if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y+movementInfo.t_y,
+ movementInfo.z+movementInfo.t_z, movementInfo.o+movementInfo.t_o) )
+ return;
+
+ // if we boarded a transport, add us to it
+ if (!GetPlayer()->m_transport)
+ {
+ // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
+ for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter)
+ {
+ if ((*iter)->GetGUID() == movementInfo.t_guid)
+ {
+ // unmount before boarding
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ GetPlayer()->m_transport = (*iter);
+ (*iter)->AddPassenger(GetPlayer());
+ break;
+ }
+ }
+ }
+ }
+ else if (GetPlayer()->m_transport) // if we were on a transport, leave
+ {
+ GetPlayer()->m_transport->RemovePassenger(GetPlayer());
+ GetPlayer()->m_transport = NULL;
+ movementInfo.t_x = 0.0f;
+ movementInfo.t_y = 0.0f;
+ movementInfo.t_z = 0.0f;
+ movementInfo.t_o = 0.0f;
+ movementInfo.t_time = 0;
+ }
+
+ // fall damage generation (ignore in flight case that can be triggred also at lags in moment teleportation to another map).
+ if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight())
+ {
+ Player *target = GetPlayer();
+
+ //Players with Feather Fall or low fall time, or physical immunity (charges used) are ignored
+ if (movementInfo.fallTime > 1100 && !target->isDead() && !target->isGameMaster() &&
+ !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) &&
+ !target->HasAuraType(SPELL_AURA_FLY) && !target->IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL,true) )
+ {
+ //Safe fall, fall time reduction
+ int32 safe_fall = target->GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
+ uint32 fall_time = (movementInfo.fallTime > (safe_fall*10)) ? movementInfo.fallTime - (safe_fall*10) : 0;
+
+ if(fall_time > 1100) //Prevent damage if fall time < 1100
+ {
+ //Fall Damage calculation
+ float fallperc = float(fall_time)/1100;
+ uint32 damage = (uint32)(((fallperc*fallperc -1) / 9 * target->GetMaxHealth())*sWorld.getRate(RATE_DAMAGE_FALL));
+
+ float height = movementInfo.z;
+ target->UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height);
+
+ if (damage > 0)
+ {
+ //Prevent fall damage from being more than the player maximum health
+ if (damage > target->GetMaxHealth())
+ damage = target->GetMaxHealth();
+
+ // Gust of Wind
+ if (target->GetDummyAura(43621))
+ damage = target->GetMaxHealth()/2;
+
+ target->EnvironmentalDamage(target->GetGUID(), DAMAGE_FALL, damage);
+ }
+
+ //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
+ DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, target->GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall);
+ }
+ }
+
+ //handle fall and logout at the same time (logout started before fall finished)
+ /* outdated and create problems with sit at stun sometime
+ if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE))
+ {
+ target->SetStandState(PLAYER_STATE_SIT);
+ // Can't move
+ WorldPacket data( SMSG_FORCE_MOVE_ROOT, 12 );
+ data.append(target->GetPackGUID());
+ data << (uint32)2;
+ SendPacket( &data );
+ }
+ */
+ }
+
+ if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater())
+ {
+ // now client not include swimming flag in case jumping under water
+ GetPlayer()->SetInWater( !GetPlayer()->IsInWater() || GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) );
+ }
+
+ /*----------------------*/
+
+ /* process position-change */
+ recv_data.put<uint32>(5, getMSTime()); // offset flags(4) + unk(1)
+ WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size()));
+ data.append(GetPlayer()->GetPackGUID());
+ data.append(recv_data.contents(), recv_data.size());
+ GetPlayer()->SendMessageToSet(&data, false);
+
+ GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
+ GetPlayer()->m_movementInfo = movementInfo;
+
+ if(GetPlayer()->isMovingOrTurning())
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if(movementInfo.z < -500.0f)
+ {
+ // NOTE: this is actually called many times while falling
+ // even after the player has been teleported away
+ // TODO: discard movement packets after the player is rooted
+ if(GetPlayer()->isAlive())
+ {
+ GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
+ // change the death state to CORPSE to prevent the death timer from
+ // starting in the next player update
+ GetPlayer()->KillPlayer();
+ GetPlayer()->BuildPlayerRepop();
+ }
+
+ // cancel the death timer here if started
+ GetPlayer()->RepopAtGraveyard();
+ }
+}
+
+void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+4+4+1+4+4+4+4+4);
+
+ /* extract packet */
+ uint64 guid;
+ uint8 unkB;
+ uint32 unk1, flags, time, fallTime;
+ float x, y, z, orientation;
+
+ uint64 t_GUID;
+ float t_x, t_y, t_z, t_o;
+ uint32 t_time;
+ float s_pitch;
+ float j_unk1, j_sinAngle, j_cosAngle, j_xyspeed;
+ float u_unk1;
+ float newspeed;
+
+ recv_data >> guid;
+
+ // now can skip not our packet
+ if(_player->GetGUID() != guid)
+ return;
+
+ // continue parse packet
+
+ recv_data >> unk1;
+ recv_data >> flags >> unkB >> time;
+ recv_data >> x >> y >> z >> orientation;
+ if (flags & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4);
+
+ recv_data >> t_GUID;
+ recv_data >> t_x >> t_y >> t_z >> t_o >> t_time;
+ }
+ if (flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up
+ }
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> fallTime; // duration of last jump (when in jump duration from jump begin to now)
+
+ if ((flags & MOVEMENTFLAG_JUMPING) || (flags & MOVEMENTFLAG_FALLING))
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4);
+
+ recv_data >> j_unk1; // ?constant, but different when jumping in water and on land?
+ recv_data >> j_sinAngle >> j_cosAngle; // sin + cos of angle between orientation0 and players orientation
+ recv_data >> j_xyspeed; // speed of xy movement
+ }
+
+ if(flags & MOVEMENTFLAG_SPLINE)
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> u_unk1; // unknown
+ }
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4);
+
+ recv_data >> newspeed;
+ /*----------------*/
+
+ // client ACK send one packet for mounted/run case and need skip all except last from its
+ // in other cases anti-cheat check can be fail in false case
+ UnitMoveType move_type;
+ UnitMoveType force_move_type;
+
+ static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "Walkback", "Swim", "Swimback", "Turn", "Fly", "Flyback" };
+
+ uint16 opcode = recv_data.GetOpcode();
+ switch(opcode)
+ {
+ case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
+ case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
+ case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_WALKBACK; force_move_type = MOVE_WALKBACK; break;
+ case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
+ case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIMBACK; force_move_type = MOVE_SWIMBACK; break;
+ case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN; force_move_type = MOVE_TURN; break;
+ case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLY; force_move_type = MOVE_FLY; break;
+ case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLYBACK; force_move_type = MOVE_FLYBACK; break;
+ default:
+ sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
+ return;
+ }
+
+ // skip all forced speed changes except last and unexpected
+ // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
+ if(_player->m_forced_speed_changes[force_move_type] > 0)
+ {
+ --_player->m_forced_speed_changes[force_move_type];
+ if(_player->m_forced_speed_changes[force_move_type] > 0)
+ return;
+ }
+
+ if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
+ {
+ if(_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
+ {
+ sLog.outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
+ move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
+ _player->SetSpeed(move_type,_player->GetSpeedRate(move_type),true);
+ }
+ else // must be lesser - cheating
+ {
+ sLog.outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),_player->GetSpeed(move_type), newspeed);
+ _player->GetSession()->KickPlayer();
+ }
+ }
+}
+
+void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
+
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement
+ data << uint32(0x00000000); // on blizz it increments periodically
+ SendPacket(&data);
+}
+
+void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/)
+{
+ //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM");
+
+ WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
+ data << uint64(GetPlayer()->GetGUID());
+
+ GetPlayer()->SendMessageToSet(&data, false);
+}
+
+void WorldSession::HandleMoveKnockBackAck( WorldPacket & /*recv_data*/ )
+{
+ // CHECK_PACKET_SIZE(recv_data,?);
+ sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
+ // Currently not used but maybe use later for recheck final player position
+ // (must be at call same as into "recv_data >> x >> y >> z >> orientation;"
+
+ /*
+ uint32 flags, time;
+ float x, y, z, orientation;
+ uint64 guid;
+ uint32 sequence;
+ uint32 ukn1;
+ float xdirection,ydirection,hspeed,vspeed;
+
+ recv_data >> guid;
+ recv_data >> sequence;
+ recv_data >> flags >> time;
+ recv_data >> x >> y >> z >> orientation;
+ recv_data >> ukn1; //unknown
+ recv_data >> vspeed >> xdirection >> ydirection >> hspeed;
+
+ // skip not personal message;
+ if(GetPlayer()->GetGUID()!=guid)
+ return;
+
+ // check code
+ */
+}
+
+void WorldSession::HandleMoveHoverAck( WorldPacket& /*recv_data*/ )
+{
+ sLog.outDebug("CMSG_MOVE_HOVER_ACK");
+}
+
+void WorldSession::HandleMoveWaterWalkAck(WorldPacket& /*recv_data*/)
+{
+ sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK");
+}
+
+void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+
+ if(!_player->isAlive() || _player->isInCombat() )
+ return;
+
+ uint64 summoner_guid;
+ bool agree;
+ recv_data >> summoner_guid;
+ recv_data >> agree;
+
+ _player->SummonIfPossible(agree);
+}
diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp
new file mode 100644
index 00000000000..5739f56e328
--- /dev/null
+++ b/src/game/NPCHandler.cpp
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "GossipDef.h"
+#include "SpellAuras.h"
+#include "UpdateMask.h"
+#include "ScriptCalls.h"
+#include "ObjectAccessor.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "Pet.h"
+#include "BattleGroundMgr.h"
+#include "BattleGround.h"
+#include "Guild.h"
+
+void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TABARDDESIGNER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTabardVendorActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendTabardVendorActivate(guid);
+}
+
+void WorldSession::SendTabardVendorActivate( uint64 guid )
+{
+ WorldPacket data( MSG_TABARDVENDOR_ACTIVATE, 8 );
+ data << guid;
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBankerActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ sLog.outDebug( "WORLD: Received CMSG_BANKER_ACTIVATE" );
+
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_BANKER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBankerActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendShowBank(guid);
+}
+
+void WorldSession::SendShowBank( uint64 guid )
+{
+ WorldPacket data( SMSG_SHOW_BANK, 8 );
+ data << guid;
+ SendPacket( &data );
+}
+
+void WorldSession::HandleTrainerListOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+ SendTrainerList( guid );
+}
+
+void WorldSession::SendTrainerList( uint64 guid )
+{
+ std::string str = GetMangosString(LANG_NPC_TAINER_HELLO);
+ SendTrainerList( guid, str );
+}
+
+void WorldSession::SendTrainerList( uint64 guid,std::string strTitle )
+{
+ sLog.outDebug( "WORLD: SendTrainerList" );
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TRAINER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: SendTrainerList - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // Lazy loading at first access
+ unit->LoadTrainerSpells();
+
+ // trainer list loaded at check;
+ if(!unit->isCanTrainingOf(_player,true))
+ return;
+
+ CreatureInfo const *ci = unit->GetCreatureInfo();
+
+ if (!ci)
+ {
+ sLog.outDebug( "WORLD: SendTrainerList - (%u) NO CREATUREINFO! (GUID: %u)", uint32(GUID_LOPART(guid)), guid );
+ return;
+ }
+
+ Creature::SpellsList const& trainer_spells = unit->GetTrainerSpells();
+
+ WorldPacket data( SMSG_TRAINER_LIST, 8+4+4+trainer_spells.size()*38 + strTitle.size()+1);
+ data << guid;
+ data << uint32(unit->GetTrainerType());
+
+ size_t count_pos = data.wpos();
+ data << uint32(trainer_spells.size());
+
+ // reputation discount
+ float fDiscountMod = _player->GetReputationPriceDiscount(unit);
+
+ uint32 count = 0;
+ for(Creature::SpellsList::const_iterator itr = trainer_spells.begin(); itr != trainer_spells.end(); ++itr)
+ {
+ if(!_player->IsSpellFitByClassAndRace(itr->spell->Id))
+ continue;
+
+ ++count;
+
+ bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(itr->spell->Id);
+
+ SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(itr->spell->Id);
+
+ data << uint32(itr->spell->Id);
+ data << uint8(_player->GetTrainerSpellState(&*itr));
+ data << uint32(floor(itr->spellcost * fDiscountMod));
+
+ data << uint32(primary_prof_first_rank ? 1 : 0); // primary prof. learn confirmation dialog
+ data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
+ data << uint8(itr->reqlevel ? itr->reqlevel : itr->spell->spellLevel);
+ data << uint32(itr->reqskill);
+ data << uint32(itr->reqskillvalue);
+ data << uint32(chain_node ? (chain_node->prev ? chain_node->prev : chain_node->req) : 0);
+ data << uint32(chain_node && chain_node->prev ? chain_node->req : 0);
+ data << uint32(0);
+ }
+
+ data << strTitle;
+
+ data.put<uint32>(count_pos,count);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 guid;
+ uint32 spellId = 0;
+
+ recv_data >> guid >> spellId;
+ sLog.outDebug( "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId );
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_TRAINER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // Lazy loading at first access
+ unit->LoadTrainerSpells();
+
+ if(!unit->isCanTrainingOf(_player,true))
+ return;
+
+ TrainerSpell const* trainer_spell = NULL;
+
+ // check present spell in trainer spell list
+ Creature::SpellsList const& trainer_spells = unit->GetTrainerSpells();
+ for(Creature::SpellsList::const_iterator itr = trainer_spells.begin(); itr != trainer_spells.end(); ++itr)
+ {
+ if(itr->spell->Id == spellId)
+ {
+ trainer_spell = &*itr;
+ break;
+ }
+ }
+
+ // not found, cheat?
+ if(!trainer_spell)
+ return;
+
+ // can't be learn, cheat? Or double learn with lags...
+ if(_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
+ return;
+
+ // apply reputation discount
+ uint32 nSpellCost = uint32(floor(trainer_spell->spellcost * _player->GetReputationPriceDiscount(unit)));
+
+ // check money requirement
+ if(_player->GetMoney() < nSpellCost )
+ return;
+
+ WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer
+ data << uint64(guid) << uint32(0xB3);
+ SendPacket(&data);
+
+ data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player
+ data << uint64(_player->GetGUID()) << uint32(0x016A);
+ SendPacket(&data);
+
+ _player->ModifyMoney( -int32(nSpellCost) );
+
+ // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation
+ _player->learnSpell(trainer_spell->spell->Id);
+
+ data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12);
+ data << uint64(guid) << uint32(spellId);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug( "WORLD: Received CMSG_GOSSIP_HELLO" );
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if( unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider())
+ {
+ unit->StopMoving();
+ }
+
+ // If spiritguide, no need for gossip menu, just put player into resurrect queue
+ if (unit->isSpiritGuide())
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(bg)
+ {
+ bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
+ sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
+ return;
+ }
+ }
+
+ if(!Script->GossipHello( _player, unit ))
+ {
+ _player->TalkedToCreature(unit->GetEntry(),unit->GetGUID());
+ unit->prepareGossipMenu(_player,0);
+ unit->sendPreparedGossip( _player );
+ }
+}
+
+void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
+
+ uint32 option;
+ uint32 unk;
+ uint64 guid;
+ std::string code = "";
+
+ recv_data >> guid >> unk >> option;
+
+ if(_player->PlayerTalkClass->GossipOptionCoded( option ))
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,8+4+1);
+ sLog.outBasic("reading string");
+ recv_data >> code;
+ sLog.outBasic("string read: %s", code.c_str());
+ }
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if(!code.empty())
+ {
+
+ if(!Script->GossipSelectWithCode( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option ), code.c_str()) )
+ unit->OnGossipSelect( _player, option );
+ }
+ else
+
+ if(!Script->GossipSelect( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option )) )
+ unit->OnGossipSelect( _player, option );
+}
+
+void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_SPIRITHEALER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleSpiritHealerActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendSpiritResurrect();
+}
+
+void WorldSession::SendSpiritResurrect()
+{
+ _player->ResurrectPlayer(0.5f,false, true);
+
+ _player->DurabilityLossAll(0.25f,true);
+
+ // get corpse nearest graveyard
+ WorldSafeLocsEntry const *corpseGrave = NULL;
+ Corpse *corpse = _player->GetCorpse();
+ if(corpse)
+ corpseGrave = objmgr.GetClosestGraveYard(
+ corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam() );
+
+ // now can spawn bones
+ _player->SpawnCorpseBones();
+
+ // teleport to nearest from corpse graveyard, if different from nearest to player ghost
+ if(corpseGrave)
+ {
+ WorldSafeLocsEntry const *ghostGrave = objmgr.GetClosestGraveYard(
+ _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam() );
+
+ if(corpseGrave != ghostGrave)
+ _player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
+ // or update at original position
+ else
+ ObjectAccessor::UpdateVisibilityForPlayer(_player);
+ }
+ // or update at original position
+ else
+ ObjectAccessor::UpdateVisibilityForPlayer(_player);
+
+ _player->SaveToDB();
+}
+
+void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 npcGUID;
+ recv_data >> npcGUID;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID,UNIT_NPC_FLAG_INNKEEPER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendBindPoint(unit);
+}
+
+void WorldSession::SendBindPoint(Creature *npc)
+{
+ uint32 bindspell = 3286;
+
+ // update sql homebind
+ CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", _player->GetMapId(), _player->GetZoneId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
+ _player->m_homebindMapId = _player->GetMapId();
+ _player->m_homebindZoneId = _player->GetZoneId();
+ _player->m_homebindX = _player->GetPositionX();
+ _player->m_homebindY = _player->GetPositionY();
+ _player->m_homebindZ = _player->GetPositionZ();
+
+ // send spell for bind 3286 bind magic
+ npc->CastSpell(_player, bindspell, true);
+
+ WorldPacket data( SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
+ data << npc->GetGUID();
+ data << bindspell;
+ SendPacket( &data );
+
+ // binding
+ data.Initialize( SMSG_BINDPOINTUPDATE, (4+4+4+4+4) );
+ data << float(_player->GetPositionX());
+ data << float(_player->GetPositionY());
+ data << float(_player->GetPositionZ());
+ data << uint32(_player->GetMapId());
+ data << uint32(_player->GetZoneId());
+ SendPacket( &data );
+
+ DEBUG_LOG("New Home Position X is %f",_player->GetPositionX());
+ DEBUG_LOG("New Home Position Y is %f",_player->GetPositionY());
+ DEBUG_LOG("New Home Position Z is %f",_player->GetPositionZ());
+ DEBUG_LOG("New Home MapId is %u",_player->GetMapId());
+ DEBUG_LOG("New Home ZoneId is %u",_player->GetZoneId());
+
+ // zone update
+ data.Initialize( SMSG_PLAYERBOUND, 8+4 );
+ data << uint64(_player->GetGUID());
+ data << uint32(_player->GetZoneId());
+ SendPacket( &data );
+
+ _player->PlayerTalkClass->CloseGossip();
+}
+
+//Need fix
+void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleListStabledPetsOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendStablePet(npcGUID);
+}
+
+void WorldSession::SendStablePet(uint64 guid )
+{
+ sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS Send.");
+
+ WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
+ data << uint64 ( guid );
+
+ Pet *pet = _player->GetPet();
+
+ data << uint8(0); // place holder for slot show number
+ data << uint8(GetPlayer()->m_stableSlots);
+
+ uint8 num = 0; // counter for place holder
+
+ // not let move dead pet in slot
+ if(pet && pet->isAlive() && pet->getPetType()==HUNTER_PET)
+ {
+ data << uint32(pet->GetCharmInfo()->GetPetNumber());
+ data << uint32(pet->GetEntry());
+ data << uint32(pet->getLevel());
+ data << pet->GetName(); // petname
+ data << uint32(pet->GetLoyaltyLevel()); // loyalty
+ data << uint8(0x01); // client slot 1 == current pet (0)
+ ++num;
+ }
+
+ // 0 1 2 3 4 5 6
+ QueryResult* result = CharacterDatabase.PQuery("SELECT owner, slot, id, entry, level, loyalty, name FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ data << uint32(fields[2].GetUInt32()); // petnumber
+ data << uint32(fields[3].GetUInt32()); // creature entry
+ data << uint32(fields[4].GetUInt32()); // level
+ data << fields[6].GetString(); // name
+ data << uint32(fields[5].GetUInt32()); // loyalty
+ data << uint8(fields[1].GetUInt32()+1); // slot
+
+ ++num;
+ }while( result->NextRow() );
+
+ delete result;
+ }
+
+ data.put<uint8>(8, num); // set real data to placeholder
+ SendPacket(&data);
+}
+
+void WorldSession::HandleStablePet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv CMSG_STABLE_PET not dispose.");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleStablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Pet *pet = _player->GetPet();
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ // can't place in stable dead pet
+ if(!pet||!pet->isAlive()||pet->getPetType()!=HUNTER_PET)
+ {
+ data << uint8(0x06);
+ SendPacket(&data);
+ return;
+ }
+
+ uint32 free_slot = 1;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3 ORDER BY slot ",_player->GetGUIDLow());
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 slot = fields[1].GetUInt32();
+
+ if(slot==free_slot) // this slot not free
+ ++free_slot;
+ }while( result->NextRow() );
+ }
+ delete result;
+
+ if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
+ {
+ _player->RemovePet(pet,PetSaveMode(free_slot));
+ data << uint8(0x08);
+ }
+ else
+ data << uint8(0x06);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
+ uint64 npcGUID;
+ uint32 petnumber;
+
+ recv_data >> npcGUID >> petnumber;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleUnstablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ Pet* pet = _player->GetPet();
+ if(pet && pet->isAlive())
+ {
+ uint8 i = 0x06;
+ data << uint8(i);
+ SendPacket(&data);
+ return;
+ }
+
+ // delete dead pet
+ if(pet)
+ _player->RemovePet(pet,PET_SAVE_AS_DELETED);
+
+ Pet *newpet = NULL;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow(),petnumber);
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ uint32 petentry = fields[0].GetUInt32();
+
+ newpet = new Pet(HUNTER_PET);
+ if(!newpet->LoadPetFromDB(_player,petentry,petnumber))
+ {
+ delete newpet;
+ newpet = NULL;
+ }
+ delete result;
+ }
+
+ if(newpet)
+ data << uint8(0x09);
+ else
+ data << uint8(0x06);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBuyStableSlot - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200);
+
+ if(GetPlayer()->m_stableSlots < 2) // max slots amount = 2
+ {
+ StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
+ if(_player->GetMoney() >= SlotPrice->Price)
+ {
+ ++GetPlayer()->m_stableSlots;
+ _player->ModifyMoney(-int32(SlotPrice->Price));
+ data << uint8(0x0A); // success buy
+ }
+ else
+ data << uint8(0x06);
+ }
+ else
+ data << uint8(0x06);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleStableRevivePet( WorldPacket &/* recv_data */)
+{
+ sLog.outDebug("HandleStableRevivePet: Not implemented");
+}
+
+void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
+ uint64 npcGUID;
+ uint32 pet_number;
+
+ recv_data >> npcGUID >> pet_number;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleStableSwapPet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ Pet* pet = _player->GetPet();
+
+ if(!pet || pet->getPetType()!=HUNTER_PET)
+ return;
+
+ // find swapped pet slot in stable
+ QueryResult *result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",_player->GetGUIDLow(),pet_number);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+
+ uint32 slot = fields[0].GetUInt32();
+ uint32 petentry = fields[1].GetUInt32();
+ delete result;
+
+ // move alive pet to slot or delele dead pet
+ _player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
+
+ // summon unstabled pet
+ Pet *newpet = new Pet;
+ if(!newpet->LoadPetFromDB(_player,petentry,pet_number))
+ {
+ delete newpet;
+ data << uint8(0x06);
+ }
+ else
+ data << uint8(0x09);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleRepairItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+1);
+
+ sLog.outDebug("WORLD: CMSG_REPAIR_ITEM");
+
+ uint64 npcGUID, itemGUID;
+ uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
+
+ recv_data >> npcGUID >> itemGUID >> guildBank;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_REPAIR);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // reputation discount
+ float discountMod = _player->GetReputationPriceDiscount(unit);
+
+ uint32 TotalCost = 0;
+ if (itemGUID)
+ {
+ sLog.outDebug("ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
+
+ Item* item = _player->GetItemByGuid(itemGUID);
+
+ if(item)
+ TotalCost= _player->DurabilityRepair(item->GetPos(),true,discountMod,guildBank>0?true:false);
+ }
+ else
+ {
+ sLog.outDebug("ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
+
+ TotalCost = _player->DurabilityRepairAll(true,discountMod,guildBank>0?true:false);
+ }
+ if (guildBank)
+ {
+ uint32 GuildId = _player->GetGuildId();
+ if (!GuildId)
+ return;
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if (!pGuild)
+ return;
+ pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost);
+ pGuild->SendMoneyInfo(this, _player->GetGUIDLow());
+ }
+}
diff --git a/src/game/NPCHandler.h b/src/game/NPCHandler.h
new file mode 100644
index 00000000000..1f42be22969
--- /dev/null
+++ b/src/game/NPCHandler.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __NPCHANDLER_H
+#define __NPCHANDLER_H
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+struct PageText
+{
+ uint32 Page_ID;
+ char * Text;
+
+ uint32 Next_Page;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+struct QEmote
+{
+ uint32 _Emote;
+ uint32 _Delay;
+};
+
+struct GossipTextOption
+{
+ std::string Text_0;
+ std::string Text_1;
+ uint32 Language;
+ float Probability;
+ QEmote Emotes[3];
+};
+
+struct GossipText
+{
+ uint32 Text_ID;
+ GossipTextOption Options[8];
+};
+
+struct PageTextLocale
+{
+ std::vector<std::string> Text;
+};
+
+struct NpcTextLocale
+{
+ NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
+
+ std::vector<std::vector<std::string> > Text_0;
+ std::vector<std::vector<std::string> > Text_1;
+};
+#endif
diff --git a/src/game/NullCreatureAI.cpp b/src/game/NullCreatureAI.cpp
new file mode 100644
index 00000000000..81f72c17da9
--- /dev/null
+++ b/src/game/NullCreatureAI.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "NullCreatureAI.h"
+
+NullCreatureAI::~NullCreatureAI()
+{
+}
diff --git a/src/game/NullCreatureAI.h b/src/game/NullCreatureAI.h
new file mode 100644
index 00000000000..7404e0a40f8
--- /dev/null
+++ b/src/game/NullCreatureAI.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_NULLCREATUREAI_H
+#define MANGOS_NULLCREATUREAI_H
+
+#include "CreatureAI.h"
+
+class MANGOS_DLL_DECL NullCreatureAI : public CreatureAI
+{
+ public:
+
+ NullCreatureAI(Creature &) {}
+ NullCreatureAI() {}
+
+ ~NullCreatureAI();
+
+ void MoveInLineOfSight(Unit *) {}
+ void AttackStart(Unit *) {}
+ void EnterEvadeMode() {}
+
+ bool IsVisible(Unit *) const { return false; }
+
+ void UpdateAI(const uint32) {}
+ static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; }
+};
+#endif
diff --git a/src/game/Object.cpp b/src/game/Object.cpp
new file mode 100644
index 00000000000..b3e61e102d3
--- /dev/null
+++ b/src/game/Object.cpp
@@ -0,0 +1,1631 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "SharedDefines.h"
+#include "WorldPacket.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "Object.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "WorldSession.h"
+#include "UpdateData.h"
+#include "UpdateMask.h"
+#include "Util.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Log.h"
+#include "Transports.h"
+#include "TargetedMovementGenerator.h"
+#include "WaypointMovementGenerator.h"
+#include "VMapFactory.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+
+#include "ObjectPosSelector.h"
+
+#include "TemporarySummon.h"
+
+uint32 GuidHigh2TypeId(uint32 guid_hi)
+{
+ switch(guid_hi)
+ {
+ case HIGHGUID_ITEM: return TYPEID_ITEM;
+ //case HIGHGUID_CONTAINER: return TYPEID_CONTAINER; HIGHGUID_CONTAINER==HIGHGUID_ITEM currently
+ case HIGHGUID_UNIT: return TYPEID_UNIT;
+ case HIGHGUID_PET: return TYPEID_UNIT;
+ case HIGHGUID_PLAYER: return TYPEID_PLAYER;
+ case HIGHGUID_GAMEOBJECT: return TYPEID_GAMEOBJECT;
+ case HIGHGUID_DYNAMICOBJECT:return TYPEID_DYNAMICOBJECT;
+ case HIGHGUID_CORPSE: return TYPEID_CORPSE;
+ case HIGHGUID_MO_TRANSPORT: return TYPEID_GAMEOBJECT;
+ }
+ return 10; // unknown
+}
+
+Object::Object( )
+{
+ m_objectTypeId = TYPEID_OBJECT;
+ m_objectType = TYPEMASK_OBJECT;
+
+ m_uint32Values = 0;
+ m_uint32Values_mirror = 0;
+ m_valuesCount = 0;
+
+ m_inWorld = false;
+ m_objectUpdated = false;
+
+ m_PackGUID.clear();
+ m_PackGUID.appendPackGUID(0);
+}
+
+Object::~Object( )
+{
+ if(m_objectUpdated)
+ ObjectAccessor::Instance().RemoveUpdateObject(this);
+
+ if(m_uint32Values)
+ {
+ if(IsInWorld())
+ {
+ ///- Do NOT call RemoveFromWorld here, if the object is a player it will crash
+ sLog.outError("Object::~Object - guid="I64FMTD", typeid=%d deleted but still in world!!", GetGUID(), GetTypeId());
+ //assert(0);
+ }
+
+ //DEBUG_LOG("Object desctr 1 check (%p)",(void*)this);
+ delete [] m_uint32Values;
+ delete [] m_uint32Values_mirror;
+ //DEBUG_LOG("Object desctr 2 check (%p)",(void*)this);
+ }
+}
+
+void Object::_InitValues()
+{
+ m_uint32Values = new uint32[ m_valuesCount ];
+ memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32));
+
+ m_uint32Values_mirror = new uint32[ m_valuesCount ];
+ memset(m_uint32Values_mirror, 0, m_valuesCount*sizeof(uint32));
+
+ m_objectUpdated = false;
+}
+
+void Object::_Create( uint32 guidlow, uint32 entry, HighGuid guidhigh )
+{
+ if(!m_uint32Values) _InitValues();
+
+ uint64 guid = MAKE_NEW_GUID(guidlow, entry, guidhigh); // required more changes to make it working
+ SetUInt64Value( OBJECT_FIELD_GUID, guid );
+ SetUInt32Value( OBJECT_FIELD_TYPE, m_objectType );
+ m_PackGUID.clear();
+ m_PackGUID.appendPackGUID(GetGUID());
+}
+
+void Object::BuildMovementUpdateBlock(UpdateData * data, uint32 flags ) const
+{
+ ByteBuffer buf(500);
+
+ buf << uint8( UPDATETYPE_MOVEMENT );
+ buf << GetGUID();
+
+ _BuildMovementUpdate(&buf, flags, 0x00000000);
+
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const
+{
+ if(!target)
+ {
+ return;
+ }
+
+ uint8 updatetype = UPDATETYPE_CREATE_OBJECT;
+ uint8 flags = m_updateFlag;
+ uint32 flags2 = 0;
+
+ /** lower flag1 **/
+ if(target == this) // building packet for oneself
+ {
+ flags |= UPDATEFLAG_SELF;
+
+ /*** temporary reverted - until real source of stack corruption will not found
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+ ****/
+ }
+
+ if(flags & UPDATEFLAG_HASPOSITION)
+ {
+ // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses...
+ if(isType(TYPEMASK_DYNAMICOBJECT) || isType(TYPEMASK_CORPSE) || isType(TYPEMASK_PLAYER))
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+
+ // UPDATETYPE_CREATE_OBJECT2 for pets...
+ if(target->GetPetGUID() == GetGUID())
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+
+ // UPDATETYPE_CREATE_OBJECT2 for some gameobject types...
+ if(isType(TYPEMASK_GAMEOBJECT))
+ {
+ switch(((GameObject*)this)->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_TRAP:
+ case GAMEOBJECT_TYPE_DUEL_ARBITER:
+ case GAMEOBJECT_TYPE_FLAGSTAND:
+ case GAMEOBJECT_TYPE_FLAGDROP:
+ updatetype = UPDATETYPE_CREATE_OBJECT2;
+ break;
+ case GAMEOBJECT_TYPE_TRANSPORT:
+ flags |= UPDATEFLAG_TRANSPORT;
+ break;
+ }
+ }
+ }
+
+ //sLog.outDebug("BuildCreateUpdate: update-type: %u, object-type: %u got flags: %X, flags2: %X", updatetype, m_objectTypeId, flags, flags2);
+
+ ByteBuffer buf(500);
+ buf << (uint8)updatetype;
+ //buf.append(GetPackGUID()); //client crashes when using this
+ buf << (uint8)0xFF << GetGUID();
+ buf << (uint8)m_objectTypeId;
+
+ _BuildMovementUpdate(&buf, flags, flags2);
+
+ UpdateMask updateMask;
+ updateMask.SetCount( m_valuesCount );
+ _SetCreateBits( &updateMask, target );
+ _BuildValuesUpdate(updatetype, &buf, &updateMask, target );
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildUpdate(UpdateDataMapType &update_players)
+{
+ ObjectAccessor::_buildUpdateObject(this,update_players);
+ ClearUpdateMask(true);
+}
+
+void Object::SendUpdateToPlayer(Player* player)
+{
+ // send update to another players
+ SendUpdateObjectToAllExcept(player);
+
+ // send create update to player
+ UpdateData upd;
+ WorldPacket packet;
+
+ upd.Clear();
+ BuildCreateUpdateBlockForPlayer(&upd, player);
+ upd.BuildPacket(&packet);
+ player->GetSession()->SendPacket(&packet);
+
+ // now object updated/(create updated)
+}
+
+void Object::BuildValuesUpdateBlockForPlayer(UpdateData *data, Player *target) const
+{
+ ByteBuffer buf(500);
+
+ buf << (uint8) UPDATETYPE_VALUES;
+ //buf.append(GetPackGUID()); //client crashes when using this. but not have crash in debug mode
+ buf << (uint8)0xFF;
+ buf << GetGUID();
+
+ UpdateMask updateMask;
+ updateMask.SetCount( m_valuesCount );
+
+ _SetUpdateBits( &updateMask, target );
+ _BuildValuesUpdate(UPDATETYPE_VALUES, &buf, &updateMask, target );
+
+ data->AddUpdateBlock(buf);
+}
+
+void Object::BuildOutOfRangeUpdateBlock(UpdateData * data) const
+{
+ data->AddOutOfRangeGUID(GetGUID());
+}
+
+void Object::DestroyForPlayer(Player *target) const
+{
+ ASSERT(target);
+
+ WorldPacket data(SMSG_DESTROY_OBJECT, 8);
+ data << GetGUID();
+ target->GetSession()->SendPacket( &data );
+}
+
+void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const
+{
+ *data << (uint8)flags; // update flags
+
+ // 0x20
+ if (flags & UPDATEFLAG_LIVING)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_UNIT:
+ {
+ flags2 = ((Unit*)this)->GetUnitMovementFlags();
+ }
+ break;
+ case TYPEID_PLAYER:
+ {
+ flags2 = ((Player*)this)->GetUnitMovementFlags();
+
+ if(((Player*)this)->GetTransport())
+ flags2 |= MOVEMENTFLAG_ONTRANSPORT;
+ else
+ flags2 &= ~MOVEMENTFLAG_ONTRANSPORT;
+
+ // remove unknown, unused etc flags for now
+ flags2 &= ~MOVEMENTFLAG_SPLINE2; // will be set manually
+
+ if(((Player*)this)->isInFlight())
+ {
+ WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
+ flags2 = (MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_SPLINE2);
+ }
+ }
+ break;
+ }
+
+ *data << uint32(flags2); // movement flags
+ *data << uint8(0); // unk 2.3.0
+ *data << uint32(getMSTime()); // time (in milliseconds)
+ }
+
+ // 0x40
+ if (flags & UPDATEFLAG_HASPOSITION)
+ {
+ // 0x02
+ if(flags & UPDATEFLAG_TRANSPORT && ((GameObject*)this)->GetGoType() == GAMEOBJECT_TYPE_MO_TRANSPORT)
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ *data << ((WorldObject *)this)->GetOrientation();
+ }
+ else
+ {
+ *data << ((WorldObject *)this)->GetPositionX();
+ *data << ((WorldObject *)this)->GetPositionY();
+ *data << ((WorldObject *)this)->GetPositionZ();
+ *data << ((WorldObject *)this)->GetOrientation();
+ }
+ }
+
+ // 0x20
+ if(flags & UPDATEFLAG_LIVING)
+ {
+ // 0x00000200
+ if(flags2 & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ *data << (uint64)((Player*)this)->GetTransport()->GetGUID();
+ *data << (float)((Player*)this)->GetTransOffsetX();
+ *data << (float)((Player*)this)->GetTransOffsetY();
+ *data << (float)((Player*)this)->GetTransOffsetZ();
+ *data << (float)((Player*)this)->GetTransOffsetO();
+ *data << (uint32)((Player*)this)->GetTransTime();
+ }
+ //MaNGOS currently not have support for other than player on transport
+ }
+
+ // 0x02200000
+ if(flags2 & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (float)((Player*)this)->m_movementInfo.s_pitch;
+ else
+ *data << (float)0; // is't part of movement packet, we must store and send it...
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (uint32)((Player*)this)->m_movementInfo.fallTime;
+ else
+ *data << (uint32)0; // last fall time
+
+ // 0x00001000
+ if(flags2 & MOVEMENTFLAG_JUMPING)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ *data << (float)((Player*)this)->m_movementInfo.j_unk;
+ *data << (float)((Player*)this)->m_movementInfo.j_sinAngle;
+ *data << (float)((Player*)this)->m_movementInfo.j_cosAngle;
+ *data << (float)((Player*)this)->m_movementInfo.j_xyspeed;
+ }
+ else
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }
+ }
+
+ // 0x04000000
+ if(flags2 & MOVEMENTFLAG_SPLINE)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ *data << (float)((Player*)this)->m_movementInfo.u_unk1;
+ else
+ *data << (float)0;
+ }
+
+ *data << ((Unit*)this)->GetSpeed( MOVE_WALK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_RUN );
+ *data << ((Unit*)this)->GetSpeed( MOVE_SWIMBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_SWIM );
+ *data << ((Unit*)this)->GetSpeed( MOVE_WALKBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_FLY );
+ *data << ((Unit*)this)->GetSpeed( MOVE_FLYBACK );
+ *data << ((Unit*)this)->GetSpeed( MOVE_TURN );
+
+ // 0x08000000
+ if(flags2 & MOVEMENTFLAG_SPLINE2)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ {
+ sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 for non-player");
+ return;
+ }
+
+ if(!((Player*)this)->isInFlight())
+ {
+ sLog.outDebug("_BuildMovementUpdate: MOVEMENTFLAG_SPLINE2 but not in flight");
+ return;
+ }
+
+ WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
+
+ FlightPathMovementGenerator *fmg = (FlightPathMovementGenerator*)(((Player*)this)->GetMotionMaster()->top());
+
+ uint32 flags3 = 0x00000300;
+
+ *data << uint32(flags3); // splines flag?
+
+ if(flags3 & 0x10000) // probably x,y,z coords there
+ {
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }
+
+ if(flags3 & 0x20000) // probably guid there
+ {
+ *data << uint64(0);
+ }
+
+ if(flags3 & 0x40000) // may be orientation
+ {
+ *data << (float)0;
+ }
+
+ Path &path = fmg->GetPath();
+
+ float x, y, z;
+ ((Player*)this)->GetPosition(x, y, z);
+
+ uint32 inflighttime = uint32(path.GetPassedLength(fmg->GetCurrentNode(), x, y, z) * 32);
+ uint32 traveltime = uint32(path.GetTotalLength() * 32);
+
+ *data << uint32(inflighttime); // passed move time?
+ *data << uint32(traveltime); // full move time?
+ *data << uint32(0); // ticks count?
+
+ uint32 poscount = uint32(path.Size());
+
+ *data << uint32(poscount); // points count
+
+ for(uint32 i = 0; i < poscount; ++i)
+ {
+ *data << path.GetNodes()[i].x;
+ *data << path.GetNodes()[i].y;
+ *data << path.GetNodes()[i].z;
+ }
+
+ /*for(uint32 i = 0; i < poscount; i++)
+ {
+ // path points
+ *data << (float)0;
+ *data << (float)0;
+ *data << (float)0;
+ }*/
+
+ *data << path.GetNodes()[poscount-1].x;
+ *data << path.GetNodes()[poscount-1].y;
+ *data << path.GetNodes()[poscount-1].z;
+
+ // target position (path end)
+ /**data << ((Unit*)this)->GetPositionX();
+ *data << ((Unit*)this)->GetPositionY();
+ *data << ((Unit*)this)->GetPositionZ();*/
+ }
+ }
+
+ // 0x8
+ if(flags & UPDATEFLAG_LOWGUID)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_OBJECT:
+ case TYPEID_ITEM:
+ case TYPEID_CONTAINER:
+ case TYPEID_GAMEOBJECT:
+ case TYPEID_DYNAMICOBJECT:
+ case TYPEID_CORPSE:
+ *data << uint32(GetGUIDLow()); // GetGUIDLow()
+ break;
+ case TYPEID_UNIT:
+ *data << uint32(0x0000000B); // unk, can be 0xB or 0xC
+ break;
+ case TYPEID_PLAYER:
+ if(flags & UPDATEFLAG_SELF)
+ *data << uint32(0x00000015); // unk, can be 0x15 or 0x22
+ else
+ *data << uint32(0x00000008); // unk, can be 0x7 or 0x8
+ break;
+ default:
+ *data << uint32(0x00000000); // unk
+ break;
+ }
+ }
+
+ // 0x10
+ if(flags & UPDATEFLAG_HIGHGUID)
+ {
+ switch(GetTypeId())
+ {
+ case TYPEID_OBJECT:
+ case TYPEID_ITEM:
+ case TYPEID_CONTAINER:
+ case TYPEID_GAMEOBJECT:
+ case TYPEID_DYNAMICOBJECT:
+ case TYPEID_CORPSE:
+ *data << uint32(GetGUIDHigh()); // GetGUIDHigh()
+ break;
+ default:
+ *data << uint32(0x00000000); // unk
+ break;
+ }
+ }
+
+ // 0x4
+ if(flags & UPDATEFLAG_FULLGUID)
+ {
+ *data << uint8(0); // packed guid (probably target guid)
+ }
+
+ // 0x2
+ if(flags & UPDATEFLAG_TRANSPORT)
+ {
+ *data << uint32(getMSTime()); // ms time
+ }
+}
+
+void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask *updateMask, Player *target) const
+{
+ if(!target)
+ return;
+
+ bool IsActivateToQuest = false;
+ if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2)
+ {
+ if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
+ {
+ if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
+ {
+ IsActivateToQuest = true;
+ updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
+ }
+ }
+ }
+ else //case UPDATETYPE_VALUES
+ {
+ if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
+ {
+ if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
+ {
+ IsActivateToQuest = true;
+ }
+ updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
+ updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
+ }
+ }
+
+ WPAssert(updateMask && updateMask->GetCount() == m_valuesCount);
+
+ *data << (uint8)updateMask->GetBlockCount();
+ data->append( updateMask->GetMask(), updateMask->GetLength() );
+
+ // 2 specialized loops for speed optimization in non-unit case
+ if(isType(TYPEMASK_UNIT)) // unit (creature/player) case
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // remove custom flag before send
+ if( index == UNIT_NPC_FLAGS )
+ *data << uint32(m_uint32Values[ index ] & ~UNIT_NPC_FLAG_GUARD);
+ // 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)
+ {
+ // convert from float to uint32 and send
+ *data << uint32(m_floatValues[ index ] < 0 ? 0 : m_floatValues[ index ]);
+ }
+ // there are some float values which may be negative or can't get negative due to other checks
+ else if(index >= UNIT_FIELD_NEGSTAT0 && index <= UNIT_FIELD_NEGSTAT4 ||
+ index >= UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6) ||
+ index >= UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6) ||
+ index >= UNIT_FIELD_POSSTAT0 && index <= UNIT_FIELD_POSSTAT4)
+ {
+ *data << uint32(m_floatValues[ index ]);
+ }
+ // Gamemasters should be always able to select units - remove not selectable flag
+ else if(index == UNIT_FIELD_FLAGS && target->isGameMaster())
+ {
+ *data << (m_uint32Values[ index ] & ~UNIT_FLAG_NOT_SELECTABLE);
+ }
+ // hide lootable animation for unallowed players
+ else if(index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
+ {
+ if(!target->isAllowedToLoot((Creature*)this))
+ *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_LOOTABLE);
+ else
+ *data << (m_uint32Values[ index ] & ~UNIT_DYNFLAG_OTHER_TAGGER);
+ }
+ else
+ {
+ // send in current format (float as float, uint32 as uint32)
+ *data << m_uint32Values[ index ];
+ }
+ }
+ }
+ }
+ else if(isType(TYPEMASK_GAMEOBJECT)) // gameobject case
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // send in current format (float as float, uint32 as uint32)
+ if ( index == GAMEOBJECT_DYN_FLAGS )
+ {
+ if(IsActivateToQuest )
+ {
+ switch(((GameObject*)this)->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_CHEST:
+ *data << uint32(9); // enable quest object. Represent 9, but 1 for client before 2.3.0
+ break;
+ case GAMEOBJECT_TYPE_GOOBER:
+ *data << uint32(1);
+ break;
+ default:
+ *data << uint32(0); //unknown. not happen.
+ break;
+ }
+ }
+ else
+ *data << uint32(0); // disable quest object
+ }
+ else
+ *data << m_uint32Values[ index ]; // other cases
+ }
+ }
+ }
+ else // other objects case (no special index checks)
+ {
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if( updateMask->GetBit( index ) )
+ {
+ // send in current format (float as float, uint32 as uint32)
+ *data << m_uint32Values[ index ];
+ }
+ }
+ }
+}
+
+void Object::ClearUpdateMask(bool remove)
+{
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if(m_uint32Values_mirror[index]!= m_uint32Values[index])
+ m_uint32Values_mirror[index] = m_uint32Values[index];
+ }
+ if(m_objectUpdated)
+ {
+ if(remove)
+ ObjectAccessor::Instance().RemoveUpdateObject(this);
+ m_objectUpdated = false;
+ }
+}
+
+// Send current value fields changes to all viewers
+void Object::SendUpdateObjectToAllExcept(Player* exceptPlayer)
+{
+ // changes will be send in create packet
+ if(!IsInWorld())
+ return;
+
+ // nothing do
+ if(!m_objectUpdated)
+ return;
+
+ ObjectAccessor::UpdateObject(this,exceptPlayer);
+}
+
+bool Object::LoadValues(const char* data)
+{
+ if(!m_uint32Values) _InitValues();
+
+ Tokens tokens = StrSplit(data, " ");
+
+ if(tokens.size() != m_valuesCount)
+ return false;
+
+ Tokens::iterator iter;
+ int index;
+ for (iter = tokens.begin(), index = 0; index < m_valuesCount; ++iter, ++index)
+ {
+ m_uint32Values[index] = atol((*iter).c_str());
+ }
+
+ return true;
+}
+
+void Object::_SetUpdateBits(UpdateMask *updateMask, Player* /*target*/) const
+{
+ for( uint16 index = 0; index < m_valuesCount; index ++ )
+ {
+ if(m_uint32Values_mirror[index]!= m_uint32Values[index])
+ updateMask->SetBit(index);
+ }
+}
+
+void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const
+{
+ for( uint16 index = 0; index < m_valuesCount; index++ )
+ {
+ if(GetUInt32Value(index) != 0)
+ updateMask->SetBit(index);
+ }
+}
+
+void Object::SetInt32Value( uint16 index, int32 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_int32Values[ index ] != value)
+ {
+ m_int32Values[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt32Value( uint16 index, uint32 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_uint32Values[ index ] != value)
+ {
+ m_uint32Values[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt64Value( uint16 index, const uint64 &value )
+{
+ ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) );
+ if(*((uint64*)&(m_uint32Values[ index ])) != value)
+ {
+ m_uint32Values[ index ] = *((uint32*)&value);
+ m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1);
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetFloatValue( uint16 index, float value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(m_floatValues[ index ] != value)
+ {
+ m_floatValues[ index ] = value;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetByteValue( uint16 index, uint8 offset, uint8 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(offset > 4)
+ {
+ sLog.outError("Object::SetByteValue: wrong offset %u", offset);
+ return;
+ }
+
+ if(uint8(m_uint32Values[ index ] >> (offset * 8)) != value)
+ {
+ m_uint32Values[ index ] &= ~uint32(uint32(0xFF) << (offset * 8));
+ m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 8));
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetUInt16Value( uint16 index, uint8 offset, uint16 value )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+
+ if(offset > 2)
+ {
+ sLog.outError("Object::SetUInt16Value: wrong offset %u", offset);
+ return;
+ }
+
+ if(uint8(m_uint32Values[ index ] >> (offset * 16)) != value)
+ {
+ m_uint32Values[ index ] &= ~uint32(uint32(0xFFFF) << (offset * 16));
+ m_uint32Values[ index ] |= uint32(uint32(value) << (offset * 16));
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::SetStatFloatValue( uint16 index, float value)
+{
+ if(value < 0)
+ value = 0.0f;
+
+ SetFloatValue(index, value);
+}
+
+void Object::SetStatInt32Value( uint16 index, int32 value)
+{
+ if(value < 0)
+ value = 0;
+
+ SetUInt32Value(index, uint32(value));
+}
+
+void Object::ApplyModUInt32Value(uint16 index, int32 val, bool apply)
+{
+ int32 cur = GetUInt32Value(index);
+ cur += (apply ? val : -val);
+ if(cur < 0)
+ cur = 0;
+ SetUInt32Value(index,cur);
+}
+
+void Object::ApplyModInt32Value(uint16 index, int32 val, bool apply)
+{
+ int32 cur = GetInt32Value(index);
+ cur += (apply ? val : -val);
+ SetInt32Value(index,cur);
+}
+
+void Object::ApplyModSignedFloatValue(uint16 index, float val, bool apply)
+{
+ float cur = GetFloatValue(index);
+ cur += (apply ? val : -val);
+ SetFloatValue(index,cur);
+}
+
+void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply)
+{
+ float cur = GetFloatValue(index);
+ cur += (apply ? val : -val);
+ if(cur < 0)
+ cur = 0;
+ SetFloatValue(index,cur);
+}
+
+void Object::SetFlag( uint16 index, uint32 newFlag )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+ uint32 oldval = m_uint32Values[ index ];
+ uint32 newval = oldval | newFlag;
+
+ if(oldval != newval)
+ {
+ m_uint32Values[ index ] = newval;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+void Object::RemoveFlag( uint16 index, uint32 oldFlag )
+{
+ ASSERT( index < m_valuesCount || PrintIndexError( index , true ) );
+ uint32 oldval = m_uint32Values[ index ];
+ uint32 newval = oldval & ~oldFlag;
+
+ if(oldval != newval)
+ {
+ m_uint32Values[ index ] = newval;
+
+ if(m_inWorld)
+ {
+ if(!m_objectUpdated)
+ {
+ ObjectAccessor::Instance().AddUpdateObject(this);
+ m_objectUpdated = true;
+ }
+ }
+ }
+}
+
+bool Object::PrintIndexError(uint32 index, bool set) const
+{
+ sLog.outError("ERROR: Attempt %s non-existed value field: %u (count: %u) for object typeid: %u type mask: %u",(set ? "set value to" : "get value from"),index,m_valuesCount,GetTypeId(),m_objectType);
+
+ // assert must fail after function call
+ return false;
+}
+
+WorldObject::WorldObject()
+{
+ m_positionX = 0.0f;
+ m_positionY = 0.0f;
+ m_positionZ = 0.0f;
+ m_orientation = 0.0f;
+
+ m_mapId = 0;
+ m_InstanceId = 0;
+
+ m_name = "";
+
+ mSemaphoreTeleport = false;
+}
+
+void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid )
+{
+ Object::_Create(guidlow, 0, guidhigh);
+
+ m_mapId = mapid;
+}
+
+uint32 WorldObject::GetZoneId() const
+{
+ return MapManager::Instance().GetBaseMap(m_mapId)->GetZoneId(m_positionX,m_positionY);
+}
+
+uint32 WorldObject::GetAreaId() const
+{
+ return MapManager::Instance().GetBaseMap(m_mapId)->GetAreaId(m_positionX,m_positionY);
+}
+
+InstanceData* WorldObject::GetInstanceData()
+{
+ Map *map = MapManager::Instance().GetMap(m_mapId, this);
+ return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceData() : NULL;
+}
+
+ //slow
+float WorldObject::GetDistance(const WorldObject* obj) const
+{
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float dz = GetPositionZ() - obj->GetPositionZ();
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance2d(float x, float y) const
+{
+ float dx = GetPositionX() - x;
+ float dy = GetPositionY() - y;
+ float sizefactor = GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance(const float x, const float y, const float z) const
+{
+ float dx = GetPositionX() - x;
+ float dy = GetPositionY() - y;
+ float dz = GetPositionZ() - z;
+ float sizefactor = GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistance2d(const WorldObject* obj) const
+{
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = sqrt((dx*dx) + (dy*dy)) - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+float WorldObject::GetDistanceZ(const WorldObject* obj) const
+{
+ float dz = fabs(GetPositionZ() - obj->GetPositionZ());
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float dist = dz - sizefactor;
+ return ( dist > 0 ? dist : 0);
+}
+
+bool WorldObject::IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const
+{
+ if (!obj || !IsInMap(obj)) return false;
+
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float dz = GetPositionZ() - obj->GetPositionZ();
+ float distsq = dx*dx + dy*dy + dz*dz;
+ float sizefactor = GetObjectSize() + obj->GetObjectSize();
+ float maxdist = dist2compare + sizefactor;
+
+ return distsq < maxdist * maxdist;
+}
+
+bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const
+{
+ if (!IsInMap(obj)) return false;
+ float ox,oy,oz;
+ obj->GetPosition(ox,oy,oz);
+ return(IsWithinLOS(ox, oy, oz ));
+}
+
+bool WorldObject::IsWithinLOS(const float ox, const float oy, const float oz ) const
+{
+ float x,y,z;
+ GetPosition(x,y,z);
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);
+}
+
+float WorldObject::GetAngle(const WorldObject* obj) const
+{
+ if(!obj) return 0;
+ return GetAngle( obj->GetPositionX(), obj->GetPositionY() );
+}
+
+// Return angle in range 0..2*pi
+float WorldObject::GetAngle( const float x, const float y ) const
+{
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+
+ float ang = atan2(dy, dx);
+ ang = (ang >= 0) ? ang : 2 * M_PI + ang;
+ return ang;
+}
+
+bool WorldObject::HasInArc(const float arcangle, const WorldObject* obj) const
+{
+ float arc = arcangle;
+
+ // move arc to range 0.. 2*pi
+ while( arc >= 2.0f * M_PI )
+ arc -= 2.0f * M_PI;
+ while( arc < 0 )
+ arc += 2.0f * M_PI;
+
+ float angle = GetAngle( obj );
+ angle -= m_orientation;
+
+ // move angle to range -pi ... +pi
+ while( angle > M_PI)
+ angle -= 2.0f * M_PI;
+ while(angle < -M_PI)
+ angle += 2.0f * M_PI;
+
+ float lborder = -1 * (arc/2.0f); // in range -pi..0
+ float rborder = (arc/2.0f); // in range 0..pi
+ return (( angle >= lborder ) && ( angle <= rborder ));
+}
+
+void WorldObject::GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z) const
+{
+ if(distance==0)
+ {
+ rand_x = x;
+ rand_y = y;
+ rand_z = z;
+ return;
+ }
+
+ // angle to face `obj` to `this`
+ float angle = rand_norm()*2*M_PI;
+ float new_dist = rand_norm()*distance;
+
+ rand_x = x + new_dist * cos(angle);
+ rand_y = y + new_dist * sin(angle);
+ rand_z = z;
+
+ MaNGOS::NormalizeMapCoord(rand_x);
+ MaNGOS::NormalizeMapCoord(rand_y);
+ UpdateGroundPositionZ(rand_x,rand_y,rand_z); // update to LOS height if available
+}
+
+void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
+{
+ float new_z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(x,y,z,true);
+ if(new_z > INVALID_HEIGHT)
+ z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface
+}
+
+bool WorldObject::IsPositionValid() const
+{
+ return MaNGOS::IsValidMapCoord(m_positionX,m_positionY,m_positionZ,m_orientation);
+}
+
+void WorldObject::MonsterSay(const char* text, uint32 language, uint64 TargetGuid)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,CHAT_MSG_MONSTER_SAY,text,language,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
+}
+
+void WorldObject::MonsterYell(const char* text, uint32 language, uint64 TargetGuid)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
+}
+
+void WorldObject::MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE,text,LANG_UNIVERSAL,GetName(),TargetGuid);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true);
+}
+
+void WorldObject::MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper)
+{
+ Player *player = objmgr.GetPlayer(receiver);
+ if(!player || !player->GetSession())
+ return;
+
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver);
+
+ player->GetSession()->SendPacket(&data);
+}
+
+namespace MaNGOS
+{
+ class MessageChatLocaleCacheDo
+ {
+ public:
+ MessageChatLocaleCacheDo(WorldObject const& obj, ChatMsg msgtype, int32 textId, uint32 language, uint64 targetGUID, float dist)
+ : i_object(obj), i_msgtype(msgtype), i_textId(textId), i_language(language),
+ i_targetGUID(targetGUID), i_dist(dist)
+ {
+ }
+
+ ~MessageChatLocaleCacheDo()
+ {
+ for(int i = 0; i < i_data_cache.size(); ++i)
+ delete i_data_cache[i];
+ }
+
+ void operator()(Player* p)
+ {
+ // skip far away players
+ if(p->GetDistance(&i_object) > i_dist)
+ return;
+
+ uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex();
+ uint32 cache_idx = loc_idx+1;
+ WorldPacket* data;
+
+ // create if not cached yet
+ if(i_data_cache.size() < cache_idx+1 || !i_data_cache[cache_idx])
+ {
+ if(i_data_cache.size() < cache_idx+1)
+ i_data_cache.resize(cache_idx+1);
+
+ char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
+ data = new WorldPacket(SMSG_MESSAGECHAT, 200);
+
+ // TODO: i_object.GetName() also must be localized?
+ i_object.BuildMonsterChat(data,i_msgtype,text,i_language,i_object.GetName(),i_targetGUID);
+
+ i_data_cache[cache_idx] = data;
+ }
+ else
+ data = i_data_cache[cache_idx];
+
+ p->SendDirectMessage(data);
+ }
+
+ private:
+ WorldObject const& i_object;
+ ChatMsg i_msgtype;
+ int32 i_textId;
+ uint32 i_language;
+ uint64 i_targetGUID;
+ float i_dist;
+ std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index
+ };
+} // namespace MaNGOS
+
+void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote)
+{
+ CellPair p = MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY());
+
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::MessageChatLocaleCacheDo say_do(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
+ MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo> say_worker(say_do);
+ TypeContainerVisitor<MaNGOS::PlayerWorker<MaNGOS::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, message, *GetMap());
+}
+
+void WorldObject::MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper)
+{
+ Player *player = objmgr.GetPlayer(receiver);
+ if(!player || !player->GetSession())
+ return;
+
+ uint32 loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
+ char const* text = objmgr.GetMangosString(textId,loc_idx);
+
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver);
+
+ player->GetSession()->SendPacket(&data);
+}
+
+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();
+ *data << (uint32)0; //2.1.0
+ *data << (uint32)(strlen(name)+1);
+ *data << name;
+ *data << (uint64)targetGuid; //Unit Target
+ if( targetGuid && !IS_PLAYER_GUID(targetGuid) )
+ {
+ *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 << text;
+ *data << (uint8)0; // ChatTag
+}
+
+void WorldObject::BuildHeartBeatMsg(WorldPacket *data) const
+{
+ //Heartbeat message cannot be used for non-units
+ if (!isType(TYPEMASK_UNIT))
+ return;
+
+ data->Initialize(MSG_MOVE_HEARTBEAT, 32);
+ data->append(GetPackGUID());
+ *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags
+ *data << uint8(0); // 2.3.0
+ *data << getMSTime(); // time
+ *data << m_positionX;
+ *data << m_positionY;
+ *data << m_positionZ;
+ *data << m_orientation;
+ *data << uint32(0);
+}
+
+void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float z, float ang) const
+{
+ //TeleportAck message cannot be used for non-units
+ if (!isType(TYPEMASK_UNIT))
+ return;
+
+ data->Initialize(MSG_MOVE_TELEPORT_ACK, 41);
+ data->append(GetPackGUID());
+ *data << uint32(0); // this value increments every time
+ *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags
+ *data << uint8(0); // 2.3.0
+ *data << getMSTime(); // time
+ *data << x;
+ *data << y;
+ *data << z;
+ *data << ang;
+ *data << uint32(0);
+}
+
+void WorldObject::SendMessageToSet(WorldPacket *data, bool /*bToSelf*/)
+{
+ MapManager::Instance().GetMap(m_mapId, this)->MessageBroadcast(this, data);
+}
+
+void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/)
+{
+ MapManager::Instance().GetMap(m_mapId, this)->MessageDistBroadcast(this, data, dist);
+}
+
+void WorldObject::SendObjectDeSpawnAnim(uint64 guid)
+{
+ WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8);
+ data << guid;
+ SendMessageToSet(&data, true);
+}
+
+Map* WorldObject::GetMap() const
+{
+ return MapManager::Instance().GetMap(GetMapId(), this);
+}
+
+Map const* WorldObject::GetBaseMap() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId());
+}
+
+void WorldObject::AddObjectToRemoveList()
+{
+ Map* map = GetMap();
+ if(!map)
+ {
+ sLog.outError("Object (TypeId: %u Entry: %u GUID: %u) at attempt add to move list not have valid map (Id: %u).",GetTypeId(),GetEntry(),GetGUIDLow(),GetMapId());
+ return;
+ }
+
+ map->AddObjectToRemoveList(this);
+}
+
+Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime)
+{
+ TemporarySummon* pCreature = new TemporarySummon(GetGUID());
+
+ pCreature->SetInstanceId(GetInstanceId());
+ uint32 team = 0;
+ if (GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)this)->GetTeam();
+
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMap(), id, team))
+ {
+ delete pCreature;
+ return NULL;
+ }
+
+ if (x == 0.0f && y == 0.0f && z == 0.0f)
+ GetClosePoint(x, y, z, pCreature->GetObjectSize());
+
+ pCreature->Relocate(x, y, z, ang);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ return NULL;
+ }
+
+ pCreature->Summon(spwtype, despwtime);
+
+ if(GetTypeId()==TYPEID_UNIT && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->JustSummoned(pCreature);
+
+ //return the creature therewith the summoner has access to it
+ return pCreature;
+}
+
+namespace MaNGOS
+{
+ class NearUsedPosDo
+ {
+ public:
+ NearUsedPosDo(WorldObject const& obj, WorldObject const* searcher, float angle, ObjectPosSelector& selector)
+ : i_object(obj), i_searcher(searcher), i_angle(angle), i_selector(selector) {}
+
+ void operator()(Corpse*) const {}
+ void operator()(DynamicObject*) const {}
+
+ void operator()(Creature* c) const
+ {
+ // skip self or target
+ if(c==i_searcher || c==&i_object)
+ return;
+
+ float x,y,z;
+
+ if( !c->isAlive() || c->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) ||
+ !c->GetMotionMaster()->GetDestination(x,y,z) )
+ {
+ x = c->GetPositionX();
+ y = c->GetPositionY();
+ }
+
+ add(c,x,y);
+ }
+
+ template<class T>
+ void operator()(T* u) const
+ {
+ // skip self or target
+ if(u==i_searcher || u==&i_object)
+ return;
+
+ float x,y;
+
+ x = u->GetPositionX();
+ y = u->GetPositionY();
+
+ add(u,x,y);
+ }
+
+ // we must add used pos that can fill places around center
+ void add(WorldObject* u, float x, float y) const
+ {
+ // dist include size of u
+ float dist2d = i_object.GetDistance2d(x,y);
+
+ // u is too nearest to i_object
+ if(dist2d + i_object.GetObjectSize() + u->GetObjectSize() < i_selector.m_dist - i_selector.m_size)
+ return;
+
+ // u is too far away from i_object
+ if(dist2d + i_object.GetObjectSize() - u->GetObjectSize() > i_selector.m_dist + i_selector.m_size)
+ return;
+
+ float angle = i_object.GetAngle(u)-i_angle;
+
+ // move angle to range -pi ... +pi
+ while( angle > M_PI)
+ angle -= 2.0f * M_PI;
+ while(angle < -M_PI)
+ angle += 2.0f * M_PI;
+
+ i_selector.AddUsedPos(u->GetObjectSize(),angle,dist2d + i_object.GetObjectSize());
+ }
+ private:
+ WorldObject const& i_object;
+ WorldObject const* i_searcher;
+ float i_angle;
+ ObjectPosSelector& i_selector;
+ };
+} // namespace MaNGOS
+
+//===================================================================================================
+
+void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float absAngle ) const
+{
+ x = GetPositionX() + (GetObjectSize() + distance2d) * cos(absAngle);
+ y = GetPositionY() + (GetObjectSize() + distance2d) * sin(absAngle);
+
+ MaNGOS::NormalizeMapCoord(x);
+ MaNGOS::NormalizeMapCoord(y);
+}
+
+void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d, float absAngle ) const
+{
+ GetNearPoint2D(x,y,distance2d+searcher_size,absAngle);
+ z = GetPositionZ();
+
+ // if detection disabled, return first point
+ if(!sWorld.getConfig(CONFIG_DETECT_POS_COLLISION))
+ {
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+ return;
+ }
+
+ // or remember first point
+ float first_x = x;
+ float first_y = y;
+ bool first_los_conflict = false; // first point LOS problems
+
+ // prepare selector for work
+ ObjectPosSelector selector(GetPositionX(),GetPositionY(),GetObjectSize(),distance2d+searcher_size);
+
+ // adding used positions around object
+ {
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::NearUsedPosDo u_do(*this,searcher,absAngle,selector);
+ MaNGOS::WorldObjectWorker<MaNGOS::NearUsedPosDo> worker(u_do);
+
+ TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::NearUsedPosDo>, GridTypeMapContainer > grid_obj_worker(worker);
+ TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::NearUsedPosDo>, WorldTypeMapContainer > world_obj_worker(worker);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_obj_worker, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, world_obj_worker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // maybe can just place in primary position
+ if( selector.CheckOriginal() )
+ {
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+
+ if(IsWithinLOS(x,y,z))
+ return;
+
+ first_los_conflict = true; // first point have LOS problems
+ }
+
+ float angle; // candidate of angle for free pos
+
+ // special case when one from list empty and then empty side preferred
+ if(selector.FirstAngle(angle))
+ {
+ GetNearPoint2D(x,y,distance2d,absAngle+angle);
+ z = GetPositionZ();
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+
+ if(IsWithinLOS(x,y,z))
+ return;
+ }
+
+ // set first used pos in lists
+ selector.InitializeAngle();
+
+ // select in positions after current nodes (selection one by one)
+ while(selector.NextAngle(angle)) // angle for free pos
+ {
+ GetNearPoint2D(x,y,distance2d,absAngle+angle);
+ z = GetPositionZ();
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+
+ if(IsWithinLOS(x,y,z))
+ return;
+ }
+
+ // BAD NEWS: not free pos (or used or have LOS problems)
+ // Attempt find _used_ pos without LOS problem
+
+ if(!first_los_conflict)
+ {
+ x = first_x;
+ y = first_y;
+
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+ return;
+ }
+
+ // special case when one from list empty and then empty side preferred
+ if( selector.IsNonBalanced() )
+ {
+ if(!selector.FirstAngle(angle)) // _used_ pos
+ {
+ GetNearPoint2D(x,y,distance2d,absAngle+angle);
+ z = GetPositionZ();
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+
+ if(IsWithinLOS(x,y,z))
+ return;
+ }
+ }
+
+ // set first used pos in lists
+ selector.InitializeAngle();
+
+ // select in positions after current nodes (selection one by one)
+ while(selector.NextUsedAngle(angle)) // angle for used pos but maybe without LOS problem
+ {
+ GetNearPoint2D(x,y,distance2d,absAngle+angle);
+ z = GetPositionZ();
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+
+ if(IsWithinLOS(x,y,z))
+ return;
+ }
+
+ // BAD BAD NEWS: all found pos (free and used) have LOS problem :(
+ x = first_x;
+ y = first_y;
+
+ UpdateGroundPositionZ(x,y,z); // update to LOS height if available
+}
diff --git a/src/game/Object.h b/src/game/Object.h
new file mode 100644
index 00000000000..c870e9452c9
--- /dev/null
+++ b/src/game/Object.h
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _OBJECT_H
+#define _OBJECT_H
+
+#include "Common.h"
+#include "ByteBuffer.h"
+#include "UpdateFields.h"
+#include "UpdateData.h"
+#include "GameSystem/GridReference.h"
+#include "ObjectDefines.h"
+
+#include <set>
+#include <string>
+
+#define CONTACT_DISTANCE 0.5f
+#define INTERACTION_DISTANCE 5
+#define ATTACK_DISTANCE 5
+#define DETECT_DISTANCE 20 // max distance to successful detect stealthed unit
+#define MAX_VISIBILITY_DISTANCE (5*SIZE_OF_GRID_CELL/2.0f) // max distance for visible object show, limited by active zone for player based at cell size (active zone = 5x5 cells)
+#define DEFAULT_VISIBILITY_DISTANCE (SIZE_OF_GRID_CELL) // default visible distance
+
+#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects
+
+enum TypeMask
+{
+ TYPEMASK_OBJECT = 0x0001,
+ TYPEMASK_ITEM = 0x0002,
+ TYPEMASK_CONTAINER = 0x0006, // TYPEMASK_ITEM | 0x0004
+ TYPEMASK_UNIT = 0x0008,
+ TYPEMASK_PLAYER = 0x0010,
+ TYPEMASK_GAMEOBJECT = 0x0020,
+ TYPEMASK_DYNAMICOBJECT = 0x0040,
+ TYPEMASK_CORPSE = 0x0080,
+ TYPEMASK_AIGROUP = 0x0100,
+ TYPEMASK_AREATRIGGER = 0x0200
+};
+
+enum TypeID
+{
+ TYPEID_OBJECT = 0,
+ TYPEID_ITEM = 1,
+ TYPEID_CONTAINER = 2,
+ TYPEID_UNIT = 3,
+ TYPEID_PLAYER = 4,
+ TYPEID_GAMEOBJECT = 5,
+ TYPEID_DYNAMICOBJECT = 6,
+ TYPEID_CORPSE = 7,
+ TYPEID_AIGROUP = 8,
+ TYPEID_AREATRIGGER = 9
+};
+
+uint32 GuidHigh2TypeId(uint32 guid_hi);
+
+enum TempSummonType
+{
+ TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 1, // despawns after a specified time OR when the creature disappears
+ TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN = 2, // despawns after a specified time OR when the creature dies
+ TEMPSUMMON_TIMED_DESPAWN = 3, // despawns after a specified time
+ TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT = 4, // despawns after a specified time after the creature is out of combat
+ TEMPSUMMON_CORPSE_DESPAWN = 5, // despawns instantly after death
+ TEMPSUMMON_CORPSE_TIMED_DESPAWN = 6, // despawns after a specified time after death
+ TEMPSUMMON_DEAD_DESPAWN = 7, // despawns when the creature disappears
+ TEMPSUMMON_MANUAL_DESPAWN = 8 // despawns when UnSummon() is called
+};
+
+class WorldPacket;
+class UpdateData;
+class ByteBuffer;
+class WorldSession;
+class Creature;
+class Player;
+class Map;
+class UpdateMask;
+class InstanceData;
+
+typedef HM_NAMESPACE::hash_map<Player*, UpdateData> UpdateDataMapType;
+
+struct WorldLocation
+{
+ uint32 mapid;
+ float x;
+ float y;
+ float z;
+ float o;
+ explicit WorldLocation(uint32 mapid = 0, float x = 0, float y = 0, float z = 0, float o = 0)
+ : mapid(mapid), x(x), y(y), z(z), o(o) {}
+ WorldLocation(WorldLocation const &loc)
+ : mapid(loc.mapid), x(loc.x), y(loc.y), z(loc.z), o(loc.o) {}
+};
+
+class MANGOS_DLL_SPEC Object
+{
+ public:
+ virtual ~Object ( );
+
+ const bool& IsInWorld() const { return m_inWorld; }
+ virtual void AddToWorld()
+ {
+ if(m_inWorld)
+ return;
+
+ m_inWorld = true;
+
+ // synchronize values mirror with values array (changes will send in updatecreate opcode any way
+ ClearUpdateMask(true);
+ }
+ virtual void RemoveFromWorld()
+ {
+ // if we remove from world then sending changes not required
+ if(m_uint32Values)
+ ClearUpdateMask(true);
+ m_inWorld = false;
+ }
+
+ const uint64& GetGUID() const { return GetUInt64Value(0); }
+ uint32 GetGUIDLow() const { return GUID_LOPART(GetUInt64Value(0)); }
+ uint32 GetGUIDMid() const { return GUID_ENPART(GetUInt64Value(0)); }
+ uint32 GetGUIDHigh() const { return GUID_HIPART(GetUInt64Value(0)); }
+ const ByteBuffer& GetPackGUID() const { return m_PackGUID; }
+ uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); }
+
+ uint8 GetTypeId() const { return m_objectTypeId; }
+ bool isType(uint16 mask) const { return (mask & m_objectType); }
+
+ virtual void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const;
+ void SendUpdateToPlayer(Player* player);
+
+ void BuildValuesUpdateBlockForPlayer( UpdateData *data, Player *target ) const;
+ void BuildOutOfRangeUpdateBlock( UpdateData *data ) const;
+ void BuildMovementUpdateBlock( UpdateData * data, uint32 flags = 0 ) const;
+ void BuildUpdate(UpdateDataMapType &);
+
+ virtual void DestroyForPlayer( Player *target ) const;
+
+ const int32& GetInt32Value( uint16 index ) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false) );
+ return m_int32Values[ index ];
+ }
+
+ const uint32& GetUInt32Value( uint16 index ) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false) );
+ return m_uint32Values[ index ];
+ }
+
+ const uint64& GetUInt64Value( uint16 index ) const
+ {
+ ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , false) );
+ return *((uint64*)&(m_uint32Values[ index ]));
+ }
+
+ const float& GetFloatValue( uint16 index ) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false ) );
+ return m_floatValues[ index ];
+ }
+
+ uint8 GetByteValue( uint16 index, uint8 offset) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false) );
+ ASSERT( offset < 4 );
+ return *(((uint8*)&m_uint32Values[ index ])+offset);
+ }
+
+ uint8 GetUInt16Value( uint16 index, uint8 offset) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false) );
+ ASSERT( offset < 2 );
+ return *(((uint16*)&m_uint32Values[ index ])+offset);
+ }
+
+ void SetInt32Value( uint16 index, int32 value );
+ void SetUInt32Value( uint16 index, uint32 value );
+ void SetUInt64Value( uint16 index, const uint64 &value );
+ void SetFloatValue( uint16 index, float value );
+ void SetByteValue( uint16 index, uint8 offset, uint8 value );
+ void SetUInt16Value( uint16 index, uint8 offset, uint16 value );
+ void SetInt16Value( uint16 index, uint8 offset, int16 value ) { SetUInt16Value(index,offset,(uint16)value); }
+ void SetStatFloatValue( uint16 index, float value);
+ void SetStatInt32Value( uint16 index, int32 value);
+
+ void ApplyModUInt32Value(uint16 index, int32 val, bool apply);
+ void ApplyModInt32Value(uint16 index, int32 val, bool apply);
+ void ApplyModUInt64Value(uint16 index, int32 val, bool apply);
+ void ApplyModPositiveFloatValue( uint16 index, float val, bool apply);
+ void ApplyModSignedFloatValue( uint16 index, float val, bool apply);
+
+ void ApplyPercentModFloatValue(uint16 index, float val, bool apply)
+ {
+ val = val != -100.0f ? val : -99.9f ;
+ SetFloatValue(index, GetFloatValue(index) * (apply?(100.0f+val)/100.0f : 100.0f / (100.0f+val)) );
+ }
+
+ void SetFlag( uint16 index, uint32 newFlag );
+ void RemoveFlag( uint16 index, uint32 oldFlag );
+
+ void ToggleFlag( uint16 index, uint32 flag)
+ {
+ if(HasFlag(index, flag))
+ RemoveFlag(index, flag);
+ else
+ SetFlag(index, flag);
+ }
+
+ bool HasFlag( uint16 index, uint32 flag ) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false ) );
+ return (m_uint32Values[ index ] & flag) != 0;
+ }
+
+ void ApplyModFlag( uint16 index, uint32 flag, bool apply)
+ {
+ if(apply) SetFlag(index,flag); else RemoveFlag(index,flag);
+ }
+
+ void SetFlag64( uint16 index, uint64 newFlag )
+ {
+ uint64 oldval = GetUInt64Value(index);
+ uint64 newval = oldval | newFlag;
+ SetUInt64Value(index,newval);
+ }
+
+ void RemoveFlag64( uint16 index, uint64 oldFlag )
+ {
+ uint64 oldval = GetUInt64Value(index);
+ uint64 newval = oldval & ~oldFlag;
+ SetUInt64Value(index,newval);
+ }
+
+ void ToggleFlag64( uint16 index, uint64 flag)
+ {
+ if(HasFlag64(index, flag))
+ RemoveFlag64(index, flag);
+ else
+ SetFlag64(index, flag);
+ }
+
+ bool HasFlag64( uint16 index, uint64 flag ) const
+ {
+ ASSERT( index < m_valuesCount || PrintIndexError( index , false ) );
+ return (GetUInt64Value( index ) & flag) != 0;
+ }
+
+ void ApplyModFlag64( uint16 index, uint64 flag, bool apply)
+ {
+ if(apply) SetFlag64(index,flag); else RemoveFlag64(index,flag);
+ }
+
+ void ClearUpdateMask(bool remove);
+ void SendUpdateObjectToAllExcept(Player* exceptPlayer);
+
+ bool LoadValues(const char* data);
+
+ uint16 GetValuesCount() const { return m_valuesCount; }
+
+ void InitValues() { _InitValues(); }
+
+ virtual bool hasQuest(uint32 /* quest_id */) const { return false; }
+ virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; }
+ protected:
+
+ Object ( );
+
+ void _InitValues();
+ void _Create (uint32 guidlow, uint32 entry, HighGuid guidhigh);
+
+ virtual void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
+
+ virtual void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
+ void _BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const;
+ void _BuildValuesUpdate(uint8 updatetype, ByteBuffer *data, UpdateMask *updateMask, Player *target ) const;
+
+ uint16 m_objectType;
+
+ uint8 m_objectTypeId;
+ uint8 m_updateFlag;
+
+ union
+ {
+ int32 *m_int32Values;
+ uint32 *m_uint32Values;
+ float *m_floatValues;
+ };
+
+ uint32 *m_uint32Values_mirror;
+
+ uint16 m_valuesCount;
+
+ bool m_objectUpdated;
+
+ private:
+ bool m_inWorld;
+
+ ByteBuffer m_PackGUID;
+
+ // for output helpfull error messages from asserts
+ bool PrintIndexError(uint32 index, bool set) const;
+ Object(const Object&); // prevent generation copy constructor
+ Object& operator=(Object const&); // prevent generation assigment operator
+};
+
+class MANGOS_DLL_SPEC WorldObject : public Object
+{
+ public:
+ virtual ~WorldObject ( ) {}
+
+ virtual void Update ( uint32 /*time_diff*/ ) { }
+
+ void _Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid );
+
+ void Relocate(float x, float y, float z, float orientation)
+ {
+ m_positionX = x;
+ m_positionY = y;
+ m_positionZ = z;
+ m_orientation = orientation;
+ }
+
+ void Relocate(float x, float y, float z)
+ {
+ m_positionX = x;
+ m_positionY = y;
+ m_positionZ = z;
+ }
+
+ void Relocate(WorldLocation const & loc)
+ {
+ SetMapId(loc.mapid);
+ Relocate(loc.x, loc.y, loc.z, loc.o);
+ }
+
+ void SetOrientation(float orientation) { m_orientation = orientation; }
+
+ float GetPositionX( ) const { return m_positionX; }
+ float GetPositionY( ) const { return m_positionY; }
+ float GetPositionZ( ) const { return m_positionZ; }
+ 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 = GetMapId(); GetPosition(loc.x, loc.y, loc.z); loc.o = GetOrientation(); }
+ float GetOrientation( ) const { return m_orientation; }
+ void GetNearPoint2D( float &x, float &y, float distance, float absAngle) const;
+ void GetNearPoint( WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d,float absAngle) const;
+ void GetClosePoint(float &x, float &y, float &z, float size, float distance2d = 0, float angle = 0) const
+ {
+ // angle calculated from current orientation
+ GetNearPoint(NULL,x,y,z,size,distance2d,GetOrientation() + angle);
+ }
+ void GetContactPoint( const WorldObject* obj, float &x, float &y, float &z, float distance2d = CONTACT_DISTANCE) const
+ {
+ // angle to face `obj` to `this` using distance includes size of `obj`
+ GetNearPoint(obj,x,y,z,obj->GetObjectSize(),distance2d,GetAngle( obj ));
+ }
+
+ float GetObjectSize() const
+ {
+ return ( m_valuesCount > UNIT_FIELD_BOUNDINGRADIUS ) ? m_floatValues[UNIT_FIELD_BOUNDINGRADIUS] : DEFAULT_WORLD_OBJECT_SIZE;
+ }
+ bool IsPositionValid() const;
+ void UpdateGroundPositionZ(float x, float y, float &z) const;
+
+ 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; }
+
+ uint32 GetMapId() const { return m_mapId; }
+
+ uint32 GetZoneId() const;
+ uint32 GetAreaId() const;
+
+ InstanceData* GetInstanceData();
+
+ const char* GetName() const { return m_name.c_str(); }
+ void SetName(std::string newname) { m_name=newname; }
+
+ float GetDistance( const WorldObject* obj ) const;
+ float GetDistance(const float x, const float y, const float z) const;
+ float GetDistance2d(const WorldObject* obj) const;
+ float GetDistance2d(const float x, const float y) const;
+ float GetDistanceZ(const WorldObject* obj) const;
+ bool IsInMap(const WorldObject* obj) const { return GetMapId()==obj->GetMapId() && GetInstanceId()==obj->GetInstanceId(); }
+ bool IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const;
+ bool IsWithinLOS(const float x, const float y, const float z ) const;
+ bool IsWithinLOSInMap(const WorldObject* obj) const;
+
+ float GetAngle( const WorldObject* obj ) const;
+ float GetAngle( const float x, const float y ) const;
+ bool HasInArc( const float arcangle, const WorldObject* obj ) const;
+
+ virtual void SendMessageToSet(WorldPacket *data, bool self);
+ virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self);
+ void BuildHeartBeatMsg( WorldPacket *data ) const;
+ void BuildTeleportAckMsg( WorldPacket *data, float x, float y, float z, float ang) const;
+ bool IsBeingTeleported() { return mSemaphoreTeleport; }
+ void SetSemaphoreTeleport(bool semphsetting) { mSemaphoreTeleport = semphsetting; }
+
+ void MonsterSay(const char* text, uint32 language, uint64 TargetGuid);
+ void MonsterYell(const char* text, uint32 language, uint64 TargetGuid);
+ void MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false);
+ void MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper = false);
+ void MonsterSay(int32 textId, uint32 language, uint64 TargetGuid);
+ void MonsterYell(int32 textId, uint32 language, uint64 TargetGuid);
+ void MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false);
+ void MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper = false);
+ void BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 TargetGuid) const;
+
+ void SendObjectDeSpawnAnim(uint64 guid);
+
+ virtual void SaveRespawnTime() {}
+
+ uint32 GetInstanceId() const { return m_InstanceId; }
+ void SetInstanceId(uint32 val) { m_InstanceId = val; }
+
+ void AddObjectToRemoveList();
+
+ // main visibility check function in normal case (ignore grey zone distance check)
+ bool isVisibleFor(Player const* u) const { return isVisibleForInState(u,false); }
+
+ // low level function for visibility change code, must be define in all main world object subclasses
+ virtual bool isVisibleForInState(Player const* u, bool inVisibleList) const = 0;
+
+ Map * GetMap() const;
+ Map const* GetBaseMap() const;
+ Creature* SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime);
+
+ protected:
+ explicit WorldObject();
+ std::string m_name;
+
+ private:
+ uint32 m_mapId;
+
+ float m_positionX;
+ float m_positionY;
+ float m_positionZ;
+ float m_orientation;
+
+ bool mSemaphoreTeleport;
+
+ uint32 m_InstanceId;
+};
+#endif
diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp
new file mode 100644
index 00000000000..702bdc72123
--- /dev/null
+++ b/src/game/ObjectAccessor.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "Policies/SingletonImp.h"
+#include "Player.h"
+#include "Creature.h"
+#include "GameObject.h"
+#include "DynamicObject.h"
+#include "Corpse.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "Item.h"
+#include "Corpse.h"
+#include "GridNotifiers.h"
+#include "MapManager.h"
+#include "Map.h"
+#include "CellImpl.h"
+#include "GridNotifiersImpl.h"
+#include "Opcodes.h"
+#include "ObjectDefines.h"
+#include "MapInstanced.h"
+
+#include <cmath>
+
+#define CLASS_LOCK MaNGOS::ClassLevelLockable<ObjectAccessor, ZThread::FastMutex>
+INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK);
+INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ZThread::FastMutex);
+
+namespace MaNGOS
+{
+
+ struct MANGOS_DLL_DECL BuildUpdateForPlayer
+ {
+ Player &i_player;
+ UpdateDataMapType &i_updatePlayers;
+
+ BuildUpdateForPlayer(Player &player, UpdateDataMapType &data_map) : i_player(player), i_updatePlayers(data_map) {}
+
+ void Visit(PlayerMapType &m)
+ {
+ for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ if( iter->getSource() == &i_player )
+ continue;
+
+ UpdateDataMapType::iterator iter2 = i_updatePlayers.find(iter->getSource());
+ if( iter2 == i_updatePlayers.end() )
+ {
+ std::pair<UpdateDataMapType::iterator, bool> p = i_updatePlayers.insert( ObjectAccessor::UpdateDataValueType(iter->getSource(), UpdateData()) );
+ assert(p.second);
+ iter2 = p.first;
+ }
+
+ i_player.BuildValuesUpdateBlockForPlayer(&iter2->second, iter2->first);
+ }
+ }
+
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+}
+
+ObjectAccessor::ObjectAccessor() {}
+ObjectAccessor::~ObjectAccessor() {}
+
+Creature*
+ObjectAccessor::GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint32 npcflagmask)
+{
+ // unit checks
+ if (!guid)
+ return NULL;
+
+ // exist
+ Creature *unit = GetCreature(player, guid);
+ if (!unit)
+ return NULL;
+
+ // player check
+ if(!player.CanInteractWithNPCs(!unit->isSpiritService()))
+ return NULL;
+
+ // appropriate npc type
+ if(npcflagmask && !unit->HasFlag( UNIT_NPC_FLAGS, npcflagmask ))
+ return NULL;
+
+ // alive or spirit healer
+ if(!unit->isAlive() && (!unit->isSpiritService() || player.isAlive() ))
+ return NULL;
+
+ // not allow interaction under control
+ if(unit->GetCharmerOrOwnerGUID())
+ return NULL;
+
+ // not enemy
+ if( unit->IsHostileTo(&player))
+ return NULL;
+
+ // not unfriendly
+ FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction());
+ if(factionTemplate)
+ {
+ FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction);
+ if( faction->reputationListID >= 0 && player.GetReputationRank(faction) <= REP_UNFRIENDLY)
+ return NULL;
+ }
+
+ // not too far
+ if(!unit->IsWithinDistInMap(&player,INTERACTION_DISTANCE))
+ return NULL;
+
+ return unit;
+}
+
+Creature*
+ObjectAccessor::GetCreatureOrPet(WorldObject const &u, uint64 guid)
+{
+ if(Creature *unit = GetPet(guid))
+ return unit;
+
+ return GetCreature(u, guid);
+}
+
+Creature*
+ObjectAccessor::GetCreature(WorldObject const &u, uint64 guid)
+{
+ Creature * ret = GetObjectInWorld(guid, (Creature*)NULL);
+ if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL;
+ return ret;
+}
+
+Unit*
+ObjectAccessor::GetUnit(WorldObject const &u, uint64 guid)
+{
+ if(!guid)
+ return NULL;
+
+ if(IS_PLAYER_GUID(guid))
+ return FindPlayer(guid);
+
+ return GetCreatureOrPet(u, guid);
+}
+
+Corpse*
+ObjectAccessor::GetCorpse(WorldObject const &u, uint64 guid)
+{
+ Corpse * ret = GetObjectInWorld(guid, (Corpse*)NULL);
+ if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL;
+ return ret;
+}
+
+Object* ObjectAccessor::GetObjectByTypeMask(Player const &p, uint64 guid, uint32 typemask)
+{
+ Object *obj = NULL;
+
+ if(typemask & TYPEMASK_PLAYER)
+ {
+ obj = FindPlayer(guid);
+ if(obj) return obj;
+ }
+
+ if(typemask & TYPEMASK_UNIT)
+ {
+ obj = GetCreatureOrPet(p,guid);
+ if(obj) return obj;
+ }
+
+ if(typemask & TYPEMASK_GAMEOBJECT)
+ {
+ obj = GetGameObject(p,guid);
+ if(obj) return obj;
+ }
+
+ if(typemask & TYPEMASK_DYNAMICOBJECT)
+ {
+ obj = GetDynamicObject(p,guid);
+ if(obj) return obj;
+ }
+
+ if(typemask & TYPEMASK_ITEM)
+ {
+ obj = p.GetItemByGuid( guid );
+ if(obj) return obj;
+ }
+
+ return NULL;
+}
+
+GameObject*
+ObjectAccessor::GetGameObject(WorldObject const &u, uint64 guid)
+{
+ GameObject * ret = GetObjectInWorld(guid, (GameObject*)NULL);
+ if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL;
+ return ret;
+}
+
+DynamicObject*
+ObjectAccessor::GetDynamicObject(Unit const &u, uint64 guid)
+{
+ DynamicObject * ret = GetObjectInWorld(guid, (DynamicObject*)NULL);
+ if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL;
+ return ret;
+}
+
+Player*
+ObjectAccessor::FindPlayer(uint64 guid)
+{
+ return GetObjectInWorld(guid, (Player*)NULL);
+}
+
+Player*
+ObjectAccessor::FindPlayerByName(const char *name)
+{
+ //TODO: Player Guard
+ 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 )
+ return iter->second;
+ return NULL;
+}
+
+void
+ObjectAccessor::SaveAllPlayers()
+{
+ Guard guard(*HashMapHolder<Player*>::GetLock());
+ HashMapHolder<Player>::MapType& m = HashMapHolder<Player>::GetContainer();
+ HashMapHolder<Player>::MapType::iterator itr = m.begin();
+ for(; itr != m.end(); ++itr)
+ itr->second->SaveToDB();
+}
+
+void
+ObjectAccessor::_update()
+{
+ UpdateDataMapType update_players;
+ {
+ Guard guard(i_updateGuard);
+ while(!i_objects.empty())
+ {
+ Object* obj = *i_objects.begin();
+ i_objects.erase(i_objects.begin());
+ if (!obj)
+ continue;
+ _buildUpdateObject(obj, update_players);
+ obj->ClearUpdateMask(false);
+ }
+ }
+
+ WorldPacket packet; // here we allocate a std::vector with a size of 0x10000
+ for(UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
+ {
+ iter->second.BuildPacket(&packet);
+ iter->first->GetSession()->SendPacket(&packet);
+ packet.clear(); // clean the string
+ }
+}
+
+void
+ObjectAccessor::UpdateObject(Object* obj, Player* exceptPlayer)
+{
+ UpdateDataMapType update_players;
+ obj->BuildUpdate(update_players);
+
+ WorldPacket packet;
+ for(UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
+ {
+ if(iter->first == exceptPlayer)
+ continue;
+
+ iter->second.BuildPacket(&packet);
+ iter->first->GetSession()->SendPacket(&packet);
+ packet.clear();
+ }
+}
+
+void
+ObjectAccessor::AddUpdateObject(Object *obj)
+{
+ Guard guard(i_updateGuard);
+ i_objects.insert(obj);
+}
+
+void
+ObjectAccessor::RemoveUpdateObject(Object *obj)
+{
+ Guard guard(i_updateGuard);
+ std::set<Object *>::iterator iter = i_objects.find(obj);
+ if( iter != i_objects.end() )
+ i_objects.erase( iter );
+}
+
+void
+ObjectAccessor::_buildUpdateObject(Object *obj, UpdateDataMapType &update_players)
+{
+ bool build_for_all = true;
+ Player *pl = NULL;
+ if( obj->isType(TYPEMASK_ITEM) )
+ {
+ Item *item = static_cast<Item *>(obj);
+ pl = item->GetOwner();
+ build_for_all = false;
+ }
+
+ if( pl != NULL )
+ _buildPacket(pl, obj, update_players);
+
+ // Capt: okey for all those fools who think its a real fix
+ // THIS IS A TEMP FIX
+ if( build_for_all )
+ {
+ WorldObject * temp = dynamic_cast<WorldObject*>(obj);
+
+ //assert(dynamic_cast<WorldObject*>(obj)!=NULL);
+ if (temp)
+ _buildChangeObjectForPlayer(temp, update_players);
+ else
+ sLog.outDebug("ObjectAccessor: Ln 405 Temp bug fix");
+ }
+}
+
+void
+ObjectAccessor::_buildPacket(Player *pl, Object *obj, UpdateDataMapType &update_players)
+{
+ UpdateDataMapType::iterator iter = update_players.find(pl);
+
+ if( iter == update_players.end() )
+ {
+ std::pair<UpdateDataMapType::iterator, bool> p = update_players.insert( UpdateDataValueType(pl, UpdateData()) );
+ assert(p.second);
+ iter = p.first;
+ }
+
+ obj->BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
+}
+
+void
+ObjectAccessor::_buildChangeObjectForPlayer(WorldObject *obj, UpdateDataMapType &update_players)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+ WorldObjectChangeAccumulator notifier(*obj, update_players);
+ TypeContainerVisitor<WorldObjectChangeAccumulator, WorldTypeMapContainer > player_notifier(notifier);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, player_notifier, *MapManager::Instance().GetMap(obj->GetMapId(), obj));
+}
+
+Pet*
+ObjectAccessor::GetPet(uint64 guid)
+{
+ return GetObjectInWorld(guid, (Pet*)NULL);
+}
+
+Corpse*
+ObjectAccessor::GetCorpseForPlayerGUID(uint64 guid)
+{
+ Guard guard(i_corpseGuard);
+
+ Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid);
+ if( iter == i_player2corpse.end() ) return NULL;
+
+ assert(iter->second->GetType() != CORPSE_BONES);
+
+ return iter->second;
+}
+
+void
+ObjectAccessor::RemoveCorpse(Corpse *corpse)
+{
+ assert(corpse && corpse->GetType() != CORPSE_BONES);
+
+ Guard guard(i_corpseGuard);
+ Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID());
+ if( iter == i_player2corpse.end() )
+ return;
+
+ // build mapid*cellid -> guid_set map
+ CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ objmgr.DeleteCorpseCellData(corpse->GetMapId(),cell_id,corpse->GetOwnerGUID());
+ corpse->RemoveFromWorld();
+
+ i_player2corpse.erase(iter);
+}
+
+void
+ObjectAccessor::AddCorpse(Corpse *corpse)
+{
+ assert(corpse && corpse->GetType() != CORPSE_BONES);
+
+ Guard guard(i_corpseGuard);
+ assert(i_player2corpse.find(corpse->GetOwnerGUID()) == i_player2corpse.end());
+ i_player2corpse[corpse->GetOwnerGUID()] = corpse;
+
+ // build mapid*cellid -> guid_set map
+ CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ objmgr.AddCorpseCellData(corpse->GetMapId(),cell_id,corpse->GetOwnerGUID(),corpse->GetInstanceId());
+}
+
+void
+ObjectAccessor::AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map)
+{
+ Guard guard(i_corpseGuard);
+ for(Player2CorpsesMapType::iterator iter = i_player2corpse.begin(); iter != i_player2corpse.end(); ++iter)
+ if(iter->second->GetGrid()==gridpair)
+ {
+ // verify, if the corpse in our instance (add only corpses which are)
+ if (map->Instanceable())
+ {
+ if (iter->second->GetInstanceId() == map->GetInstanceId())
+ {
+ grid.AddWorldObject(iter->second,iter->second->GetGUID());
+ }
+ }
+ else
+ {
+ grid.AddWorldObject(iter->second,iter->second->GetGUID());
+ }
+ }
+}
+
+Corpse*
+ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid)
+{
+ Corpse *corpse = GetCorpseForPlayerGUID(player_guid);
+ if(!corpse)
+ {
+ //in fact this function is called from several places
+ //even when player doesn't have a corpse, not an error
+ //sLog.outError("ERROR: Try remove corpse that not in map for GUID %ul", player_guid);
+ return NULL;
+ }
+
+ DEBUG_LOG("Deleting Corpse and spawning bones.\n");
+
+ // remove corpse from player_guid -> corpse map
+ RemoveCorpse(corpse);
+
+ // remove resurrectble corpse from grid object registry (loaded state checked into call)
+ // do not load the map if it's not loaded
+ Map *map = MapManager::Instance().FindMap(corpse->GetMapId(), corpse->GetInstanceId());
+ if(map) map->Remove(corpse,false);
+
+ // remove corpse from DB
+ corpse->DeleteFromDB();
+
+ Corpse *bones = NULL;
+ // create the bones only if the map and the grid is loaded at the corpse's location
+ if(map && !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY()))
+ {
+ // Create bones, don't change Corpse
+ bones = new Corpse;
+ bones->Create(corpse->GetGUIDLow());
+
+ for (int i = 3; i < CORPSE_END; i++) // don't overwrite guid and object type
+ bones->SetUInt32Value(i, corpse->GetUInt32Value(i));
+
+ bones->SetGrid(corpse->GetGrid());
+ // bones->m_time = m_time; // don't overwrite time
+ // 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->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES);
+ bones->SetUInt64Value(CORPSE_FIELD_OWNER, 0);
+
+ for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ if(corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i))
+ bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0);
+ }
+
+ // add bones in grid store if grid loaded where corpse placed
+ map->Add(bones);
+ }
+
+ // all references to the corpse should be removed at this point
+ delete corpse;
+
+ return bones;
+}
+
+void
+ObjectAccessor::Update(uint32 diff)
+{
+ {
+ typedef std::multimap<uint32, Player *> CreatureLocationHolderType;
+ CreatureLocationHolderType creature_locations;
+ //TODO: Player guard
+ HashMapHolder<Player>::MapType& playerMap = HashMapHolder<Player>::GetContainer();
+ for(HashMapHolder<Player>::MapType::iterator iter = playerMap.begin(); iter != playerMap.end(); ++iter)
+ {
+ if(iter->second->IsInWorld())
+ {
+ iter->second->Update(diff);
+ creature_locations.insert( CreatureLocationHolderType::value_type(iter->second->GetMapId(), iter->second) );
+ }
+ }
+
+ Map *map;
+
+ MaNGOS::ObjectUpdater updater(diff);
+ // for creature
+ TypeContainerVisitor<MaNGOS::ObjectUpdater, GridTypeMapContainer > grid_object_update(updater);
+ // for pets
+ TypeContainerVisitor<MaNGOS::ObjectUpdater, WorldTypeMapContainer > world_object_update(updater);
+
+ for(CreatureLocationHolderType::iterator iter=creature_locations.begin(); iter != creature_locations.end(); ++iter)
+ {
+ MapManager::Instance().GetMap((*iter).first, (*iter).second)->resetMarkedCells();
+ }
+
+ for(CreatureLocationHolderType::iterator iter=creature_locations.begin(); iter != creature_locations.end(); ++iter)
+ {
+ Player *player = (*iter).second;
+ map = MapManager::Instance().GetMap((*iter).first, player);
+
+ CellPair standing_cell(MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()));
+
+ // Check for correctness of standing_cell, it also avoids problems with update_cell
+ if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
+ continue;
+
+ // the overloaded operators handle range checking
+ // so ther's no need for range checking inside the loop
+ CellPair begin_cell(standing_cell), end_cell(standing_cell);
+ begin_cell << 1; begin_cell -= 1; // upper left
+ end_cell >> 1; end_cell += 1; // lower right
+
+ for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++)
+ {
+ for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++)
+ {
+ uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
+ if( !map->isCellMarked(cell_id) )
+ {
+ CellPair cell_pair(x,y);
+ map->markCell(cell_id);
+ Cell cell(cell_pair);
+ cell.data.Part.reserved = CENTER_DISTRICT;
+ cell.SetNoCreate();
+ CellLock<NullGuard> cell_lock(cell, cell_pair);
+ cell_lock->Visit(cell_lock, grid_object_update, *map);
+ cell_lock->Visit(cell_lock, world_object_update, *map);
+ }
+ }
+ }
+ }
+ }
+
+ _update();
+}
+
+bool
+ObjectAccessor::PlayersNearGrid(uint32 x, uint32 y, uint32 m_id, uint32 i_id) const
+{
+ CellPair cell_min(x*MAX_NUMBER_OF_CELLS, y*MAX_NUMBER_OF_CELLS);
+ CellPair cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS);
+ cell_min << 2;
+ cell_min -= 2;
+ cell_max >> 2;
+ cell_max += 2;
+
+ //TODO: Guard player
+ HashMapHolder<Player>::MapType& playerMap = HashMapHolder<Player>::GetContainer();
+ for(HashMapHolder<Player>::MapType::const_iterator iter=playerMap.begin(); iter != playerMap.end(); ++iter)
+ {
+ if( m_id != iter->second->GetMapId() || i_id != iter->second->GetInstanceId() )
+ continue;
+
+ CellPair p = MaNGOS::ComputeCellPair(iter->second->GetPositionX(), iter->second->GetPositionY());
+ if( (cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) &&
+ (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord) )
+ return true;
+ }
+
+ return false;
+}
+
+void
+ObjectAccessor::WorldObjectChangeAccumulator::Visit(PlayerMapType &m)
+{
+ for(PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
+ if(iter->getSource()->HaveAtClient(&i_object))
+ ObjectAccessor::_buildPacket(iter->getSource(), &i_object, i_updateDatas);
+}
+
+void
+ObjectAccessor::UpdateObjectVisibility(WorldObject *obj)
+{
+ CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+ Cell cell(p);
+
+ MapManager::Instance().GetMap(obj->GetMapId(), obj)->UpdateObjectVisibility(obj,cell,p);
+}
+
+void ObjectAccessor::UpdateVisibilityForPlayer( Player* player )
+{
+ CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
+ Cell cell(p);
+ Map* m = MapManager::Instance().GetMap(player->GetMapId(),player);
+
+ m->UpdatePlayerVisibility(player,cell,p);
+ m->UpdateObjectsVisibilityFor(player,cell,p);
+}
+
+/// Define the static member of HashMapHolder
+
+template <class T> HM_NAMESPACE::hash_map< uint64, T* > HashMapHolder<T>::m_objectMap;
+template <class T> ZThread::FastMutex HashMapHolder<T>::i_lock;
+
+/// Global defintions for the hashmap storage
+
+template class HashMapHolder<Player>;
+template class HashMapHolder<Pet>;
+template class HashMapHolder<GameObject>;
+template class HashMapHolder<DynamicObject>;
+template class HashMapHolder<Creature>;
+template class HashMapHolder<Corpse>;
+
+template Player* ObjectAccessor::GetObjectInWorld<Player>(uint32 mapid, float x, float y, uint64 guid, Player* /*fake*/);
+template Pet* ObjectAccessor::GetObjectInWorld<Pet>(uint32 mapid, float x, float y, uint64 guid, Pet* /*fake*/);
+template Creature* ObjectAccessor::GetObjectInWorld<Creature>(uint32 mapid, float x, float y, uint64 guid, Creature* /*fake*/);
+template Corpse* ObjectAccessor::GetObjectInWorld<Corpse>(uint32 mapid, float x, float y, uint64 guid, Corpse* /*fake*/);
+template GameObject* ObjectAccessor::GetObjectInWorld<GameObject>(uint32 mapid, float x, float y, uint64 guid, GameObject* /*fake*/);
+template DynamicObject* ObjectAccessor::GetObjectInWorld<DynamicObject>(uint32 mapid, float x, float y, uint64 guid, DynamicObject* /*fake*/);
diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h
new file mode 100644
index 00000000000..feb0780e36e
--- /dev/null
+++ b/src/game/ObjectAccessor.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_OBJECTACCESSOR_H
+#define MANGOS_OBJECTACCESSOR_H
+
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "zthread/FastMutex.h"
+#include "Utilities/HashMap.h"
+#include "Policies/ThreadingModel.h"
+
+#include "ByteBuffer.h"
+#include "UpdateData.h"
+
+#include "GridDefines.h"
+#include "Object.h"
+#include "Player.h"
+
+#include <set>
+
+class Creature;
+class Corpse;
+class Unit;
+class GameObject;
+class DynamicObject;
+class WorldObject;
+class Map;
+
+template <class T>
+class HashMapHolder
+{
+ public:
+
+ typedef HM_NAMESPACE::hash_map< uint64, T* > MapType;
+ typedef ZThread::FastMutex LockType;
+ typedef MaNGOS::GeneralLock<LockType > Guard;
+
+ static void Insert(T* o) { m_objectMap[o->GetGUID()] = o; }
+
+ static void Remove(T* o)
+ {
+ Guard guard(i_lock);
+ typename MapType::iterator itr = m_objectMap.find(o->GetGUID());
+ if (itr != m_objectMap.end())
+ m_objectMap.erase(itr);
+ }
+
+ static T* Find(uint64 guid)
+ {
+ typename MapType::iterator itr = m_objectMap.find(guid);
+ return (itr != m_objectMap.end()) ? itr->second : NULL;
+ }
+
+ static MapType& GetContainer() { return m_objectMap; }
+
+ static LockType* GetLock() { return &i_lock; }
+ private:
+
+ //Non instanciable only static
+ HashMapHolder() {}
+
+ static LockType i_lock;
+ static MapType m_objectMap;
+};
+
+class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, MaNGOS::ClassLevelLockable<ObjectAccessor, ZThread::FastMutex> >
+{
+
+ friend class MaNGOS::OperatorNew<ObjectAccessor>;
+ ObjectAccessor();
+ ~ObjectAccessor();
+ ObjectAccessor(const ObjectAccessor &);
+ ObjectAccessor& operator=(const ObjectAccessor &);
+
+ public:
+ typedef HM_NAMESPACE::hash_map<uint64, Corpse* > Player2CorpsesMapType;
+ typedef HM_NAMESPACE::hash_map<Player*, UpdateData>::value_type UpdateDataValueType;
+
+ template<class T> static T* GetObjectInWorld(uint64 guid, T* /*fake*/)
+ {
+ return HashMapHolder<T>::Find(guid);
+ }
+
+ static Unit* GetObjectInWorld(uint64 guid, Unit* /*fake*/)
+ {
+ if(!guid)
+ return NULL;
+
+ if (IS_PLAYER_GUID(guid))
+ return (Unit*)HashMapHolder<Player>::Find(guid);
+
+ if (Unit* u = (Unit*)HashMapHolder<Pet>::Find(guid))
+ return u;
+
+ return (Unit*)HashMapHolder<Creature>::Find(guid);
+ }
+
+ template<class T> static T* GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, T* /*fake*/)
+ {
+ T* obj = HashMapHolder<T>::Find(guid);
+ if(!obj || obj->GetMapId() != mapid) return NULL;
+
+ CellPair p = MaNGOS::ComputeCellPair(x,y);
+ if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("ObjectAccessor::GetObjectInWorld: invalid coordinates supplied X:%f Y:%f grid cell [%u:%u]", x, y, p.x_coord, p.y_coord);
+ return NULL;
+ }
+
+ CellPair q = MaNGOS::ComputeCellPair(obj->GetPositionX(),obj->GetPositionY());
+ if(q.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || q.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
+ {
+ sLog.outError("ObjectAccessor::GetObjecInWorld: object "I64FMTD" has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), q.x_coord, q.y_coord);
+ return NULL;
+ }
+
+ int32 dx = int32(p.x_coord) - int32(q.x_coord);
+ int32 dy = int32(p.y_coord) - int32(q.y_coord);
+
+ if (dx > -2 && dx < 2 && dy > -2 && dy < 2) return obj;
+ else return NULL;
+ }
+
+ static Object* GetObjectByTypeMask(Player const &, uint64, uint32 typemask);
+ static Creature* GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint32 npcflagmask);
+ static Creature* GetCreature(WorldObject const &, uint64);
+ static Creature* GetCreatureOrPet(WorldObject const &, uint64);
+ static Unit* GetUnit(WorldObject const &, uint64);
+ static Pet* GetPet(Unit const &, uint64 guid) { return GetPet(guid); }
+ static Player* GetPlayer(Unit const &, uint64 guid) { return FindPlayer(guid); }
+ static GameObject* GetGameObject(WorldObject const &, uint64);
+ static DynamicObject* GetDynamicObject(Unit const &, uint64);
+ static Corpse* GetCorpse(WorldObject const &u, uint64 guid);
+ static Pet* GetPet(uint64 guid);
+ static Player* FindPlayer(uint64);
+
+ Player* FindPlayerByName(const char *name) ;
+
+ HashMapHolder<Player>::MapType& GetPlayers()
+ {
+ return HashMapHolder<Player>::GetContainer();
+ }
+
+ template<class T> void AddObject(T *object)
+ {
+ HashMapHolder<T>::Insert(object);
+ }
+
+ template<class T> void RemoveObject(T *object)
+ {
+ HashMapHolder<T>::Remove(object);
+ }
+
+ void RemoveObject(Player *pl)
+ {
+ HashMapHolder<Player>::Remove(pl);
+
+ Guard guard(i_updateGuard);
+
+ std::set<Object *>::iterator iter2 = std::find(i_objects.begin(), i_objects.end(), (Object *)pl);
+ if( iter2 != i_objects.end() )
+ i_objects.erase(iter2);
+ }
+
+ void SaveAllPlayers();
+
+ void AddUpdateObject(Object *obj);
+ void RemoveUpdateObject(Object *obj);
+
+ void Update(uint32 diff);
+
+ Corpse* GetCorpseForPlayerGUID(uint64 guid);
+ void RemoveCorpse(Corpse *corpse);
+ void AddCorpse(Corpse* corpse);
+ void AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map);
+ Corpse* ConvertCorpseForPlayer(uint64 player_guid);
+
+ bool PlayersNearGrid(uint32 x,uint32 y,uint32 m_id,uint32 i_id) const;
+
+ static void UpdateObject(Object* obj, Player* exceptPlayer);
+ static void _buildUpdateObject(Object* obj, UpdateDataMapType &);
+
+ static void UpdateObjectVisibility(WorldObject* obj);
+ static void UpdateVisibilityForPlayer(Player* player);
+ private:
+ struct WorldObjectChangeAccumulator
+ {
+ UpdateDataMapType &i_updateDatas;
+ WorldObject &i_object;
+ WorldObjectChangeAccumulator(WorldObject &obj, UpdateDataMapType &d) : i_updateDatas(d), i_object(obj) {}
+ void Visit(PlayerMapType &);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ friend struct WorldObjectChangeAccumulator;
+ Player2CorpsesMapType i_player2corpse;
+
+ typedef ZThread::FastMutex LockType;
+ typedef MaNGOS::GeneralLock<LockType > Guard;
+
+ static void _buildChangeObjectForPlayer(WorldObject *, UpdateDataMapType &);
+ static void _buildPacket(Player *, Object *, UpdateDataMapType &);
+ void _update(void);
+ std::set<Object *> i_objects;
+ LockType i_playerGuard;
+ LockType i_updateGuard;
+ LockType i_corpseGuard;
+ LockType i_petGuard;
+};
+#endif
diff --git a/src/game/ObjectDefines.h b/src/game/ObjectDefines.h
new file mode 100644
index 00000000000..ca0fecef5a6
--- /dev/null
+++ b/src/game/ObjectDefines.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_OBJECTDEFINES_H
+#define MANGOS_OBJECTDEFINES_H
+
+#include "Platform/Define.h"
+
+// used for creating values for respawn for example
+#define MAKE_PAIR64(l, h) uint64( uint32(l) | ( uint64(h) << 32 ) )
+#define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & 0x00000000FFFFFFFFLL)
+#define PAIR64_LOPART(x) (uint32)(uint64(x) & 0x00000000FFFFFFFFLL)
+
+#define MAKE_PAIR32(l, h) uint32( uint16(l) | ( uint32(h) << 16 ) )
+#define PAIR32_HIPART(x) (uint16)((uint32(x) >> 16) & 0x0000FFFF)
+#define PAIR32_LOPART(x) (uint16)(uint32(x) & 0x0000FFFF)
+
+enum HighGuid
+{
+ HIGHGUID_ITEM = 0x4000, // blizz 4000
+ HIGHGUID_CONTAINER = 0x4000, // blizz 4000
+ HIGHGUID_PLAYER = 0x0000, // blizz 0000
+ HIGHGUID_GAMEOBJECT = 0xF110, // blizz F110
+ HIGHGUID_TRANSPORT = 0xF120, // blizz F120 (for GAMEOBJECT_TYPE_TRANSPORT)
+ HIGHGUID_UNIT = 0xF130, // blizz F130
+ HIGHGUID_PET = 0xF140, // blizz F140
+ HIGHGUID_DYNAMICOBJECT = 0xF100, // blizz F100
+ HIGHGUID_CORPSE = 0xF101, // blizz F100
+ HIGHGUID_MO_TRANSPORT = 0x1FC0, // blizz 1FC0 (for GAMEOBJECT_TYPE_MO_TRANSPORT)
+};
+
+#define IS_EMPTY_GUID(Guid) ( Guid == 0 )
+
+#define IS_CREATURE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_UNIT )
+#define IS_PET_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PET )
+#define IS_CREATURE_OR_PET_GUID(Guid)( IS_CREATURE_GUID(Guid) || IS_PET_GUID(Guid) )
+#define IS_PLAYER_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PLAYER && Guid!=0 )
+#define IS_UNIT_GUID(Guid) ( IS_CREATURE_OR_PET_GUID(Guid) || IS_PLAYER_GUID(Guid) )
+ // special case for empty guid need check
+#define IS_ITEM_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_ITEM )
+#define IS_GAMEOBJECT_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_GAMEOBJECT )
+#define IS_DYNAMICOBJECT_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_DYNAMICOBJECT )
+#define IS_CORPSE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_CORPSE )
+#define IS_TRANSPORT(Guid) ( GUID_HIPART(Guid) == HIGHGUID_TRANSPORT )
+#define IS_MO_TRANSPORT(Guid) ( GUID_HIPART(Guid) == HIGHGUID_MO_TRANSPORT )
+
+// l - OBJECT_FIELD_GUID
+// e - OBJECT_FIELD_ENTRY for GO (except GAMEOBJECT_TYPE_MO_TRANSPORT) and creatures or UNIT_FIELD_PETNUMBER for pets
+// h - OBJECT_FIELD_GUID + 1
+#define MAKE_NEW_GUID(l, e, h) uint64( uint64(l) | ( uint64(e) << 24 ) | ( uint64(h) << 48 ) )
+
+#define GUID_HIPART(x) (uint32)((uint64(x) >> 48) & 0x0000FFFF)
+
+// We have different low and middle part size for different guid types
+#define _GUID_ENPART_2(x) 0
+#define _GUID_ENPART_3(x) (uint32)((uint64(x) >> 24) & 0x0000000000FFFFFFLL)
+#define _GUID_LOPART_2(x) (uint32)(uint64(x) & 0x00000000FFFFFFFFLL)
+#define _GUID_LOPART_3(x) (uint32)(uint64(x) & 0x0000000000FFFFFFLL)
+
+inline bool IsGuidHaveEnPart(uint64 const& guid)
+{
+ switch(GUID_HIPART(guid))
+ {
+ case HIGHGUID_ITEM:
+ case HIGHGUID_PLAYER:
+ case HIGHGUID_DYNAMICOBJECT:
+ case HIGHGUID_CORPSE:
+ return false;
+ case HIGHGUID_GAMEOBJECT:
+ case HIGHGUID_TRANSPORT:
+ case HIGHGUID_UNIT:
+ case HIGHGUID_PET:
+ case HIGHGUID_MO_TRANSPORT:
+ default:
+ return true;
+ }
+}
+
+#define GUID_ENPART(x) (IsGuidHaveEnPart(x) ? _GUID_ENPART_3(x) : _GUID_ENPART_2(x))
+#define GUID_LOPART(x) (IsGuidHaveEnPart(x) ? _GUID_LOPART_3(x) : _GUID_LOPART_2(x))
+
+inline char const* GetLogNameForGuid(uint64 guid)
+{
+ switch(GUID_HIPART(guid))
+ {
+ case HIGHGUID_ITEM: return "item";
+ case HIGHGUID_PLAYER: return guid ? "player" : "none";
+ case HIGHGUID_GAMEOBJECT: return "gameobject";
+ case HIGHGUID_TRANSPORT: return "transport";
+ case HIGHGUID_UNIT: return "creature";
+ case HIGHGUID_PET: return "pet";
+ case HIGHGUID_DYNAMICOBJECT:return "dynobject";
+ case HIGHGUID_CORPSE: return "corpse";
+ case HIGHGUID_MO_TRANSPORT: return "mo_transport";
+ default:
+ return "<unknown>";
+ }
+}
+#endif
diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp
new file mode 100644
index 00000000000..2ce17d4650f
--- /dev/null
+++ b/src/game/ObjectGridLoader.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ObjectGridLoader.h"
+#include "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "MapManager.h"
+#include "Creature.h"
+#include "GameObject.h"
+#include "DynamicObject.h"
+#include "Corpse.h"
+#include "World.h"
+#include "CellImpl.h"
+
+class MANGOS_DLL_DECL ObjectGridRespawnMover
+{
+ public:
+ ObjectGridRespawnMover() {}
+
+ void Move(GridType &grid);
+
+ template<class T> void Visit(GridRefManager<T> &) {}
+ void Visit(CreatureMapType &m);
+};
+
+void
+ObjectGridRespawnMover::Move(GridType &grid)
+{
+ TypeContainerVisitor<ObjectGridRespawnMover, GridTypeMapContainer > mover(*this);
+ grid.Visit(mover);
+}
+
+void
+ObjectGridRespawnMover::Visit(CreatureMapType &m)
+{
+ // creature in unloading grid can have respawn point in another grid
+ // if it will be unloaded then it will not respawn in original grid until unload/load original grid
+ // move to respwn point to prevent this case. For player view in respawn grid this wll be normal respawn.
+ for(CreatureMapType::iterator iter=m.begin(), next; iter != m.end(); iter = next)
+ {
+ next = iter; ++next;
+
+ Creature * c = iter->getSource();
+
+ assert(!c->isPet() && "ObjectGridRespawnMover don't must be called for pets");
+
+ Cell const& cur_cell = c->GetCurrentCell();
+
+ float resp_x, resp_y, resp_z;
+ c->GetRespawnCoord(resp_x, resp_y, resp_z);
+ CellPair resp_val = MaNGOS::ComputeCellPair(resp_x, resp_y);
+ Cell resp_cell(resp_val);
+
+ if(cur_cell.DiffGrid(resp_cell))
+ {
+ MapManager::Instance().GetMap(c->GetMapId(), c)->CreatureRespawnRelocation(c);
+ // false result ignored: will be unload with other creatures at grid
+ }
+ }
+}
+
+// for loading world object at grid loading (Corpses)
+class ObjectWorldLoader
+{
+ public:
+ explicit ObjectWorldLoader(ObjectGridLoader& gloader)
+ : i_cell(gloader.i_cell), i_grid(gloader.i_grid), i_map(gloader.i_map), i_corpses (0)
+ {}
+
+ void Visit(CorpseMapType &m);
+
+ template<class T> void Visit(GridRefManager<T>&) { }
+
+ private:
+ Cell i_cell;
+ NGridType &i_grid;
+ Map* i_map;
+ public:
+ uint32 i_corpses;
+};
+
+template<class T> void addUnitState(T* /*obj*/, CellPair const& /*cell_pair*/)
+{
+}
+
+template<> void addUnitState(Creature *obj, CellPair const& cell_pair)
+{
+ Cell cell(cell_pair);
+
+ obj->SetCurrentCell(cell);
+ if(obj->isSpiritService())
+ obj->setDeathState(DEAD);
+}
+
+template <class T>
+void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* map)
+{
+ for(CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid)
+ {
+ T* obj = new T;
+ uint32 guid = *i_guid;
+ //sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid);
+ if(!obj->LoadFromDB(guid, map))
+ {
+ delete obj;
+ continue;
+ }
+
+ obj->GetGridRef().link(&m, obj);
+
+ addUnitState(obj,cell);
+ obj->AddToWorld();
+ ++count;
+
+ }
+}
+
+void LoadHelper(CellCorpseSet const& cell_corpses, CellPair &cell, CorpseMapType &m, uint32 &count, Map* map)
+{
+ if(cell_corpses.empty())
+ return;
+
+ for(CellCorpseSet::const_iterator itr = cell_corpses.begin(); itr != cell_corpses.end(); ++itr)
+ {
+ if(itr->second != map->GetInstanceId())
+ continue;
+
+ uint32 player_guid = itr->first;
+
+ Corpse *obj = ObjectAccessor::Instance().GetCorpseForPlayerGUID(player_guid);
+ if(!obj)
+ continue;
+
+ obj->GetGridRef().link(&m, obj);
+
+ addUnitState(obj,cell);
+ obj->AddToWorld();
+ ++count;
+ }
+}
+
+void
+ObjectGridLoader::Visit(GameObjectMapType &m)
+{
+ uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX();
+ uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY();
+ CellPair cell_pair(x,y);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), i_map->GetSpawnMode(), cell_id);
+
+ LoadHelper(cell_guids.gameobjects, cell_pair, m, i_gameObjects, i_map);
+}
+
+void
+ObjectGridLoader::Visit(CreatureMapType &m)
+{
+ uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX();
+ uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY();
+ CellPair cell_pair(x,y);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), i_map->GetSpawnMode(), cell_id);
+
+ LoadHelper(cell_guids.creatures, cell_pair, m, i_creatures, i_map);
+}
+
+void
+ObjectWorldLoader::Visit(CorpseMapType &m)
+{
+ uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX();
+ uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY();
+ CellPair cell_pair(x,y);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ // corpses are always added to spawn mode 0 and they are spawned by their instance id
+ CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), 0, cell_id);
+ LoadHelper(cell_guids.corpses, cell_pair, m, i_corpses, i_map);
+}
+
+void
+ObjectGridLoader::Load(GridType &grid)
+{
+ {
+ TypeContainerVisitor<ObjectGridLoader, GridTypeMapContainer > loader(*this);
+ grid.Visit(loader);
+ }
+
+ {
+ ObjectWorldLoader wloader(*this);
+ TypeContainerVisitor<ObjectWorldLoader, WorldTypeMapContainer > loader(wloader);
+ grid.Visit(loader);
+ i_corpses = wloader.i_corpses;
+ }
+}
+
+void ObjectGridLoader::LoadN(void)
+{
+ i_gameObjects = 0; i_creatures = 0; i_corpses = 0;
+ i_cell.data.Part.cell_y = 0;
+ for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
+ {
+ i_cell.data.Part.cell_x = x;
+ for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y)
+ {
+ i_cell.data.Part.cell_y = y;
+ GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> loader;
+ loader.Load(i_grid(x, y), *this);
+ }
+ }
+ sLog.outDebug("%u GameObjects, %u Creatures, and %u Corpses/Bones loaded for grid %u on map %u", i_gameObjects, i_creatures, i_corpses,i_grid.GetGridId(), i_map->GetId());
+}
+
+void ObjectGridUnloader::MoveToRespawnN()
+{
+ for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
+ {
+ for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y)
+ {
+ ObjectGridRespawnMover mover;
+ mover.Move(i_grid(x, y));
+ }
+ }
+}
+
+void
+ObjectGridUnloader::Unload(GridType &grid)
+{
+ TypeContainerVisitor<ObjectGridUnloader, GridTypeMapContainer > unloader(*this);
+ grid.Visit(unloader);
+}
+
+template<class T>
+void
+ObjectGridUnloader::Visit(GridRefManager<T> &m)
+{
+ while(!m.isEmpty())
+ {
+ T *obj = m.getFirst()->getSource();
+ // if option set then object already saved at this moment
+ if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ obj->SaveRespawnTime();
+ ///- object must be out of world before delete
+ obj->RemoveFromWorld();
+ ///- object will get delinked from the manager when deleted
+ delete obj;
+ }
+}
+
+template<>
+void
+ObjectGridUnloader::Visit(CreatureMapType &m)
+{
+ // remove all cross-reference before deleting
+ for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ iter->getSource()->CleanupsBeforeDelete();
+
+ while(!m.isEmpty())
+ {
+ Creature *obj = m.getFirst()->getSource();
+ // if option set then object already saved at this moment
+ if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ obj->SaveRespawnTime();
+ ///- object will get delinked from the manager when deleted
+ delete obj;
+ }
+}
+
+void
+ObjectGridStoper::Stop(GridType &grid)
+{
+ TypeContainerVisitor<ObjectGridStoper, GridTypeMapContainer > stoper(*this);
+ grid.Visit(stoper);
+}
+
+void
+ObjectGridStoper::Visit(CreatureMapType &m)
+{
+ // stop any fights at grid de-activation and remove dynobjects created at cast by creatures
+ for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
+ {
+ iter->getSource()->CombatStop();
+ iter->getSource()->DeleteThreatList();
+ iter->getSource()->RemoveAllDynObjects();
+ }
+}
+
+template void ObjectGridUnloader::Visit(GameObjectMapType &);
+template void ObjectGridUnloader::Visit(DynamicObjectMapType &);
diff --git a/src/game/ObjectGridLoader.h b/src/game/ObjectGridLoader.h
new file mode 100644
index 00000000000..26cc3543b60
--- /dev/null
+++ b/src/game/ObjectGridLoader.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_OBJECTGRIDLOADER_H
+#define MANGOS_OBJECTGRIDLOADER_H
+
+#include "Utilities/TypeList.h"
+#include "Platform/Define.h"
+#include "GameSystem/GridLoader.h"
+#include "GridDefines.h"
+#include "Cell.h"
+
+class ObjectWorldLoader;
+
+class MANGOS_DLL_DECL ObjectGridLoader
+{
+ friend class ObjectWorldLoader;
+
+ public:
+ ObjectGridLoader(NGridType &grid, Map* map, const Cell &cell)
+ : i_cell(cell), i_grid(grid), i_map(map), i_gameObjects(0), i_creatures(0), i_corpses (0)
+ {}
+
+ void Load(GridType &grid);
+ void Visit(GameObjectMapType &m);
+ void Visit(CreatureMapType &m);
+ void Visit(CorpseMapType &) {}
+
+ void Visit(DynamicObjectMapType&) { }
+
+ void LoadN(void);
+
+ private:
+ Cell i_cell;
+ NGridType &i_grid;
+ Map* i_map;
+ uint32 i_gameObjects;
+ uint32 i_creatures;
+ uint32 i_corpses;
+};
+
+class MANGOS_DLL_DECL ObjectGridUnloader
+{
+ public:
+ ObjectGridUnloader(NGridType &grid) : i_grid(grid) {}
+
+ void MoveToRespawnN();
+ void UnloadN()
+ {
+ for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
+ {
+ for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y)
+ {
+ GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> loader;
+ loader.Unload(i_grid(x, y), *this);
+ }
+ }
+ }
+
+ void Unload(GridType &grid);
+ template<class T> void Visit(GridRefManager<T> &m);
+ private:
+ NGridType &i_grid;
+};
+
+class MANGOS_DLL_DECL ObjectGridStoper
+{
+ public:
+ ObjectGridStoper(NGridType &grid) : i_grid(grid) {}
+
+ void MoveToRespawnN();
+ void StopN()
+ {
+ for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
+ {
+ for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y)
+ {
+ GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> loader;
+ loader.Stop(i_grid(x, y), *this);
+ }
+ }
+ }
+
+ void Stop(GridType &grid);
+ void Visit(CreatureMapType &m);
+
+ template<class NONACTIVE> void Visit(GridRefManager<NONACTIVE> &) {}
+ private:
+ NGridType &i_grid;
+};
+
+typedef GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> GridLoaderType;
+#endif
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
new file mode 100644
index 00000000000..982ae3c4bc4
--- /dev/null
+++ b/src/game/ObjectMgr.cpp
@@ -0,0 +1,6512 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/SQLStorage.h"
+
+#include "Log.h"
+#include "MapManager.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "WorldSession.h"
+#include "Group.h"
+#include "Guild.h"
+#include "ArenaTeam.h"
+#include "Transports.h"
+#include "ProgressBar.h"
+#include "Policies/SingletonImp.h"
+#include "Language.h"
+#include "GameEvent.h"
+#include "Spell.h"
+#include "Chat.h"
+#include "InstanceSaveMgr.h"
+#include "SpellAuras.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1(ObjectMgr);
+
+ScriptMapMap sQuestEndScripts;
+ScriptMapMap sQuestStartScripts;
+ScriptMapMap sSpellScripts;
+ScriptMapMap sGameObjectScripts;
+ScriptMapMap sEventScripts;
+
+bool normalizePlayerName(std::string& name)
+{
+ if(name.empty())
+ return false;
+
+ wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
+ size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
+
+ if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
+ return false;
+
+ wstr_buf[0] = wcharToUpper(wstr_buf[0]);
+ for(size_t i = 1; i < wstr_len; ++i)
+ wstr_buf[i] = wcharToLower(wstr_buf[i]);
+
+ if(!WStrToUtf8(wstr_buf,wstr_len,name))
+ return false;
+
+ return true;
+}
+
+ObjectMgr::ObjectMgr()
+{
+ m_hiCharGuid = 1;
+ m_hiCreatureGuid = 1;
+ m_hiPetGuid = 1;
+ m_hiItemGuid = 1;
+ m_hiGoGuid = 1;
+ m_hiDoGuid = 1;
+ m_hiCorpseGuid = 1;
+
+ m_hiPetNumber = 1;
+
+ mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS);
+ mGuildBankTabPrice[0] = 100;
+ mGuildBankTabPrice[1] = 250;
+ mGuildBankTabPrice[2] = 500;
+ mGuildBankTabPrice[3] = 1000;
+ mGuildBankTabPrice[4] = 2500;
+ mGuildBankTabPrice[5] = 5000;
+
+ // Only zero condition left, others will be added while loading DB tables
+ mConditions.resize(1);
+}
+
+ObjectMgr::~ObjectMgr()
+{
+ for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++ i )
+ {
+ delete i->second;
+ }
+ mQuestTemplates.clear( );
+
+ for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++ i )
+ {
+ delete i->second;
+ }
+ mGossipText.clear( );
+
+ mAreaTriggers.clear();
+
+ for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++ i )
+ {
+ delete[] i->second;
+ }
+ petInfo.clear();
+
+ // free only if loaded
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ delete[] playerClassInfo[class_].levelInfo;
+
+ for (int race = 0; race < MAX_RACES; ++race)
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ delete[] playerInfo[race][class_].levelInfo;
+
+ // free group and guild objects
+ for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
+ delete (*itr);
+ for (GuildSet::iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
+ delete (*itr);
+
+ for(ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
+ delete itr->second;
+}
+
+Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
+{
+ for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
+ if ((*itr)->GetLeaderGUID() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetId() == GuildId)
+ return *itr;
+
+ return NULL;
+}
+
+Guild * ObjectMgr::GetGuildByName(std::string guildname) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetName() == guildname)
+ return *itr;
+
+ return NULL;
+}
+
+std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetId() == GuildId)
+ return (*itr)->GetName();
+
+ return "";
+}
+
+Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
+ if( (*itr)->GetLeader() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 ArenaTeamId) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetId() == ArenaTeamId)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamByName(std::string arenateamname) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetName() == arenateamname)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamByCapitan(uint64 const& guid) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetCaptain() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+AuctionHouseObject * ObjectMgr::GetAuctionsMap( uint32 location )
+{
+ switch ( location )
+ {
+ case 6: //horde
+ return & mHordeAuctions;
+ break;
+ case 2: //alliance
+ return & mAllianceAuctions;
+ break;
+ default: //neutral
+ return & mNeutralAuctions;
+ }
+}
+
+uint32 ObjectMgr::GetAuctionCut(uint32 location, uint32 highBid)
+{
+ if (location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
+ return (uint32) (0.15f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
+ else
+ return (uint32) (0.05f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
+}
+
+uint32 ObjectMgr::GetAuctionDeposit(uint32 location, uint32 time, Item *pItem)
+{
+ float percentance; // in 0..1
+ if ( location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
+ percentance = 0.75f;
+ else
+ percentance = 0.15f;
+
+ percentance *= sWorld.getRate(RATE_AUCTION_DEPOSIT);
+
+ return uint32( percentance * pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME ) );
+}
+
+/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
+uint32 ObjectMgr::GetAuctionOutBid(uint32 currentBid)
+{
+ uint32 outbid = (currentBid / 100) * 5;
+ if (!outbid)
+ outbid = 1;
+ return outbid;
+}
+
+//does not clear ram
+void ObjectMgr::SendAuctionWonMail( AuctionEntry *auction )
+{
+ Item *pItem = objmgr.GetAItem(auction->item_guidlow);
+ if(!pItem)
+ return;
+
+ uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
+ Player *bidder = objmgr.GetPlayer(bidder_guid);
+
+ uint32 bidder_accId = 0;
+
+ // data for gm.log
+ if( sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ {
+ uint32 bidder_security = 0;
+ std::string bidder_name;
+ if (bidder)
+ {
+ bidder_accId = bidder->GetSession()->GetAccountId();
+ bidder_security = bidder->GetSession()->GetSecurity();
+ bidder_name = bidder->GetName();
+ }
+ else
+ {
+ bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
+ bidder_security = GetSecurityByAccount(bidder_accId);
+
+ if(bidder_security > SEC_PLAYER ) // not do redundant DB requests
+ {
+ if(!GetPlayerNameByGUID(bidder_guid,bidder_name))
+ bidder_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
+ }
+ }
+
+ if( bidder_security > SEC_PLAYER )
+ {
+ std::string owner_name;
+ if(!GetPlayerNameByGUID(auction->owner,owner_name))
+ owner_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
+
+ uint32 owner_accid = GetPlayerAccountIdByGUID(auction->owner);
+
+ sLog.outCommand("GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)",
+ bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid);
+ }
+ }
+ else if(!bidder)
+ bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
+
+ // receiver exist
+ if(bidder || bidder_accId)
+ {
+ std::ostringstream msgAuctionWonSubject;
+ msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON;
+
+ std::ostringstream msgAuctionWonBody;
+ msgAuctionWonBody.width(16);
+ msgAuctionWonBody << std::right << std::hex << auction->owner;
+ msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ sLog.outDebug( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() );
+
+ //prepare mail data... :
+ uint32 itemTextId = this->CreateItemText( msgAuctionWonBody.str() );
+
+ // set owner to bidder (to prevent delete item with sender char deleting)
+ // owner in `data` will set at mail receive and item extracting
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+
+ MailItemsInfo mi;
+ mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
+ if (bidder)
+ bidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, bidder_guid, 0, 0, auction->item_template);
+ else
+ objmgr.RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ }
+ // receiver not exist
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem->GetGUIDLow());
+ objmgr.RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+ delete pItem;
+ }
+}
+
+void ObjectMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
+{
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = objmgr.GetPlayer(owner_guid);
+
+ // owner exist (online or offline)
+ if(owner || GetPlayerAccountIdByGUID(owner_guid))
+ {
+ std::ostringstream msgAuctionSalePendingSubject;
+ msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
+
+ std::ostringstream msgAuctionSalePendingBody;
+ uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
+
+ time_t distrTime = time(NULL) + HOUR;
+
+ msgAuctionSalePendingBody.width(16);
+ msgAuctionSalePendingBody << std::right << std::hex << auction->bidder;
+ msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:";
+ msgAuctionSalePendingBody << secsToTimeBitFields(distrTime);
+
+ sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str());
+
+ uint32 itemTextId = this->CreateItemText( msgAuctionSalePendingBody.str() );
+
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSalePendingSubject.str(), itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ }
+}
+
+//call this method to send mail to auction owner, when auction is successful, it does not clear ram
+void ObjectMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
+{
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = objmgr.GetPlayer(owner_guid);
+
+ uint32 owner_accId = 0;
+ if(!owner)
+ owner_accId = GetPlayerAccountIdByGUID(owner_guid);
+
+ // owner exist
+ if(owner || owner_accId)
+ {
+ std::ostringstream msgAuctionSuccessfulSubject;
+ msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
+
+ std::ostringstream auctionSuccessfulBody;
+ uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
+
+ auctionSuccessfulBody.width(16);
+ auctionSuccessfulBody << std::right << std::hex << auction->bidder;
+ auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut;
+
+ sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str());
+
+ uint32 itemTextId = this->CreateItemText( auctionSuccessfulBody.str() );
+
+ uint32 profit = auction->bid + auction->deposit - auctionCut;
+
+ if (owner)
+ {
+ //send auction owner notification, bidder must be current!
+ owner->GetSession()->SendAuctionOwnerNotification( auction );
+ }
+
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, HOUR);
+ }
+}
+
+//does not clear ram
+void ObjectMgr::SendAuctionExpiredMail( AuctionEntry * auction )
+{ //return an item in auction to its owner by mail
+ Item *pItem = objmgr.GetAItem(auction->item_guidlow);
+ if(!pItem)
+ {
+ sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow);
+ return;
+ }
+
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = objmgr.GetPlayer(owner_guid);
+
+ uint32 owner_accId = 0;
+ if(!owner)
+ owner_accId = GetPlayerAccountIdByGUID(owner_guid);
+
+ // owner exist
+ if(owner || owner_accId)
+ {
+ std::ostringstream subject;
+ subject << auction->item_template << ":0:" << AUCTION_EXPIRED;
+
+ if ( owner )
+ owner->GetSession()->SendAuctionOwnerNotification( auction );
+ else
+ objmgr.RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+
+ MailItemsInfo mi;
+ mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+
+ }
+ // owner not found
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'",pItem->GetGUIDLow());
+ objmgr.RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+ delete pItem;
+ }
+}
+
+CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
+{
+ return sCreatureStorage.LookupEntry<CreatureInfo>(id);
+}
+
+void ObjectMgr::LoadCreatureLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ CreatureLocale& data = mCreatureLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+2*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+ str = fields[1+2*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.SubName.size() <= idx)
+ data.SubName.resize(idx+1);
+
+ data.SubName[idx] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() );
+}
+
+void ObjectMgr::LoadCreatureTemplates()
+{
+ sCreatureStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
+ sLog.outString();
+
+ std::set<uint32> heroicEntries; // already loaded heroic value in creatures
+ std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
+
+ // check data correctness
+ for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
+ {
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
+ if(!cInfo)
+ continue;
+
+ if(cInfo->HeroicEntry)
+ {
+ CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
+ if(!heroicInfo)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.",cInfo->HeroicEntry,cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(heroicEntries.find(i)!=heroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
+ continue;
+ }
+
+ if(heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(cInfo->npcflag != heroicInfo->npcflag)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->classNum != heroicInfo->classNum)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `classNum` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->race != heroicInfo->race)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `race` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->trainer_type != heroicInfo->trainer_type)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->trainer_spell != heroicInfo->trainer_spell)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i);
+ continue;
+ }
+
+ hasHeroicEntries.insert(i);
+ heroicEntries.insert(cInfo->HeroicEntry);
+ }
+
+ FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
+ if(!factionTemplate)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
+
+ factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
+ if(!factionTemplate)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
+
+ CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A);
+ if (!minfo)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_A (%u)", cInfo->Entry, cInfo->DisplayID_A);
+ minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H);
+ if (!minfo)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_H (%u)", cInfo->Entry, cInfo->DisplayID_H);
+
+ if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
+ const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
+ }
+
+ if(cInfo->baseattacktime == 0)
+ const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
+
+ if(cInfo->rangeattacktime == 0)
+ const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
+
+ if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
+ sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type);
+
+ if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
+ const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
+ }
+
+ if(cInfo->PetSpellDataId)
+ {
+ CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
+ if(!spellDataId)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
+ }
+
+ if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
+ const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
+ }
+
+ if(cInfo->equipmentId > 0) // 0 no equipment
+ {
+ if(!GetEquipmentInfo(cInfo->equipmentId))
+ {
+ sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
+ const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
+ }
+ }
+
+ /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
+ if(cInfo->scale <= 0.0f)
+ {
+ CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A);
+ const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f;
+ }
+ }
+}
+
+void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
+{
+ // Now add the auras, format "spellid effectindex spellid effectindex..."
+ char *p,*s;
+ std::vector<int> val;
+ s=p=(char*)reinterpret_cast<char const*>(addon->auras);
+ if(p)
+ {
+ while (p[0]!=0)
+ {
+ ++p;
+ if (p[0]==' ')
+ {
+ val.push_back(atoi(s));
+ s=++p;
+ }
+ }
+ if (p!=s)
+ val.push_back(atoi(s));
+
+ // free char* loaded memory
+ delete[] (char*)reinterpret_cast<char const*>(addon->auras);
+
+ // wrong list
+ if (val.size()%2)
+ {
+ addon->auras = NULL;
+ sLog.outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.",guidEntryStr,addon->guidOrEntry,table);
+ return;
+ }
+ }
+
+ // empty list
+ if(val.empty())
+ {
+ addon->auras = NULL;
+ return;
+ }
+
+ // replace by new strucutres array
+ const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
+
+ int i=0;
+ for(int j=0;j<val.size()/2;++j)
+ {
+ CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
+ cAura.spell_id = (uint32)val[2*j+0];
+ cAura.effect_idx = (uint32)val[2*j+1];
+ if ( cAura.effect_idx > 2 )
+ {
+ sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
+ continue;
+ }
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
+ if (!AdditionalSpellInfo)
+ {
+ sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
+ continue;
+ }
+
+ if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
+ {
+ sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
+ continue;
+ }
+
+ ++i;
+ }
+
+ // fill terminator element (after last added)
+ CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
+ endAura.spell_id = 0;
+ endAura.effect_idx = 0;
+}
+
+void ObjectMgr::LoadCreatureAddons()
+{
+ sCreatureInfoAddonStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount );
+ sLog.outString();
+
+ // check data correctness and convert 'auras'
+ for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
+ {
+ CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i);
+ if(!addon)
+ continue;
+
+ ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry");
+
+ if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
+ sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `creature_template_addon`",addon->guidOrEntry);
+ }
+
+ 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;
+
+ ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow");
+
+ 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)
+{
+ return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
+}
+
+void ObjectMgr::LoadEquipmentTemplates()
+{
+ sEquipmentStorage.Load();
+
+ sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
+ sLog.outString();
+}
+
+CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
+{
+ return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
+}
+
+uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data)
+{
+ // Load creature model (display id)
+ uint32 display_id;
+ if (!data || data->displayid == 0) // use defaults from the template
+ {
+ // DisplayID_A is used if no team is given
+ if (team == HORDE)
+ display_id = (cinfo->DisplayID_H2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_H2 : cinfo->DisplayID_H;
+ else
+ display_id = (cinfo->DisplayID_A2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_A2 : cinfo->DisplayID_A;
+ }
+ else // overriden in creature data
+ display_id = data->displayid;
+
+ return display_id;
+}
+
+CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
+{
+ CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
+ if(!minfo)
+ return NULL;
+
+ // If a model for another gender exists, 50% chance to use it
+ if(minfo->modelid_other_gender != 0 && urand(0,1) == 0)
+ {
+ CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
+ if(!minfo_tmp)
+ {
+ sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
+ return minfo; // not fatal, just use the previous one
+ }
+ else
+ return minfo_tmp;
+ }
+ else
+ return minfo;
+}
+
+void ObjectMgr::LoadCreatureModelInfo()
+{
+ sCreatureModelStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadCreatures()
+{
+ uint32 count = 0;
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
+ // 4 5 6 7 8 9 10 11
+ "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
+ // 12 13 14 15 16 17
+ "curhealth, curmana, DeathState, MovementType, spawnMask, event "
+ "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
+ return;
+ }
+
+ // build single time for check creature data
+ std::set<uint32> heroicCreatures;
+ for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
+ if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
+ if(cInfo->HeroicEntry)
+ heroicCreatures.insert(cInfo->HeroicEntry);
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 guid = fields[0].GetUInt32();
+
+ CreatureData& data = mCreatureDataMap[guid];
+
+ data.id = fields[ 1].GetUInt32();
+ data.mapid = fields[ 2].GetUInt32();
+ data.displayid = fields[ 3].GetUInt32();
+ data.equipmentId = fields[ 4].GetUInt32();
+ data.posX = fields[ 5].GetFloat();
+ data.posY = fields[ 6].GetFloat();
+ data.posZ = fields[ 7].GetFloat();
+ data.orientation = fields[ 8].GetFloat();
+ data.spawntimesecs = fields[ 9].GetUInt32();
+ data.spawndist = fields[10].GetFloat();
+ data.currentwaypoint= fields[11].GetUInt32();
+ data.curhealth = fields[12].GetUInt32();
+ data.curmana = fields[13].GetUInt32();
+ data.is_dead = fields[14].GetBool();
+ data.movementType = fields[15].GetUInt8();
+ data.spawnMask = fields[16].GetUInt8();
+ int16 gameEvent = fields[17].GetInt16();
+
+ CreatureInfo const* cInfo = GetCreatureTemplate(data.id);
+ if(!cInfo)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id );
+ continue;
+ }
+
+ if(heroicCreatures.find(data.id)!=heroicCreatures.end())
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id );
+ continue;
+ }
+
+ if(data.equipmentId > 0) // -1 no equipment, 0 use default
+ {
+ if(!GetEquipmentInfo(data.equipmentId))
+ {
+ sLog.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
+ data.equipmentId = -1;
+ }
+ }
+
+ if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.",guid,data.id,data.curhealth, cInfo->minhealth );
+ data.curhealth = cInfo->minhealth;
+ }
+
+ if(data.curmana < cInfo->minmana)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana );
+ data.curmana = cInfo->minmana;
+ }
+
+ if(data.spawndist < 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
+ data.spawndist = 0.0f;
+ }
+ else if(data.movementType == RANDOM_MOTION_TYPE)
+ {
+ if(data.spawndist == 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).",guid,data.id );
+ data.movementType = IDLE_MOTION_TYPE;
+ }
+ }
+ else if(data.movementType == IDLE_MOTION_TYPE)
+ {
+ if(data.spawndist != 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.",guid,data.id );
+ data.spawndist = 0.0f;
+ }
+ }
+
+ if (gameEvent==0) // if not this is to be managed by GameEvent System
+ AddCreatureToGrid(guid, &data);
+ ++count;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() );
+}
+
+void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.creatures.insert(guid);
+ }
+ }
+}
+
+void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.creatures.erase(guid);
+ }
+ }
+}
+
+void ObjectMgr::LoadGameobjects()
+{
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6
+ QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
+ // 7 8 9 10 11 12 13 14 15
+ "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event "
+ "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 guid = fields[0].GetUInt32();
+
+ GameObjectData& data = mGameObjectDataMap[guid];
+
+ data.id = fields[ 1].GetUInt32();
+ data.mapid = fields[ 2].GetUInt32();
+ data.posX = fields[ 3].GetFloat();
+ data.posY = fields[ 4].GetFloat();
+ data.posZ = fields[ 5].GetFloat();
+ data.orientation = fields[ 6].GetFloat();
+ data.rotation0 = fields[ 7].GetFloat();
+ data.rotation1 = fields[ 8].GetFloat();
+ data.rotation2 = fields[ 9].GetFloat();
+ data.rotation3 = fields[10].GetFloat();
+ data.spawntimesecs = fields[11].GetInt32();
+ data.animprogress = fields[12].GetUInt32();
+ data.go_state = fields[13].GetUInt32();
+ data.spawnMask = fields[14].GetUInt8();
+ int16 gameEvent = fields[15].GetInt16();
+
+ GameObjectInfo const* gInfo = GetGameObjectInfo(data.id);
+ if(!gInfo)
+ {
+ sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id );
+ continue;
+ }
+
+ if (gameEvent==0) // if not this is to be managed by GameEvent System
+ AddGameobjectToGrid(guid, &data);
+ ++count;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size());
+}
+
+void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.gameobjects.insert(guid);
+ }
+ }
+}
+
+void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.gameobjects.erase(guid);
+ }
+ }
+}
+
+void ObjectMgr::LoadCreatureRespawnTimes()
+{
+ // remove outdated data
+ WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 creature respawn time.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 loguid = fields[0].GetUInt32();
+ uint64 respawn_time = fields[1].GetUInt64();
+ uint32 instance = fields[2].GetUInt32();
+
+ mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadGameobjectRespawnTimes()
+{
+ // remove outdated data
+ WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 gameobject respawn time.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 loguid = fields[0].GetUInt32();
+ uint64 respawn_time = fields[1].GetUInt64();
+ uint32 instance = fields[2].GetUInt32();
+
+ mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() );
+ sLog.outString();
+}
+
+// name must be checked to correctness (if received) before call this function
+uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
+{
+ uint64 guid = 0;
+
+ CharacterDatabase.escape_string(name);
+
+ // Player name safe to sending to DB (checked at login) and this function using
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
+ if(result)
+ {
+ guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ delete result;
+ }
+
+ return guid;
+}
+
+bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
+{
+ // prevent DB access for online player
+ if(Player* player = GetPlayer(guid))
+ {
+ name = player->GetName();
+ return true;
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if(result)
+ {
+ name = (*result)[0].GetCppString();
+ delete result;
+ return true;
+ }
+
+ return false;
+}
+
+uint32 ObjectMgr::GetPlayerTeamByGUID(const uint64 &guid) const
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if(result)
+ {
+ uint8 race = (*result)[0].GetUInt8();
+ delete result;
+ return Player::TeamForRace(race);
+ }
+
+ return 0;
+}
+
+uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ uint32 acc = (*result)[0].GetUInt32();
+ delete result;
+ return acc;
+ }
+
+ return 0;
+}
+
+uint32 ObjectMgr::GetSecurityByAccount(uint32 acc_id) const
+{
+ QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id);
+ if(result)
+ {
+ uint32 sec = (*result)[0].GetUInt32();
+ delete result;
+ return sec;
+ }
+
+ return 0;
+}
+
+bool ObjectMgr::GetAccountNameByAccount(uint32 acc_id, std::string &name) const
+{
+ QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id);
+ if(result)
+ {
+ name = (*result)[0].GetCppString();
+ delete result;
+ return true;
+ }
+
+ return false;
+}
+
+uint32 ObjectMgr::GetAccountByAccountName(std::string name) const
+{
+ loginDatabase.escape_string(name);
+ QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", name.c_str());
+ if(result)
+ {
+ uint32 id = (*result)[0].GetUInt32();
+ delete result;
+ return id;
+ }
+
+ return 0;
+}
+
+void ObjectMgr::LoadAuctions()
+{
+ QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse");
+ if( !result )
+ return;
+
+ Field *fields = result->Fetch();
+ uint32 AuctionCount=fields[0].GetUInt32();
+ delete result;
+
+ if(!AuctionCount)
+ return;
+
+ result = CharacterDatabase.Query( "SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location FROM auctionhouse" );
+ if( !result )
+ return;
+
+ barGoLink bar( AuctionCount );
+
+ AuctionEntry *aItem;
+
+ do
+ {
+ fields = result->Fetch();
+
+ bar.step();
+
+ aItem = new AuctionEntry;
+ aItem->Id = fields[0].GetUInt32();
+ aItem->auctioneer = fields[1].GetUInt32();
+ aItem->item_guidlow = fields[2].GetUInt32();
+ aItem->item_template = fields[3].GetUInt32();
+ aItem->owner = fields[4].GetUInt32();
+ aItem->buyout = fields[5].GetUInt32();
+ aItem->time = fields[6].GetUInt32();
+ aItem->bidder = fields[7].GetUInt32();
+ aItem->bid = fields[8].GetUInt32();
+ aItem->startbid = fields[9].GetUInt32();
+ aItem->deposit = fields[10].GetUInt32();
+ aItem->location = fields[11].GetUInt8();
+ //check if sold item exists
+ if ( this->GetAItem( aItem->item_guidlow ) )
+ {
+ GetAuctionsMap( aItem->location )->AddAuction(aItem);
+ }
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem->Id);
+ sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow);
+ delete aItem;
+ }
+ } while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u auctions", AuctionCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadItemLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ ItemLocale& data = mItemLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+2*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+
+ str = fields[1+2*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Description.size() <= idx)
+ data.Description.resize(idx+1);
+
+ data.Description[idx] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() );
+}
+
+void ObjectMgr::LoadItemPrototypes()
+{
+ sItemStorage.Load ();
+ sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
+ sLog.outString();
+
+ // check data correctness
+ for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
+ {
+ ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
+ ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
+ if(!proto)
+ {
+ /* to many errors, and possible not all items really used in game
+ if (dbcitem)
+ sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
+ */
+ continue;
+ }
+
+ if(dbcitem)
+ {
+ if(proto->InventoryType != dbcitem->InventoryType)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
+ // It safe let use InventoryType from DB
+ }
+
+ if(proto->DisplayInfoID != dbcitem->DisplayId)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
+ const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
+ }
+ if(proto->Sheath != dbcitem->Sheath)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i,proto->Sheath,dbcitem->Sheath);
+ const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
+ }
+
+ if(proto->Class >= MAX_ITEM_CLASS)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
+ const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK;
+ }
+
+ if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
+ const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
+ }
+
+ if(proto->Quality >= MAX_ITEM_QUALITY)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
+ const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
+ }
+
+ if(proto->BuyCount <= 0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
+ const_cast<ItemPrototype*>(proto)->BuyCount = 1;
+ }
+
+ if(proto->InventoryType >= MAX_INVTYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
+ const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
+ }
+
+ if(proto->RequiredSkill >= MAX_SKILL_TYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
+ const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
+ }
+
+ if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
+ {
+ sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass);
+ }
+
+ if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
+ {
+ sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace);
+ }
+
+ if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
+ {
+ sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
+ const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
+ }
+
+ if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
+
+ if(proto->RequiredReputationFaction)
+ {
+ if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
+ const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
+ }
+
+ if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
+ }
+ else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
+
+ if(proto->Stackable==0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable);
+ const_cast<ItemPrototype*>(proto)->Stackable = 1;
+ }
+ else if(proto->Stackable > 255)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable);
+ const_cast<ItemPrototype*>(proto)->Stackable = 255;
+ }
+
+ for (int j = 0; j < 10; j++)
+ {
+ // for ItemStatValue != 0
+ if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
+ const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
+ }
+ }
+
+ for (int j = 0; j < 5; j++)
+ {
+ if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)",i,j+1,proto->Damage[j].DamageType);
+ const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
+ }
+ }
+
+ // special format
+ if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN)
+ {
+ // spell_1
+ if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format",i,0+1,proto->Spells[0].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+
+ // spell_2 have learning spell
+ if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.",i,1+1,proto->Spells[1].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else if(!proto->Spells[1].SpellId)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.",i,1+1);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ // allowed only in special format
+ else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ }
+
+ // spell_3*,spell_4*,spell_5* is empty
+ for (int j = 2; j < 5; j++)
+ {
+ if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else if(proto->Spells[j].SpellId != 0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ }
+ }
+ // normal spell list
+ else
+ {
+ for (int j = 0; j < 5; j++)
+ {
+ if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+
+ if(proto->Spells[j].SpellId)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ // allowed only in special format
+ else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ }
+ }
+ }
+
+ if(proto->Bonding >= MAX_BIND_TYPE)
+ sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
+
+ if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
+ sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
+
+ if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
+ sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
+
+ if(proto->Sheath >= MAX_SHEATHETYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
+ const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
+ }
+
+ if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
+ const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
+ }
+
+ if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
+ const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
+ }
+
+ if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
+ {
+ sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
+ const_cast<ItemPrototype*>(proto)->ItemSet = 0;
+ }
+
+ if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
+ sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
+
+ if(proto->Map && !sMapStore.LookupEntry(proto->Map))
+ sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
+
+ if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
+ sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
+
+ for (int j = 0; j < 3; j++)
+ {
+ if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)",i,j+1,proto->Socket[j].Color);
+ const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
+ }
+ }
+
+ if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
+ sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
+
+ if(proto->FoodType >= MAX_PET_DIET)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
+ const_cast<ItemPrototype*>(proto)->FoodType = 0;
+ }
+ }
+
+ // this DBC used currently only for check item templates in DB.
+ sItemStore.Clear();
+}
+
+void ObjectMgr::LoadAuctionItems()
+{
+ QueryResult *result = CharacterDatabase.Query( "SELECT itemguid,item_template FROM auctionhouse" );
+
+ if( !result )
+ return;
+
+ barGoLink bar( result->GetRowCount() );
+
+ uint32 count = 0;
+
+ Field *fields;
+ do
+ {
+ bar.step();
+
+ fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_template = fields[1].GetUInt32();
+
+ ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
+
+ if(!proto)
+ {
+ sLog.outError( "ObjectMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template);
+ continue;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid,0))
+ {
+ delete item;
+ continue;
+ }
+ AddAItem(item);
+
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u auction items", count );
+}
+
+void ObjectMgr::LoadPetLevelInfo()
+{
+ // Loading levels data
+ {
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level pet stats definitions", count );
+ sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 creature_id = fields[0].GetUInt32();
+ if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
+ {
+ sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
+ continue;
+ }
+
+ uint32 current_level = fields[1].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `pet_levelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+ else if(current_level < 1)
+ {
+ sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
+
+ if(pInfoMapEntry==NULL)
+ pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ // data for level 1 stored in [0] array element, ...
+ PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
+
+ pLevelInfo->health = fields[2].GetUInt16();
+ pLevelInfo->mana = fields[3].GetUInt16();
+ pLevelInfo->armor = fields[9].GetUInt16();
+
+ for (int i = 0; i < MAX_STATS; i++)
+ {
+ pLevelInfo->stats[i] = fields[i+4].GetUInt16();
+ }
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level pet stats definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
+ {
+ PetLevelInfo* pInfo = itr->second;
+
+ // fatal error if no level 1 data
+ if(!pInfo || pInfo[0].health == 0 )
+ {
+ sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!",itr->first);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pInfo[level].health == 0)
+ {
+ sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.",itr->first,level+1, level);
+ pInfo[level] = pInfo[level-1];
+ }
+ }
+ }
+}
+
+PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
+{
+ if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
+
+ PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
+ if(itr == petInfo.end())
+ return NULL;
+
+ return &itr->second[level-1]; // data for level 1 stored in [0] array element, ...
+}
+
+void ObjectMgr::LoadPlayerInfo()
+{
+ // Load playercreate
+ {
+ // 0 1 2 3 4 5 6
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z FROM playercreateinfo");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create definitions", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ uint32 current_class = fields[1].GetUInt32();
+ uint32 mapId = fields[2].GetUInt32();
+ uint32 zoneId = fields[3].GetUInt32();
+ float positionX = fields[4].GetFloat();
+ float positionY = fields[5].GetFloat();
+ float positionZ = fields[6].GetFloat();
+
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
+ continue;
+ }
+
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
+ if(!rEntry)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
+ continue;
+ }
+
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
+ continue;
+ }
+
+ if(!sChrClassesStore.LookupEntry(current_class))
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
+ continue;
+ }
+
+ // accept DB data only for valid position (and non instanceable)
+ if( !MapManager::IsValidMapCoord(mapId,positionX,positionY,positionZ) )
+ {
+ sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
+ continue;
+ }
+
+ if( sMapStore.LookupEntry(mapId)->Instanceable() )
+ {
+ sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ pInfo->mapId = mapId;
+ pInfo->zoneId = zoneId;
+ pInfo->positionX = positionX;
+ pInfo->positionY = positionY;
+ pInfo->positionZ = positionZ;
+
+ pInfo->displayId_m = rEntry->model_m;
+ pInfo->displayId_f = rEntry->model_f;
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create definitions", count );
+ }
+
+ // Load playercreate items
+ {
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create items", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo_item` table or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.",current_class);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ uint32 item_id = fields[2].GetUInt32();
+
+ if(!GetItemPrototype(item_id))
+ {
+ sLog.outErrorDb("Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.",item_id,current_race,current_class);
+ continue;
+ }
+
+ uint32 amount = fields[3].GetUInt32();
+
+ if(!amount)
+ {
+ sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.",item_id,current_race,current_class);
+ continue;
+ }
+
+ pInfo->item.push_back(PlayerCreateInfoItem( item_id, amount));
+
+ bar.step();
+ ++count;
+ }
+ while(result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create items", count );
+ }
+ }
+
+ // Load playercreate spells
+ {
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create spells", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo_spell` table or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.",current_class);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+ pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8()));
+
+ bar.step();
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create spells", count );
+ }
+ }
+
+ // Load playercreate actions
+ {
+ // 0 1 2 3 4 5
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create actions", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo_action` table or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.",current_class);
+ continue;
+ }
+
+ 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());
+
+ bar.step();
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create actions", count );
+ }
+ }
+
+ // Loading levels data (class only dependent)
+ {
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level health/mana definitions", count );
+ sLog.outErrorDb( "Error loading `player_classlevelstats` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_class = fields[0].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.",current_class);
+ continue;
+ }
+
+ uint32 current_level = fields[1].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `player_classlevelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
+
+ if(!pClassInfo->levelInfo)
+ pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level-1];
+
+ pClassLevelInfo->basehealth = fields[2].GetUInt16();
+ pClassLevelInfo->basemana = fields[3].GetUInt16();
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level health/mana definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ {
+ // skip non existed classes
+ if(!sChrClassesStore.LookupEntry(class_))
+ continue;
+
+ PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
+
+ // fatal error if no level 1 data
+ if(!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0 )
+ {
+ sLog.outErrorDb("Class %i Level 1 does not have health/mana data!",class_);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pClassInfo->levelInfo[level].basehealth == 0)
+ {
+ sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.",class_,level+1, level);
+ pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1];
+ }
+ }
+ }
+
+ // Loading levels data (class/race dependent)
+ {
+ // 0 1 2 3 4 5 6 7
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level stats definitions", count );
+ sLog.outErrorDb( "Error loading `player_levelstats` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.",current_class);
+ continue;
+ }
+
+ uint32 current_level = fields[2].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `player_levelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ if(!pInfo->levelInfo)
+ pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level-1];
+
+ for (int i = 0; i < MAX_STATS; i++)
+ {
+ pLevelInfo->stats[i] = fields[i+3].GetUInt8();
+ }
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level stats definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (int race = 0; race < MAX_RACES; ++race)
+ {
+ // skip non existed races
+ if(!sChrRacesStore.LookupEntry(race))
+ continue;
+
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ {
+ // skip non existed classes
+ if(!sChrClassesStore.LookupEntry(class_))
+ continue;
+
+ PlayerInfo* pInfo = &playerInfo[race][class_];
+
+ // skip non loaded combinations
+ if(!pInfo->displayId_m || !pInfo->displayId_f)
+ continue;
+
+ // skip expansion races if not playing with expansion
+ if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
+ continue;
+
+ // fatal error if no level 1 data
+ if(!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0 )
+ {
+ sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!",race,class_);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pInfo->levelInfo[level].stats[0] == 0)
+ {
+ sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.",race,class_,level+1, level);
+ pInfo->levelInfo[level] = pInfo->levelInfo[level-1];
+ }
+ }
+ }
+ }
+}
+
+void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
+{
+ if(level < 1 || class_ >= MAX_CLASSES)
+ return;
+
+ PlayerClassInfo const* pInfo = &playerClassInfo[class_];
+
+ if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
+
+ *info = pInfo->levelInfo[level-1];
+}
+
+void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
+{
+ if(level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
+ return;
+
+ PlayerInfo const* pInfo = &playerInfo[race][class_];
+ if(pInfo->displayId_m==0 || pInfo->displayId_f==0)
+ return;
+
+ if(level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ *info = pInfo->levelInfo[level-1];
+ else
+ BuildPlayerLevelInfo(race,class_,level,info);
+}
+
+void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
+{
+ // base data (last known level)
+ *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
+
+ for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
+ {
+ switch(_class)
+ {
+ case CLASS_WARRIOR:
+ info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ break;
+ case CLASS_PALADIN:
+ info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
+ break;
+ case CLASS_HUNTER:
+ info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
+ break;
+ case CLASS_ROGUE:
+ info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
+ break;
+ case CLASS_PRIEST:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
+ break;
+ case CLASS_SHAMAN:
+ info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
+ break;
+ case CLASS_MAGE:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
+ break;
+ case CLASS_WARLOCK:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
+ break;
+ case CLASS_DRUID:
+ info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
+ }
+ }
+}
+
+void ObjectMgr::LoadGuilds()
+{
+ Guild *newguild;
+ uint32 count = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT guildid FROM guild" );
+
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u guild definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+ ++count;
+
+ newguild = new Guild;
+ if(!newguild->LoadGuildFromDB(fields[0].GetUInt32()))
+ {
+ newguild->Disband();
+ delete newguild;
+ continue;
+ }
+ AddGuild(newguild);
+
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u guild definitions", count );
+}
+
+void ObjectMgr::LoadArenaTeams()
+{
+ uint32 count = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT arenateamid FROM arena_team" );
+
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u arenateam definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+ ++count;
+
+ ArenaTeam *newarenateam = new ArenaTeam;
+ if(!newarenateam->LoadArenaTeamFromDB(fields[0].GetUInt32()))
+ {
+ delete newarenateam;
+ continue;
+ }
+ AddArenaTeam(newarenateam);
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u arenateam definitions", count );
+}
+
+void ObjectMgr::LoadGroups()
+{
+ // -- loading groups --
+ Group *group = NULL;
+ uint64 leaderGuid = 0;
+ uint32 count = 0;
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ QueryResult *result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ ++count;
+ leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(),0,HIGHGUID_PLAYER);
+
+ group = new Group;
+ if(!group->LoadGroupFromDB(leaderGuid, result, false))
+ {
+ group->Disband();
+ delete group;
+ continue;
+ }
+ AddGroup(group);
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group definitions", count );
+
+ // -- loading members --
+ count = 0;
+ group = NULL;
+ leaderGuid = 0;
+ // 0 1 2 3
+ result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ count++;
+ leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
+ if(!group || group->GetLeaderGUID() != leaderGuid)
+ {
+ group = GetGroupByLeader(leaderGuid);
+ if(!group)
+ {
+ sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
+ continue;
+ }
+ }
+
+ if(!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
+ {
+ sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
+ }
+ }while( result->NextRow() );
+ delete result;
+ }
+
+ // clean groups
+ // TODO: maybe delete from the DB before loading in this case
+ for(GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
+ {
+ if((*itr)->GetMembersCount() < 2)
+ {
+ (*itr)->Disband();
+ delete *itr;
+ mGroupSet.erase(itr++);
+ }
+ else
+ ++itr;
+ }
+
+ // -- loading instances --
+ count = 0;
+ group = NULL;
+ leaderGuid = 0;
+ result = CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5
+ "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
+ // 6
+ "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
+ "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
+ );
+
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ count++;
+ leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ if(!group || group->GetLeaderGUID() != leaderGuid)
+ {
+ group = GetGroupByLeader(leaderGuid);
+ if(!group)
+ {
+ sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
+ continue;
+ }
+ }
+
+ InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
+ group->BindToInstance(save, fields[3].GetBool(), true);
+ }while( result->NextRow() );
+ delete result;
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group-instance binds total", count );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group members total", count );
+}
+
+void ObjectMgr::LoadQuests()
+{
+ // For reload case
+ for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
+ delete itr->second;
+ mQuestTemplates.clear();
+
+ mExclusiveQuestGroups.clear();
+
+ // 0 1 2 3 4 5 6 7
+ QueryResult *result = WorldDatabase.Query("SELECT entry, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
+ // 8 9 10 11 12 13 14 15
+ "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
+ // 16 17 18 19 20 21 22 23 24 25
+ "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
+ // 26 27 28 29 30 31 32 33 34 35
+ "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
+ // 36 37 38 39 40 41 42 43
+ "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
+ // 44 45 46 47 48 49 50 51 52 53 54 55
+ "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
+ // 56 57 58 59 60 61 62 63
+ "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
+ // 64 65 66 67
+ "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
+ // 68 69 70 71 72 73
+ "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
+ // 74 75 76 77 78 79
+ "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
+ // 80 81 82 83 84 85 86 87
+ "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
+ // 88 89 90 91 92 93 94 95 96 97
+ "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
+ // 98 99 100 101 102 103 104 105 106 107
+ "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
+ // 108 109 110 111 112 113 114 115 116 117
+ "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
+ // 118 119
+ "StartScript, CompleteScript"
+ " FROM quest_template");
+ if(result == NULL)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 quests definitions" );
+ sLog.outErrorDb("`quest_template` table is empty!");
+ return;
+ }
+
+ // create multimap previous quest for each existed quest
+ // some quests can have many previous maps set by NextQuestId in previous quest
+ // for example set of race quests can lead to single not race specific quest
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ Quest * newQuest = new Quest(fields);
+ mQuestTemplates[newQuest->GetQuestId()] = newQuest;
+ } while( result->NextRow() );
+
+ delete result;
+
+ // Post processing
+ for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); iter++)
+ {
+ Quest * qinfo = iter->second;
+
+ // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
+
+ if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
+ {
+ sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
+ qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_MANGOS_FLAGS_DB_ALLOWED >> 16);
+ qinfo->QuestFlags &= QUEST_MANGOS_FLAGS_DB_ALLOWED;
+ }
+
+ if(qinfo->QuestFlags & QUEST_FLAGS_DAILY)
+ {
+ if(!(qinfo->QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE))
+ {
+ sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.",qinfo->GetQuestId());
+ qinfo->QuestFlags |= QUEST_MANGOS_FLAGS_REPEATABLE;
+ }
+ }
+
+ if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
+ {
+ // at auto-reward can be rewarded only RewChoiceItemId[0]
+ for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
+ {
+ if(uint32 id = qinfo->RewChoiceItemId[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest ignore this data
+ }
+ }
+ }
+
+ // client quest log visual (area case)
+ if( qinfo->ZoneOrSort > 0 )
+ {
+ if(!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort);
+ // no changes, quest not dependent from this value but can have problems at client
+ }
+ }
+ // client quest log visual (sort case)
+ if( qinfo->ZoneOrSort < 0 )
+ {
+ QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
+ if( !qSort )
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort);
+ // no changes, quest not dependent from this value but can have problems at client (note some may be 0, we must allow this so no check)
+ }
+ //check SkillOrClass value (class case).
+ if( ClassByQuestSort(-int32(qinfo->ZoneOrSort)) )
+ {
+ // SkillOrClass should not have class case when class case already set in ZoneOrSort.
+ if(qinfo->SkillOrClass < 0)
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClass` = %i (class case), redundant.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort,qinfo->SkillOrClass);
+ }
+ }
+ //check for proper SkillOrClass value (skill case)
+ if(int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
+ {
+ // skill is positive value in SkillOrClass
+ if(qinfo->SkillOrClass != skill_id )
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClass` does not have a corresponding value (%i).",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort,skill_id);
+ //override, and force proper value here?
+ }
+ }
+ }
+
+ // SkillOrClass (class case)
+ if( qinfo->SkillOrClass < 0 )
+ {
+ if( !sChrClassesStore.LookupEntry(-int32(qinfo->SkillOrClass)) )
+ {
+ sLog.outErrorDb("Quest %u has `SkillOrClass` = %i (class case) but class (%i) does not exist",
+ qinfo->GetQuestId(),qinfo->SkillOrClass,-qinfo->SkillOrClass);
+ }
+ }
+ // SkillOrClass (skill case)
+ if( qinfo->SkillOrClass > 0 )
+ {
+ if( !sSkillLineStore.LookupEntry(qinfo->SkillOrClass) )
+ {
+ sLog.outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist",
+ qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
+ }
+ }
+
+ if( qinfo->RequiredSkillValue )
+ {
+ if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredSkillValue,sWorld.GetConfigMaxSkillValue());
+ // no changes, quest can't be done for this requirement
+ }
+
+ if( qinfo->SkillOrClass <= 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.",
+ qinfo->GetQuestId(),qinfo->RequiredSkillValue,qinfo->SkillOrClass);
+ // no changes, quest can't be done for this requirement (fail at wrong skill id)
+ }
+ }
+ // else Skill quests can have 0 skill level, this is ok
+
+ if(qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RepObjectiveFaction,qinfo->RepObjectiveFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepFaction,qinfo->RequiredMinRepFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepFaction,qinfo->RequiredMaxRepFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap)
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepValue,qinfo->RequiredMinRepValue);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RepObjectiveValue);
+ // warning
+ }
+
+ if(!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepValue);
+ // warning
+ }
+
+ if(!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepValue);
+ // warning
+ }
+
+ if(qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
+ {
+ sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
+ qinfo->GetQuestId(),qinfo->GetCharTitleId(),qinfo->GetCharTitleId());
+ qinfo->CharTitleId = 0;
+ // quest can't reward this title
+ }
+
+ if(qinfo->SrcItemId)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcItemId,qinfo->SrcItemId);
+ qinfo->SrcItemId = 0; // quest can't be done for this requirement
+ }
+ else if(qinfo->SrcItemCount==0)
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
+ qinfo->GetQuestId(),qinfo->SrcItemId);
+ qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward comptibility with DB
+ }
+ }
+ else if(qinfo->SrcItemCount>0)
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
+ qinfo->GetQuestId(),qinfo->SrcItemCount);
+ qinfo->SrcItemCount=0; // no quest work changes in fact
+ }
+
+ if(qinfo->SrcSpell)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
+ qinfo->SrcSpell = 0; // quest can't be done for this requirement
+ }
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
+ qinfo->SrcSpell = 0; // quest can't be done for this requirement
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqItemId[j];
+ if(id)
+ {
+ if(qinfo->ReqItemCount[j]==0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can't be done for this requirement
+ }
+
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_DELIVER);
+
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ else if(qinfo->ReqItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqItemCount[j]);
+ qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
+ }
+ }
+
+ for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqSourceId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->ReqSourceCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+
+ if(!qinfo->ReqSourceRef[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ else
+ {
+ if(qinfo->ReqSourceCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]);
+ // no changes, quest ignore this data
+ }
+
+ if(qinfo->ReqSourceRef[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]);
+ // no changes, quest ignore this data
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
+ {
+ uint32 ref = qinfo->ReqSourceRef[j];
+ if(ref)
+ {
+ if(ref > QUEST_OBJECTIVES_COUNT)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.",
+ qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT);
+ // no changes, quest can't be done for this requirement
+ }
+ else
+ if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,ref,ref,ref);
+ // no changes, quest can't be done for this requirement
+ }
+ else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1])
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.",
+ qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]);
+ // no changes, quest can't be done for this requirement
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqSpell[j];
+ if(id)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->ReqCreatureOrGOId[j])
+ {
+ bool found = false;
+ for(int k = 0; k < 3; ++k)
+ {
+ if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId ||
+ spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(found)
+ {
+ if(!qinfo->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.",spellInfo->Id,qinfo->QuestId,j+1,j+1);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(qinfo)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1,id);
+ // no changes, quest can't be done for this requirement
+ }
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ int32 id = qinfo->ReqCreatureOrGOId[j];
+ if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,uint32(-id));
+ qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
+ }
+
+ if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,uint32(id));
+ qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
+ }
+
+ if(id)
+ {
+ // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
+
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO);
+
+ if(!qinfo->ReqCreatureOrGOCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can be incorrectly done, but we already report this
+ }
+ }
+ else if(qinfo->ReqCreatureOrGOCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqCreatureOrGOCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
+ {
+ uint32 id = qinfo->RewChoiceItemId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
+ }
+
+ if(!qinfo->RewChoiceItemCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can't be done
+ }
+ }
+ else if(qinfo->RewChoiceItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewChoiceItemCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REWARDS_COUNT; ++j )
+ {
+ uint32 id = qinfo->RewItemId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
+ }
+
+ if(!qinfo->RewItemCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes
+ }
+ }
+ else if(qinfo->RewItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewItemCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
+ {
+ if(qinfo->RewRepFaction[j])
+ {
+ if(!qinfo->RewRepValue[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
+ qinfo->GetQuestId(),j+1,qinfo->RewRepValue[j],j+1);
+ // no changes
+ }
+
+ if(!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
+ qinfo->GetQuestId(),j+1,qinfo->RewRepFaction[j] ,qinfo->RewRepFaction[j] );
+ qinfo->RewRepFaction[j] = 0; // quest will not reward this
+ }
+ }
+ else if(qinfo->RewRepValue[j]!=0)
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewRepValue[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ if(qinfo->RewSpell)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
+ qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
+ qinfo->RewSpell = 0; // no spell reward will display for this quest
+ }
+
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
+ qinfo->RewSpell = 0; // no spell reward will display for this quest
+ }
+
+ }
+
+ if(qinfo->RewSpellCast)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
+ qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
+ qinfo->RewSpellCast = 0; // no spell will be casted on player
+ }
+
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
+ qinfo->RewSpellCast = 0; // no spell will be casted on player
+ }
+
+ }
+
+ if(qinfo->RewMailTemplateId)
+ {
+ if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
+ {
+ sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
+ qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
+ qinfo->RewMailTemplateId = 0; // no mail will send to player
+ qinfo->RewMailDelaySecs = 0; // no mail will send to player
+ }
+ }
+
+ if(qinfo->NextQuestInChain)
+ {
+ if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
+ qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
+ qinfo->NextQuestInChain = 0;
+ }
+ else
+ mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId());
+ }
+
+ // fill additional data stores
+ if(qinfo->PrevQuestId)
+ {
+ if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
+ }
+ else
+ {
+ qinfo->prevQuests.push_back(qinfo->PrevQuestId);
+ }
+ }
+
+ if(qinfo->NextQuestId)
+ {
+ if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
+ }
+ else
+ {
+ int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
+ mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId);
+ }
+ }
+
+ if(qinfo->ExclusiveGroup)
+ mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
+ if(qinfo->LimitTime)
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_TIMED);
+ }
+
+ // check QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
+ continue;
+
+ uint32 quest_id = spellInfo->EffectMiscValue[j];
+
+ Quest const* quest = GetQuestTemplate(quest_id);
+
+ // some quest referenced in spells not exist (outdataed spells)
+ if(!quest)
+ continue;
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() );
+}
+
+void ObjectMgr::LoadQuestLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
+ "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
+ "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
+ "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
+ "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
+ "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
+ "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
+ "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
+ " FROM locales_quest"
+ );
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ QuestLocale& data = mQuestLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+10*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Title.size() <= idx)
+ data.Title.resize(idx+1);
+
+ data.Title[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Details.size() <= idx)
+ data.Details.resize(idx+1);
+
+ data.Details[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+2].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Objectives.size() <= idx)
+ data.Objectives.resize(idx+1);
+
+ data.Objectives[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+3].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.OfferRewardText.size() <= idx)
+ data.OfferRewardText.resize(idx+1);
+
+ data.OfferRewardText[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+4].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.RequestItemsText.size() <= idx)
+ data.RequestItemsText.resize(idx+1);
+
+ data.RequestItemsText[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+5].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.EndText.size() <= idx)
+ data.EndText.resize(idx+1);
+
+ data.EndText[idx] = str;
+ }
+ }
+ for(int k = 0; k < 4; ++k)
+ {
+ str = fields[1+10*(i-1)+6+k].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.ObjectiveText[k].size() <= idx)
+ data.ObjectiveText[k].resize(idx+1);
+
+ data.ObjectiveText[k][idx] = str;
+ }
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() );
+}
+
+void ObjectMgr::LoadPetCreateSpells()
+{
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 pet create spells" );
+ sLog.outErrorDb("`petcreateinfo_spell` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar( result->GetRowCount() );
+
+ mPetCreateSpell.clear();
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 creature_id = fields[0].GetUInt32();
+
+ if(!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
+ continue;
+
+ PetCreateSpellEntry PetCreateSpell;
+ for(int i = 0; i < 4; i++)
+ {
+ PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
+
+ if(PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i]))
+ sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist",PetCreateSpell.spellid[i]);
+ }
+
+ mPetCreateSpell[creature_id] = PetCreateSpell;
+
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet create spells", count );
+}
+
+void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
+{
+ if(sWorld.IsScriptScheduled()) // function don't must be called in time scripts use.
+ return;
+
+ sLog.outString( "%s :", tablename);
+
+ scripts.clear(); // need for reload support
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id,delay,command,datalong,datalong2,datatext, x, y, z, o FROM %s", tablename );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u script definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ ScriptInfo tmp;
+ tmp.id = fields[0].GetUInt32();
+ tmp.delay = fields[1].GetUInt32();
+ tmp.command = fields[2].GetUInt32();
+ tmp.datalong = fields[3].GetUInt32();
+ tmp.datalong2 = fields[4].GetUInt32();
+ tmp.datatext = fields[5].GetCppString();
+ tmp.x = fields[6].GetFloat();
+ tmp.y = fields[7].GetFloat();
+ tmp.z = fields[8].GetFloat();
+ tmp.o = fields[9].GetFloat();
+
+ // generic command args check
+ switch(tmp.command)
+ {
+ case SCRIPT_COMMAND_TALK:
+ {
+ if(tmp.datalong > 3)
+ {
+ sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_TELEPORT_TO:
+ {
+ if(!sMapStore.LookupEntry(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
+ {
+ sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.x,tmp.y,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
+ {
+ if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
+ {
+ sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.x,tmp.y,tmp.id);
+ continue;
+ }
+
+ if(!GetCreatureTemplate(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
+ {
+ GameObjectData const* data = GetGOData(tmp.datalong);
+ if(!data)
+ {
+ sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ GameObjectInfo const* info = GetGameObjectInfo(data->id);
+ if(!info)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,data->id,tmp.id);
+ continue;
+ }
+
+ if( info->type==GAMEOBJECT_TYPE_FISHINGNODE ||
+ info->type==GAMEOBJECT_TYPE_FISHINGHOLE ||
+ info->type==GAMEOBJECT_TYPE_DOOR ||
+ info->type==GAMEOBJECT_TYPE_BUTTON ||
+ info->type==GAMEOBJECT_TYPE_TRAP )
+ {
+ sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,info->id,tmp.id);
+ continue;
+ }
+ break;
+ }
+ case SCRIPT_COMMAND_OPEN_DOOR:
+ case SCRIPT_COMMAND_CLOSE_DOOR:
+ {
+ GameObjectData const* data = GetGOData(tmp.datalong);
+ if(!data)
+ {
+ sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",tablename,tmp.datalong,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ GameObjectInfo const* info = GetGameObjectInfo(data->id);
+ if(!info)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",tablename,tmp.datalong,data->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ if( info->type!=GAMEOBJECT_TYPE_DOOR)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u",tablename,info->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ break;
+ }
+ case SCRIPT_COMMAND_QUEST_EXPLORED:
+ {
+ Quest const* quest = objmgr.GetQuestTemplate(tmp.datalong);
+ if(!quest)
+ {
+ sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+
+ // continue; - quest objective requiremet set and command can be allowed
+ }
+
+ if(float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong2,tmp.id);
+ continue;
+ }
+
+ if(tmp.datalong2 && float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(DEFAULT_VISIBILITY_DISTANCE));
+ continue;
+ }
+
+ if(tmp.datalong2 && float(tmp.datalong2) < INTERACTION_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(INTERACTION_DISTANCE));
+ continue;
+ }
+
+ break;
+ }
+
+ case SCRIPT_COMMAND_REMOVE_AURA:
+ case SCRIPT_COMMAND_CAST_SPELL:
+ {
+ if(!sSpellStore.LookupEntry(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (scripts.find(tmp.id) == scripts.end())
+ {
+ ScriptMap emptyMap;
+ scripts[tmp.id] = emptyMap;
+ }
+ scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u script definitions", count );
+}
+
+void ObjectMgr::LoadGameObjectScripts()
+{
+ LoadScripts(sGameObjectScripts, "gameobject_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
+ {
+ if(!GetGOData(itr->first))
+ sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadQuestEndScripts()
+{
+ objmgr.LoadScripts(sQuestEndScripts, "quest_end_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
+ {
+ if(!GetQuestTemplate(itr->first))
+ sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadQuestStartScripts()
+{
+ objmgr.LoadScripts(sQuestStartScripts,"quest_start_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
+ {
+ if(!GetQuestTemplate(itr->first))
+ sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadSpellScripts()
+{
+ objmgr.LoadScripts(sSpellScripts, "spell_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
+ continue;
+ }
+
+ //check for correct spellEffect
+ bool found = false;
+ for(int i=0; i<3; ++i)
+ {
+ // skip empty effects
+ if( !spellInfo->Effect[i] )
+ continue;
+
+ if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+}
+
+void ObjectMgr::LoadEventScripts()
+{
+ objmgr.LoadScripts(sEventScripts, "event_scripts");
+
+ std::set<uint32> evt_scripts;
+ // Load all possible script entries from gameobjects
+ for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
+ {
+ GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i);
+ if (goInfo)
+ {
+ switch(goInfo->type)
+ {
+ case GAMEOBJECT_TYPE_GOOBER:
+ if(goInfo->goober.eventId)
+ evt_scripts.insert(goInfo->goober.eventId);
+ break;
+ case GAMEOBJECT_TYPE_CHEST:
+ if(goInfo->chest.eventId)
+ evt_scripts.insert(goInfo->chest.eventId);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // Load all possible script entries from spells
+ for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
+ {
+ SpellEntry const * spell = sSpellStore.LookupEntry(i);
+ if (spell)
+ {
+ for(int j=0; j<3; ++j)
+ {
+ if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT )
+ {
+ if (spell->EffectMiscValue[j])
+ evt_scripts.insert(spell->EffectMiscValue[j]);
+ }
+ }
+ }
+ }
+ // Then check if all scripts are in above list of possible script entries
+ for(ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
+ {
+ std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
+ if (itr2 == evt_scripts.end())
+ sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not refering to any gameobject_template type 10 data2 field or type 3 data6 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT);
+ }
+}
+
+void ObjectMgr::LoadItemTexts()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id, text FROM item_text");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u item pages", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ Field* fields;
+ do
+ {
+ bar.step();
+
+ fields = result->Fetch();
+
+ mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
+
+ ++count;
+
+ } while ( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u item texts", count );
+}
+
+void ObjectMgr::LoadPageTexts()
+{
+ sPageTextStore.Free(); // for reload case
+
+ sPageTextStore.Load();
+ sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
+ sLog.outString();
+
+ for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
+ {
+ // check data correctness
+ PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
+ if(!page)
+ continue;
+
+ if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
+ {
+ sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
+ continue;
+ }
+
+ // detect circular reference
+ std::set<uint32> checkedPages;
+ for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
+ {
+ if(!pageItr->Next_Page)
+ break;
+ checkedPages.insert(pageItr->Page_ID);
+ if(checkedPages.find(pageItr->Next_Page)!=checkedPages.end())
+ {
+ std::ostringstream ss;
+ ss<< "The text page(s) ";
+ for (std::set<uint32>::iterator itr= checkedPages.begin();itr!=checkedPages.end(); itr++)
+ ss << *itr << " ";
+ ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
+ << pageItr->Page_ID <<" to 0";
+ sLog.outErrorDb(ss.str().c_str());
+ const_cast<PageText*>(pageItr)->Next_Page = 0;
+ break;
+ }
+ }
+ }
+}
+
+void ObjectMgr::LoadPageTextLocales()
+{
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ PageTextLocale& data = mPageTextLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(str.empty())
+ continue;
+
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text.size() <= idx)
+ data.Text.resize(idx+1);
+
+ data.Text[idx] = str;
+ }
+ }
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() );
+}
+
+void ObjectMgr::LoadInstanceTemplate()
+{
+ sInstanceTemplate.Load();
+
+ for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
+ {
+ InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
+ if(!temp) continue;
+ const MapEntry* entry = sMapStore.LookupEntry(temp->map);
+ if(!entry)
+ {
+ sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
+ continue;
+ }
+ else if(!entry->HasResetTime())
+ continue;
+
+ if(temp->reset_delay == 0)
+ {
+ // use defaults from the DBC
+ if(entry->SupportsHeroicMode())
+ {
+ temp->reset_delay = entry->resetTimeHeroic / DAY;
+ }
+ else if (entry->resetTimeRaid && entry->map_type == MAP_RAID)
+ {
+ temp->reset_delay = entry->resetTimeRaid / DAY;
+ }
+ }
+
+ // the reset_delay must be atleast one day
+ temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
+ }
+
+ sLog.outString( ">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::AddGossipText(GossipText *pGText)
+{
+ ASSERT( pGText->Text_ID );
+ ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() );
+ mGossipText[pGText->Text_ID] = pGText;
+}
+
+GossipText *ObjectMgr::GetGossipText(uint32 Text_ID)
+{
+ GossipTextMap::const_iterator itr;
+ for (itr = mGossipText.begin(); itr != mGossipText.end(); itr++)
+ {
+ if(itr->second->Text_ID == Text_ID)
+ return itr->second;
+ }
+ return NULL;
+}
+
+void ObjectMgr::LoadGossipText()
+{
+ GossipText *pGText;
+ QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" );
+
+ int count = 0;
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u npc texts", count );
+ return;
+ }
+
+ int cic;
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ cic = 0;
+
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ pGText = new GossipText;
+ pGText->Text_ID = fields[cic++].GetUInt32();
+
+ for (int i=0; i< 8; i++)
+ {
+ pGText->Options[i].Text_0 = fields[cic++].GetCppString();
+ pGText->Options[i].Text_1 = fields[cic++].GetCppString();
+
+ pGText->Options[i].Language = fields[cic++].GetUInt32();
+ pGText->Options[i].Probability = fields[cic++].GetFloat();
+
+ pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32();
+
+ pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32();
+
+ pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32();
+ }
+
+ if ( !pGText->Text_ID ) continue;
+ AddGossipText( pGText );
+
+ } while( result->NextRow() );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u npc texts", count );
+ delete result;
+}
+
+void ObjectMgr::LoadNpcTextLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "Text0_0_loc1,Text0_1_loc1,Text1_0_loc1,Text1_1_loc1,Text2_0_loc1,Text2_1_loc1,Text3_0_loc1,Text3_1_loc1,Text4_0_loc1,Text4_1_loc1,Text5_0_loc1,Text5_1_loc1,Text6_0_loc1,Text6_1_loc1,Text7_0_loc1,Text7_1_loc1,"
+ "Text0_0_loc2,Text0_1_loc2,Text1_0_loc2,Text1_1_loc2,Text2_0_loc2,Text2_1_loc2,Text3_0_loc2,Text3_1_loc1,Text4_0_loc2,Text4_1_loc2,Text5_0_loc2,Text5_1_loc2,Text6_0_loc2,Text6_1_loc2,Text7_0_loc2,Text7_1_loc2,"
+ "Text0_0_loc3,Text0_1_loc3,Text1_0_loc3,Text1_1_loc3,Text2_0_loc3,Text2_1_loc3,Text3_0_loc3,Text3_1_loc1,Text4_0_loc3,Text4_1_loc3,Text5_0_loc3,Text5_1_loc3,Text6_0_loc3,Text6_1_loc3,Text7_0_loc3,Text7_1_loc3,"
+ "Text0_0_loc4,Text0_1_loc4,Text1_0_loc4,Text1_1_loc4,Text2_0_loc4,Text2_1_loc4,Text3_0_loc4,Text3_1_loc1,Text4_0_loc4,Text4_1_loc4,Text5_0_loc4,Text5_1_loc4,Text6_0_loc4,Text6_1_loc4,Text7_0_loc4,Text7_1_loc4,"
+ "Text0_0_loc5,Text0_1_loc5,Text1_0_loc5,Text1_1_loc5,Text2_0_loc5,Text2_1_loc5,Text3_0_loc5,Text3_1_loc1,Text4_0_loc5,Text4_1_loc5,Text5_0_loc5,Text5_1_loc5,Text6_0_loc5,Text6_1_loc5,Text7_0_loc5,Text7_1_loc5,"
+ "Text0_0_loc6,Text0_1_loc6,Text1_0_loc6,Text1_1_loc6,Text2_0_loc6,Text2_1_loc6,Text3_0_loc6,Text3_1_loc1,Text4_0_loc6,Text4_1_loc6,Text5_0_loc6,Text5_1_loc6,Text6_0_loc6,Text6_1_loc6,Text7_0_loc6,Text7_1_loc6,"
+ "Text0_0_loc7,Text0_1_loc7,Text1_0_loc7,Text1_1_loc7,Text2_0_loc7,Text2_1_loc7,Text3_0_loc7,Text3_1_loc1,Text4_0_loc7,Text4_1_loc7,Text5_0_loc7,Text5_1_loc7,Text6_0_loc7,Text6_1_loc7,Text7_0_loc7,Text7_1_loc7, "
+ "Text0_0_loc8,Text0_1_loc8,Text1_0_loc8,Text1_1_loc8,Text2_0_loc8,Text2_1_loc8,Text3_0_loc8,Text3_1_loc1,Text4_0_loc8,Text4_1_loc8,Text5_0_loc8,Text5_1_loc8,Text6_0_loc8,Text6_1_loc8,Text7_0_loc8,Text7_1_loc8 "
+ " FROM locales_npc_text");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ NpcTextLocale& data = mNpcTextLocaleMap[entry];
+
+ for(int i=1; i<MAX_LOCALE; ++i)
+ {
+ for(int j=0; j<8; ++j)
+ {
+ std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString();
+ if(!str0.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text_0[j].size() <= idx)
+ data.Text_0[j].resize(idx+1);
+
+ data.Text_0[j][idx] = str0;
+ }
+ }
+ std::string str1 = fields[1+8*2*(i-1)+2*j+1].GetCppString();
+ if(!str1.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text_1[j].size() <= idx)
+ data.Text_1[j].resize(idx+1);
+
+ data.Text_1[j][idx] = str1;
+ }
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() );
+}
+
+//not very fast function but it is called only once a day, or on starting-up
+void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
+{
+ time_t basetime = time(NULL);
+ sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
+ //delete all old mails without item and without body immediately, if starting server
+ if (!serverUp)
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" I64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime);
+ if ( !result )
+ return; // any mails need to be returned or deleted
+ Field *fields;
+ //std::ostringstream delitems, delmails; //will be here for optimization
+ //bool deletemail = false, deleteitem = false;
+ //delitems << "DELETE FROM item_instance WHERE guid IN ( ";
+ //delmails << "DELETE FROM mail WHERE id IN ( "
+ do
+ {
+ fields = result->Fetch();
+ Mail *m = new Mail;
+ m->messageID = fields[0].GetUInt32();
+ m->messageType = fields[1].GetUInt8();
+ m->sender = fields[2].GetUInt32();
+ m->receiver = fields[3].GetUInt32();
+ m->itemTextId = fields[4].GetUInt32();
+ bool has_items = fields[5].GetBool();
+ m->expire_time = (time_t)fields[6].GetUInt64();
+ m->deliver_time = 0;
+ m->COD = fields[7].GetUInt32();
+ m->checked = fields[8].GetUInt32();
+ m->mailTemplateId = fields[9].GetInt16();
+
+ Player *pl = 0;
+ if (serverUp)
+ pl = objmgr.GetPlayer((uint64)m->receiver);
+ if (pl && pl->m_mailsLoaded)
+ { //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
+ //his in mailbox and he has already listed his mails )
+ delete m;
+ continue;
+ }
+ //delete or return mail:
+ if (has_items)
+ {
+ QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
+ if(resultItems)
+ {
+ do
+ {
+ Field *fields2 = resultItems->Fetch();
+
+ uint32 item_guid_low = fields2[0].GetUInt32();
+ uint32 item_template = fields2[1].GetUInt32();
+
+ m->AddItem(item_guid_low, item_template);
+ }
+ while (resultItems->NextRow());
+
+ delete resultItems;
+ }
+ //if it is mail from AH, it shouldn't be returned, but deleted
+ if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_AUCTION | MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
+ {
+ // mail open and then not returned
+ for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
+ }
+ else
+ {
+ //mail will be returned:
+ CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
+ delete m;
+ continue;
+ }
+ }
+
+ if (m->itemTextId)
+ CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
+
+ //deletemail = true;
+ //delmails << m->messageID << ", ";
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
+ delete m;
+ } while (result->NextRow());
+ delete result;
+}
+
+void ObjectMgr::LoadQuestAreaTriggers()
+{
+ mQuestAreaTriggerMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query( "SELECT id,quest FROM areatrigger_involvedrelation" );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quest trigger points", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 trigger_ID = fields[0].GetUInt32();
+ uint32 quest_ID = fields[1].GetUInt32();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",trigger_ID);
+ continue;
+ }
+
+ Quest const* quest = GetQuestTemplate(quest_ID);
+
+ if(!quest)
+ {
+ sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u",trigger_ID,quest_ID);
+ continue;
+ }
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+
+ // continue; - quest modified to required obkective and trigger can be allowed.
+ }
+
+ mQuestAreaTriggerMap[trigger_ID] = quest_ID;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quest trigger points", count );
+}
+
+void ObjectMgr::LoadTavernAreaTriggers()
+{
+ mTavernAreaTriggerSet.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u tavern triggers", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+
+ mTavernAreaTriggerSet.insert(Trigger_ID);
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u tavern triggers", count );
+}
+
+void ObjectMgr::LoadAreaTriggerScripts()
+{
+ mAreaTriggerScripts.clear(); // need for reload case
+ QueryResult *result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areatrigger scripts", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+ std::string scriptName = fields[1].GetCppString();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+ mAreaTriggerScripts[Trigger_ID] = scriptName;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areatrigger scripts", count );
+}
+uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid )
+{
+ bool found = false;
+ float dist;
+ uint32 id = 0;
+
+ for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
+ {
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
+ if(node && node->map_id == mapid)
+ {
+ float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
+ if(found)
+ {
+ if(dist2 < dist)
+ {
+ dist = dist2;
+ id = i;
+ }
+ }
+ else
+ {
+ found = true;
+ dist = dist2;
+ id = i;
+ }
+ }
+ }
+
+ return id;
+}
+
+void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
+{
+ TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
+ if(src_i==sTaxiPathSetBySource.end())
+ {
+ path = 0;
+ cost = 0;
+ return;
+ }
+
+ TaxiPathSetForSource& pathSet = src_i->second;
+
+ TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
+ if(dest_i==pathSet.end())
+ {
+ path = 0;
+ cost = 0;
+ return;
+ }
+
+ cost = dest_i->second.price;
+ path = dest_i->second.ID;
+}
+
+uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team )
+{
+ uint16 mount_entry = 0;
+ uint16 mount_id = 0;
+
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
+ if(node)
+ {
+ if (team == ALLIANCE)
+ {
+ mount_entry = node->alliance_mount_type;
+ CreatureInfo const *ci = objmgr.GetCreatureTemplate(mount_entry);
+ if(ci)
+ mount_id = ci->DisplayID_A;
+ }
+ if (team == HORDE)
+ {
+ mount_entry = node->horde_mount_type;
+ CreatureInfo const *ci = objmgr.GetCreatureTemplate(mount_entry);
+ if(ci)
+ mount_id = ci->DisplayID_H;
+ }
+ }
+
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(mount_id);
+ if(!minfo)
+ {
+ sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table `creature_model_info`, can't load. ",
+ mount_entry,id,team,mount_id);
+
+ return false;
+ }
+ if(minfo->modelid_other_gender!=0)
+ mount_id = urand(0,1) ? mount_id : minfo->modelid_other_gender;
+
+ return mount_id;
+}
+
+void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
+{
+ if(path >= sTaxiPathNodesByPath.size())
+ return;
+
+ TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
+ pathnodes.Resize(nodeList.size());
+ mapIds.resize(nodeList.size());
+
+ for(size_t i = 0; i < nodeList.size(); ++i)
+ {
+ pathnodes[ i ].x = nodeList[i].x;
+ pathnodes[ i ].y = nodeList[i].y;
+ pathnodes[ i ].z = nodeList[i].z;
+
+ mapIds[i] = nodeList[i].mapid;
+ }
+}
+
+void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
+{
+ if(path >= sTaxiPathNodesByPath.size())
+ return;
+
+ TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
+ pathnodes.Resize(nodeList.size());
+
+ for(size_t i = 0; i < nodeList.size(); ++i)
+ {
+ pathnodes[ i ].mapid = nodeList[i].mapid;
+ pathnodes[ i ].x = nodeList[i].x;
+ pathnodes[ i ].y = nodeList[i].y;
+ pathnodes[ i ].z = nodeList[i].z;
+ pathnodes[ i ].actionFlag = nodeList[i].actionFlag;
+ pathnodes[ i ].delay = nodeList[i].delay;
+ }
+}
+
+void ObjectMgr::LoadGraveyardZones()
+{
+ mGraveYardMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u graveyard-zone links", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 safeLocId = fields[0].GetUInt32();
+ uint32 zoneId = fields[1].GetUInt32();
+ uint32 team = fields[2].GetUInt32();
+
+ WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
+ if(!entry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",safeLocId);
+ continue;
+ }
+
+ AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
+ if(!areaEntry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.",zoneId);
+ continue;
+ }
+
+ if(areaEntry->zone != 0)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.",zoneId);
+ continue;
+ }
+
+ if(team!=0 && team!=HORDE && team!=ALLIANCE)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.",team);
+ continue;
+ }
+
+ if(entry->map_id != areaEntry->mapid && team != 0)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for ghost zone (%u) at map %u and graveyard (%u) at map %u for team %u, but in case maps are different, player faction setting is ignored. Use faction 0 instead.",zoneId,areaEntry->mapid, safeLocId, entry->map_id, team);
+ team = 0;
+ }
+
+ if(!AddGraveYardLink(safeLocId,zoneId,team,false))
+ sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Garveyard (ID: %u) and Zone (ID: %u), skipped.",safeLocId,zoneId);
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u graveyard-zone links", count );
+}
+
+WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
+{
+ // search for zone associated closest graveyard
+ uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y);
+
+ // Simulate std. algorithm:
+ // found some graveyard associated to (ghost_zone,ghost_map)
+ //
+ // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
+ // then check faction
+ // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
+ // then skip check faction
+ GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
+ GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
+ if(graveLow==graveUp)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
+ return NULL;
+ }
+
+ bool foundNear = false;
+ float distNear;
+ WorldSafeLocsEntry const* entryNear = NULL;
+ WorldSafeLocsEntry const* entryFar = NULL;
+
+ for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
+ {
+ GraveYardData const& data = itr->second;
+
+ WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
+ if(!entry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",data.safeLocId);
+ continue;
+ }
+
+ // remember first graveyard at another map and ignore other
+ if(MapId != entry->map_id)
+ {
+ if(!entryFar)
+ entryFar = entry;
+ continue;
+ }
+
+ // skip enemy faction graveyard at same map (normal area, city, or battleground)
+ // team == 0 case can be at call from .neargrave
+ if(data.team != 0 && team != 0 && data.team != team)
+ continue;
+
+ // find now nearest graveyard at same map
+ float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
+ if(foundNear)
+ {
+ if(dist2 < distNear)
+ {
+ distNear = dist2;
+ entryNear = entry;
+ }
+ }
+ else
+ {
+ foundNear = true;
+ distNear = dist2;
+ entryNear = entry;
+ }
+ }
+
+ if(entryNear)
+ return entryNear;
+
+ return entryFar;
+}
+
+GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
+{
+ GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
+ GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
+
+ for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
+ {
+ if(itr->second.safeLocId==id)
+ return &itr->second;
+ }
+
+ return NULL;
+}
+
+bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
+{
+ if(FindGraveYardData(id,zoneId))
+ return false;
+
+ // add link to loaded data
+ GraveYardData data;
+ data.safeLocId = id;
+ data.team = team;
+
+ mGraveYardMap.insert(GraveYardMap::value_type(zoneId,data));
+
+ // add link to DB
+ if(inDB)
+ {
+ WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
+ "VALUES ('%u', '%u','%u')",id,zoneId,team);
+ }
+
+ return true;
+}
+
+void ObjectMgr::LoadAreaTriggerTeleports()
+{
+ mAreaTriggers.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ QueryResult *result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ ++count;
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+
+ AreaTrigger at;
+
+ at.requiredLevel = fields[1].GetUInt8();
+ at.requiredItem = fields[2].GetUInt32();
+ at.requiredItem2 = fields[3].GetUInt32();
+ at.heroicKey = fields[4].GetUInt32();
+ at.heroicKey2 = fields[5].GetUInt32();
+ at.requiredQuest = fields[6].GetUInt32();
+ at.requiredFailedText = fields[7].GetCppString();
+ at.target_mapId = fields[8].GetUInt32();
+ at.target_X = fields[9].GetFloat();
+ at.target_Y = fields[10].GetFloat();
+ at.target_Z = fields[11].GetFloat();
+ at.target_Orientation = fields[12].GetFloat();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+
+ if(at.requiredItem)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(at.requiredItem);
+ if(!pProto)
+ {
+ sLog.outError("Key item %u does not exist for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
+ at.requiredItem = 0;
+ }
+ }
+ if(at.requiredItem2)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(at.requiredItem2);
+ if(!pProto)
+ {
+ sLog.outError("Second item %u not exist for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
+ at.requiredItem2 = 0;
+ }
+ }
+
+ if(at.heroicKey)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(at.heroicKey);
+ if(!pProto)
+ {
+ sLog.outError("Heroic key item %u not exist for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
+ at.heroicKey = 0;
+ }
+ }
+
+ if(at.heroicKey2)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(at.heroicKey2);
+ if(!pProto)
+ {
+ sLog.outError("Heroic second key item %u not exist for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
+ at.heroicKey2 = 0;
+ }
+ }
+
+ if(at.requiredQuest)
+ {
+ if(!mQuestTemplates[at.requiredQuest])
+ {
+ sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuest,Trigger_ID);
+ at.requiredQuest = 0;
+ }
+ }
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
+ if(!mapEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Trigger_ID,at.target_mapId);
+ continue;
+ }
+
+ if(at.target_X==0 && at.target_Y==0 && at.target_Z==0)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) target coordinates not provided.",Trigger_ID);
+ continue;
+ }
+
+ mAreaTriggers[Trigger_ID] = at;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
+}
+
+AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
+{
+ const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
+ if(!mapEntry) return NULL;
+ for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); itr++)
+ {
+ if(itr->second.target_mapId == mapEntry->parent_map)
+ {
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
+ if(atEntry && atEntry->mapid == Map)
+ return &itr->second;
+ }
+ }
+ return NULL;
+}
+
+void ObjectMgr::SetHighestGuids()
+{
+ QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
+ if( result )
+ {
+ m_hiCharGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = WorldDatabase.Query( "SELECT MAX(guid) FROM creature" );
+ if( result )
+ {
+ m_hiCreatureGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM character_pet" );
+ if( result )
+ {
+ m_hiPetGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" );
+ if( result )
+ {
+ m_hiItemGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ // Cleanup other tables from not existed guids (>=m_hiItemGuid)
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);
+
+ result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject" );
+ if( result )
+ {
+ m_hiGoGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse" );
+ if( result )
+ {
+ m_auctionid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+ else
+ {
+ m_auctionid = 0;
+ }
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
+ if( result )
+ {
+ m_mailid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+ else
+ {
+ m_mailid = 0;
+ }
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
+ if( result )
+ {
+ m_ItemTextId = (*result)[0].GetUInt32();
+
+ delete result;
+ }
+ else
+ m_ItemTextId = 0;
+
+ result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
+ if( result )
+ {
+ m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+}
+
+uint32 ObjectMgr::GenerateAuctionID()
+{
+ ++m_auctionid;
+ if(m_auctionid>=0xFFFFFFFF)
+ {
+ sLog.outError("Auctions ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_auctionid;
+}
+
+uint32 ObjectMgr::GenerateMailID()
+{
+ ++m_mailid;
+ if(m_mailid>=0xFFFFFFFF)
+ {
+ sLog.outError("Mail ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_mailid;
+}
+
+uint32 ObjectMgr::GenerateItemTextID()
+{
+ ++m_ItemTextId;
+ if(m_ItemTextId>=0xFFFFFFFF)
+ {
+ sLog.outError("Item text ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_ItemTextId;
+}
+
+uint32 ObjectMgr::CreateItemText(std::string text)
+{
+ uint32 newItemTextId = GenerateItemTextID();
+ //insert new itempage to container
+ mItemTexts[ newItemTextId ] = text;
+ //save new itempage
+ CharacterDatabase.escape_string(text);
+ //any Delete query needed, itemTextId is maximum of all ids
+ std::ostringstream query;
+ query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
+ CharacterDatabase.Execute(query.str().c_str()); //needs to be run this way, because mail body may be more than 1024 characters
+ return newItemTextId;
+}
+
+uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
+{
+ switch(guidhigh)
+ {
+ case HIGHGUID_ITEM:
+ ++m_hiItemGuid;
+ if(m_hiItemGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Item guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiItemGuid;
+ case HIGHGUID_UNIT:
+ ++m_hiCreatureGuid;
+ if(m_hiCreatureGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Creature guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCreatureGuid;
+ case HIGHGUID_PET:
+ ++m_hiPetGuid;
+ if(m_hiPetGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Pet guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiPetGuid;
+ case HIGHGUID_PLAYER:
+ ++m_hiCharGuid;
+ if(m_hiCharGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Players guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCharGuid;
+ case HIGHGUID_GAMEOBJECT:
+ ++m_hiGoGuid;
+ if(m_hiGoGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Gameobject guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiGoGuid;
+ case HIGHGUID_CORPSE:
+ ++m_hiCorpseGuid;
+ if(m_hiCorpseGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Corpse guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCorpseGuid;
+ case HIGHGUID_DYNAMICOBJECT:
+ ++m_hiDoGuid;
+ if(m_hiDoGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("DynamicObject guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiDoGuid;
+ default:
+ ASSERT(0);
+ }
+
+ ASSERT(0);
+ return 0;
+}
+
+void ObjectMgr::LoadGameObjectLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
+ "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
+ "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ GameObjectLocale& data = mGameObjectLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+ }
+
+ for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.CastBarCaption.size() <= idx)
+ data.CastBarCaption.resize(idx+1);
+
+ data.CastBarCaption[idx] = str;
+ }
+ }
+ }
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() );
+}
+
+void ObjectMgr::LoadGameobjectInfo()
+{
+ sGOStorage.Load();
+
+ // some checks
+ for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
+ {
+ GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
+ if(!goInfo)
+ continue;
+
+ switch(goInfo->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR: //0
+ {
+ if(goInfo->door.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->door.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_BUTTON: //1
+ {
+ if(goInfo->button.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->button.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_CHEST: //3
+ {
+ if(goInfo->chest.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->chest.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId);
+ }
+ if(goInfo->chest.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_TRAP: //6
+ {
+ /* disable check for while
+ if(goInfo->trap.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->trap.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId);
+ }
+ */
+ break;
+ }
+ case GAMEOBJECT_TYPE_CHAIR: //7
+ if(goInfo->chair.height > 2)
+ {
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.",
+ id,goInfo->type,goInfo->chair.height);
+
+ // prevent client and server unexpected work
+ const_cast<GameObjectInfo*>(goInfo)->chair.height = 0;
+ }
+ break;
+ case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
+ {
+ if(goInfo->spellFocus.focusId)
+ {
+ if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
+ id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId);
+ }
+
+ if(goInfo->spellFocus.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER: //10
+ {
+ if(goInfo->goober.pageId) // pageId
+ {
+ if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
+ id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId);
+ }
+ /* disable check for while
+ if(goInfo->goober.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->goober.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId);
+ }
+ */
+ if(goInfo->goober.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
+ {
+ if(goInfo->moTransport.taxiPathId)
+ {
+ if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
+ id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
+ {
+ /* disabled
+ if(goInfo->summoningRitual.spellId)
+ {
+ if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId);
+ }
+ */
+ break;
+ }
+ case GAMEOBJECT_TYPE_SPELLCASTER: //22
+ {
+ if(goInfo->spellcaster.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId);
+ }
+ break;
+ }
+ }
+ }
+
+ sLog.outString( ">> Loaded %u game object templates", sGOStorage.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadExplorationBaseXP()
+{
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u BaseXP definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ uint32 level = fields[0].GetUInt32();
+ uint32 basexp = fields[1].GetUInt32();
+ mBaseXPTable[level] = basexp;
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u BaseXP definitions", count );
+}
+
+uint32 ObjectMgr::GetBaseXP(uint32 level)
+{
+ return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
+}
+
+void ObjectMgr::LoadPetNames()
+{
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet name parts", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ std::string word = fields[0].GetString();
+ uint32 entry = fields[1].GetUInt32();
+ bool half = fields[2].GetBool();
+ if(half)
+ PetHalfName1[entry].push_back(word);
+ else
+ PetHalfName0[entry].push_back(word);
+ ++count;
+ }
+ while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet name parts", count );
+}
+
+void ObjectMgr::LoadPetNumber()
+{
+ QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ m_hiPetNumber = fields[0].GetUInt32()+1;
+ delete result;
+ }
+
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
+}
+
+std::string ObjectMgr::GeneratePetName(uint32 entry)
+{
+ std::vector<std::string> & list0 = PetHalfName0[entry];
+ std::vector<std::string> & list1 = PetHalfName1[entry];
+
+ if(list0.empty() || list1.empty())
+ {
+ CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(entry);
+ char* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
+ if(!petname)
+ petname = cinfo->Name;
+ return std::string(petname);
+ }
+
+ return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
+}
+
+uint32 ObjectMgr::GeneratePetNumber()
+{
+ return ++m_hiPetNumber;
+}
+
+void ObjectMgr::LoadCorpses()
+{
+ uint32 count = 0;
+ // 0 1 2 3 4 5 6 7 8 10
+ QueryResult *result = CharacterDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, data, time, corpse_type, instance, guid FROM corpse WHERE corpse_type <> 0");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u corpses", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 guid = fields[result->GetFieldCount()-1].GetUInt32();
+
+ Corpse *corpse = new Corpse;
+ if(!corpse->LoadFromDB(guid,fields))
+ {
+ delete corpse;
+ continue;
+ }
+
+ ObjectAccessor::Instance().AddCorpse(corpse);
+
+ ++count;
+ }
+ while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u corpses", count );
+}
+
+void ObjectMgr::LoadReputationOnKill()
+{
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
+ // 3 4 5 6 7 8 9
+ "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
+ "FROM creature_onkill_reputation");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 creature_id = fields[0].GetUInt32();
+
+ ReputationOnKillEntry repOnKill;
+ repOnKill.repfaction1 = fields[1].GetUInt32();
+ repOnKill.repfaction2 = fields[2].GetUInt32();
+ repOnKill.is_teamaward1 = fields[3].GetBool();
+ repOnKill.reputration_max_cap1 = fields[4].GetUInt32();
+ repOnKill.repvalue1 = fields[5].GetInt32();
+ repOnKill.is_teamaward2 = fields[6].GetBool();
+ repOnKill.reputration_max_cap2 = fields[7].GetUInt32();
+ repOnKill.repvalue2 = fields[8].GetInt32();
+ repOnKill.team_dependent = fields[9].GetUInt8();
+
+ if(!GetCreatureTemplate(creature_id))
+ {
+ sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
+ continue;
+ }
+
+ if(repOnKill.repfaction1)
+ {
+ FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
+ if(!factionEntry1)
+ {
+ sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction1);
+ continue;
+ }
+ }
+
+ if(repOnKill.repfaction2)
+ {
+ FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
+ if(!factionEntry2)
+ {
+ sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction2);
+ continue;
+ }
+ }
+
+ mRepOnKill[creature_id] = repOnKill;
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u creature award reputation definitions", count);
+}
+
+void ObjectMgr::LoadWeatherZoneChances()
+{
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ QueryResult *result = WorldDatabase.Query("SELECT zone, spring_rain_chance, spring_snow_chance, spring_storm_chance, summer_rain_chance, summer_snow_chance, summer_storm_chance, fall_rain_chance, fall_snow_chance, fall_storm_chance, winter_rain_chance, winter_snow_chance, winter_storm_chance FROM game_weather");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 zone_id = fields[0].GetUInt32();
+
+ WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
+
+ for(int season = 0; season < WEATHER_SEASONS; ++season)
+ {
+ wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32();
+ wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32();
+ wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE-1) + 3].GetUInt32();
+
+ if(wzc.data[season].rainChance > 100)
+ {
+ wzc.data[season].rainChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season);
+ }
+
+ if(wzc.data[season].snowChance > 100)
+ {
+ wzc.data[season].snowChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season);
+ }
+
+ if(wzc.data[season].stormChance > 100)
+ {
+ wzc.data[season].stormChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season);
+ }
+ }
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u weather definitions", count);
+}
+
+void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t)
+{
+ mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
+ WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
+ if(t)
+ WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
+}
+
+void ObjectMgr::DeleteCreatureData(uint32 guid)
+{
+ // remove mapid*cellid -> guid_set map
+ CreatureData const* data = GetCreatureData(guid);
+ if(data)
+ RemoveCreatureFromGrid(guid, data);
+
+ mCreatureDataMap.erase(guid);
+}
+
+void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
+{
+ mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
+ WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
+ if(t)
+ WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
+}
+
+void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance)
+{
+ RespawnTimes::iterator next;
+
+ for(RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(GUID_HIPART(itr->first)==instance)
+ mGORespawnTimes.erase(itr);
+ }
+
+ for(RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(GUID_HIPART(itr->first)==instance)
+ mCreatureRespawnTimes.erase(itr);
+ }
+
+ WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", instance);
+ WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", instance);
+}
+
+void ObjectMgr::DeleteGOData(uint32 guid)
+{
+ // remove mapid*cellid -> guid_set map
+ GameObjectData const* data = GetGOData(guid);
+ if(data)
+ RemoveGameobjectFromGrid(guid, data);
+
+ mGameObjectDataMap.erase(guid);
+}
+
+void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
+{
+ // corpses are always added to spawn mode 0 and they are spawned by their instance id
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
+ cell_guids.corpses[player_guid] = instance;
+}
+
+void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
+{
+ // corpses are always added to spawn mode 0 and they are spawned by their instance id
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
+ cell_guids.corpses.erase(player_guid);
+}
+
+void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map,char const* table)
+{
+ map.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s",table);
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.",table,table);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 id = fields[0].GetUInt32();
+ uint32 quest = fields[1].GetUInt32();
+
+ if(mQuestTemplates.find(quest) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.",table,quest,id);
+ continue;
+ }
+
+ map.insert(QuestRelations::value_type(id,quest));
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u quest relations from %s", count,table);
+}
+
+void ObjectMgr::LoadGameobjectQuestRelations()
+{
+ LoadQuestRelationsHelper(mGOQuestRelations,"gameobject_questrelation");
+
+ for(QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
+ {
+ GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
+ if(!goInfo)
+ sLog.outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
+ sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadGameobjectInvolvedRelations()
+{
+ LoadQuestRelationsHelper(mGOQuestInvolvedRelations,"gameobject_involvedrelation");
+
+ for(QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
+ {
+ GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
+ if(!goInfo)
+ sLog.outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
+ sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadCreatureQuestRelations()
+{
+ LoadQuestRelationsHelper(mCreatureQuestRelations,"creature_questrelation");
+
+ for(QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
+ {
+ CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
+ if(!cInfo)
+ sLog.outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
+ sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadCreatureInvolvedRelations()
+{
+ LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations,"creature_involvedrelation");
+
+ for(QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
+ {
+ CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
+ if(!cInfo)
+ sLog.outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
+ sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadReservedPlayersNames()
+{
+ m_ReservedNames.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT name FROM reserved_name");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u reserved player names", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ Field* fields;
+ do
+ {
+ bar.step();
+ fields = result->Fetch();
+ std::string name= fields[0].GetCppString();
+ if(normalizePlayerName(name))
+ {
+ m_ReservedNames.insert(name);
+ ++count;
+ }
+ } while ( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u reserved player names", count );
+}
+
+enum LanguageType
+{
+ LT_BASIC_LATIN = 0x0000,
+ LT_EXTENDEN_LATIN = 0x0001,
+ LT_CYRILLIC = 0x0002,
+ LT_EAST_ASIA = 0x0004,
+ LT_ANY = 0xFFFF
+};
+
+static LanguageType GetRealmLanguageType(bool create)
+{
+ switch(sWorld.getConfig(CONFIG_REALM_ZONE))
+ {
+ case REALM_ZONE_UNKNOWN: // any language
+ case REALM_ZONE_DEVELOPMENT:
+ case REALM_ZONE_TEST_SERVER:
+ case REALM_ZONE_QA_SERVER:
+ return LT_ANY;
+ case REALM_ZONE_UNITED_STATES: // extended-Latin
+ case REALM_ZONE_OCEANIC:
+ case REALM_ZONE_LATIN_AMERICA:
+ case REALM_ZONE_ENGLISH:
+ case REALM_ZONE_GERMAN:
+ case REALM_ZONE_FRENCH:
+ case REALM_ZONE_SPANISH:
+ return LT_EXTENDEN_LATIN;
+ case REALM_ZONE_KOREA: // East-Asian
+ case REALM_ZONE_TAIWAN:
+ case REALM_ZONE_CHINA:
+ return LT_EAST_ASIA;
+ case REALM_ZONE_RUSSIAN: // Cyrillic
+ return LT_CYRILLIC;
+ default:
+ return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
+ }
+}
+
+bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
+{
+ if(strictMask==0) // any language, ignore realm
+ {
+ if(isExtendedLatinString(wstr,numericOrSpace))
+ return true;
+ if(isCyrillicString(wstr,numericOrSpace))
+ return true;
+ if(isEastAsianString(wstr,numericOrSpace))
+ return true;
+ return false;
+ }
+
+ if(strictMask & 0x2) // realm zone specific
+ {
+ LanguageType lt = GetRealmLanguageType(create);
+ if(lt & LT_EXTENDEN_LATIN)
+ if(isExtendedLatinString(wstr,numericOrSpace))
+ return true;
+ if(lt & LT_CYRILLIC)
+ if(isCyrillicString(wstr,numericOrSpace))
+ return true;
+ if(lt & LT_EAST_ASIA)
+ if(isEastAsianString(wstr,numericOrSpace))
+ return true;
+ }
+
+ if(strictMask & 0x1) // basic latin
+ {
+ if(isBasicLatinString(wstr,numericOrSpace))
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjectMgr::IsValidName( std::string name, bool create )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
+
+ return isValidString(wname,strictMask,false,create);
+}
+
+bool ObjectMgr::IsValidCharterName( std::string name )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
+
+ return isValidString(wname,strictMask,true);
+}
+
+bool ObjectMgr::IsValidPetName( std::string name )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
+
+ return isValidString(wname,strictMask,false);
+}
+
+int ObjectMgr::GetIndexForLocale( LocaleConstant loc )
+{
+ if(loc==LOCALE_enUS)
+ return -1;
+
+ for(size_t i=0;i < m_LocalForIndex.size(); ++i)
+ if(m_LocalForIndex[i]==loc)
+ return i;
+
+ return -1;
+}
+
+LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
+{
+ if (i<0 || i>=m_LocalForIndex.size())
+ return LOCALE_enUS;
+
+ return m_LocalForIndex[i];
+}
+
+int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc )
+{
+ if(loc==LOCALE_enUS)
+ return -1;
+
+ for(size_t i=0;i < m_LocalForIndex.size(); ++i)
+ if(m_LocalForIndex[i]==loc)
+ return i;
+
+ m_LocalForIndex.push_back(loc);
+ return m_LocalForIndex.size()-1;
+}
+
+void ObjectMgr::LoadBattleMastersEntry()
+{
+ mBattleMastersMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint32 bgTypeId = fields[1].GetUInt32();
+
+ mBattleMastersMap[entry] = bgTypeId;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u battlemaster entries", count );
+}
+
+void ObjectMgr::LoadGameObjectForQuests()
+{
+ mGameObjectForQuestSet.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // collect GO entries for GO that must activated
+ for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
+ {
+ GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
+ if(!goInfo)
+ continue;
+
+ switch(goInfo->type)
+ {
+ // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ uint32 loot_id = GameObject::GetLootId(goInfo);
+
+ // find quest loot for GO
+ if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
+ {
+ mGameObjectForQuestSet.insert(go_entry);
+ ++count;
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER:
+ {
+ if(goInfo->goober.questId) //quests objects
+ {
+ mGameObjectForQuestSet.insert(go_entry);
+ count++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u GameObject for quests", count );
+}
+
+bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, bool positive_entries)
+{
+ // cleanup affected map part for reloading case
+ for(MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
+ {
+ if(itr->first > 0 && positive_entries || itr->first < 0 && !positive_entries)
+ {
+ MangosStringLocaleMap::iterator itr2 = itr;
+ ++itr;
+ mMangosStringLocaleMap.erase(itr2);
+ }
+ else
+ ++itr;
+ }
+
+ QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s",table);
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ if(positive_entries) // error only in case internal strings
+ sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.",table);
+ else
+ sLog.outString(">> Loaded 0 mangos strings. DB table `%s` is empty.",table);
+ return false;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ int32 entry = fields[0].GetInt32();
+
+ if(entry==0)
+ {
+ sLog.outString("Table `%s` contain reserved entry 0, ignored.",table);
+ continue;
+ }
+ else if(entry < 0)
+ {
+ if(positive_entries)
+ {
+ sLog.outString("Table `%s` contain unexpected negative entry %i, ignored.",table,entry);
+ continue;
+ }
+ }
+ else
+ {
+ if(!positive_entries)
+ {
+ sLog.outString("Table `%s` contain unexpected positive entry %i, ignored.",table,entry);
+ continue;
+ }
+ }
+
+ MangosStringLocale& data = mMangosStringLocaleMap[entry];
+
+ if(data.Content.size() < 1)
+ data.Content.resize(1);
+
+ // 0 -> default, idx in to idx+1
+ data.Content[0] = fields[1].GetCppString();
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ // 0 -> default, idx in to idx+1
+ if(data.Content.size() <= idx+1)
+ data.Content.resize(idx+2);
+
+ data.Content[idx+1] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u MaNGOS strings from table %s", mMangosStringLocaleMap.size(),table);
+ return true;
+}
+
+const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
+{
+ // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
+ // Content[0] always exist if exist MangosStringLocale
+ if(MangosStringLocale const *msl = GetMangosStringLocale(entry))
+ {
+ if(msl->Content.size() > locale_idx+1 && !msl->Content[locale_idx+1].empty())
+ return msl->Content[locale_idx+1].c_str();
+ else
+ return msl->Content[0].c_str();
+ }
+
+ if(entry > 0)
+ sLog.outErrorDb("Entry %i not found in `mangos_string` table.",entry);
+ else
+ sLog.outErrorDb("Mangos string entry %i not found in DB.",entry);
+ return "<error>";
+}
+
+void ObjectMgr::LoadFishingBaseSkillLevel()
+{
+ mFishingBaseForArea.clear(); // for relaod case
+
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
+ int32 skill = fields[1].GetInt32();
+
+ AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
+ if(!fArea)
+ {
+ sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist",entry);
+ continue;
+ }
+
+ mFishingBaseForArea[entry] = skill;
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areas for fishing base skill level", count );
+}
+
+// Searches for the same condition already in Conditions store
+// Returns Id if found, else adds it to Conditions and returns Id
+uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 )
+{
+ PlayerCondition lc = PlayerCondition(condition, value1, value2);
+ for (uint16 i=0; i < mConditions.size(); ++i)
+ {
+ if (lc == mConditions[i])
+ return i;
+ }
+
+ mConditions.push_back(lc);
+
+ if(mConditions.size() > 0xFFFF)
+ {
+ sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!");
+ return 0;
+ }
+
+ return mConditions.size() - 1;
+}
+
+bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names )
+{
+ for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ std::wstring wname;
+ if(!Utf8toWStr(names.name[i],wname))
+ return false;
+
+ if(mainpart!=GetMainPartOfName(wname,i+1))
+ return false;
+ }
+ return true;
+}
+
+const char* ObjectMgr::GetAreaTriggerScriptName(uint32 id)
+{
+ AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(id);
+ if(i!= mAreaTriggerScripts.end())
+ return i->second.c_str();
+ return "";
+}
+
+// Checks if player meets the condition
+bool PlayerCondition::Meets(Player const * player) const
+{
+ if( !player )
+ return false; // player not present, return false
+
+ switch (condition)
+ {
+ case CONDITION_NONE:
+ return true; // empty condition, always met
+ case CONDITION_AURA:
+ return player->HasAura(value1, value2);
+ case CONDITION_ITEM:
+ return player->HasItemCount(value1, value2);
+ case CONDITION_ITEM_EQUIPPED:
+ return player->GetItemOrItemWithGemEquipped(value1) != NULL;
+ case CONDITION_ZONEID:
+ return player->GetZoneId() == value1;
+ case CONDITION_REPUTATION_RANK:
+ {
+ FactionEntry const* faction = sFactionStore.LookupEntry(value1);
+ return faction && player->GetReputationRank(faction) >= value2;
+ }
+ case CONDITION_TEAM:
+ return player->GetTeam() == value1;
+ case CONDITION_SKILL:
+ return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2;
+ case CONDITION_QUESTREWARDED:
+ return player->GetQuestRewardStatus(value1);
+ case CONDITION_QUESTTAKEN:
+ {
+ QuestStatus status = player->GetQuestStatus(value1);
+ return (status == QUEST_STATUS_INCOMPLETE);
+ }
+ case CONDITION_AD_COMMISSION_AURA:
+ {
+ Unit::AuraMap const& auras = player->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580)
+ return true;
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+// Verification of condition values validity
+bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
+{
+ if( condition >= MAX_CONDITION) // Wrong condition type
+ {
+ sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
+ return false;
+ }
+
+ switch (condition)
+ {
+ case CONDITION_AURA:
+ {
+ if(!sSpellStore.LookupEntry(value1))
+ {
+ sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
+ return false;
+ }
+ if(value2 > 2)
+ {
+ sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ITEM:
+ {
+ ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
+ if(!proto)
+ {
+ sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ITEM_EQUIPPED:
+ {
+ ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
+ if(!proto)
+ {
+ sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ZONEID:
+ {
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
+ if(!areaEntry)
+ {
+ sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1);
+ return false;
+ }
+ if(areaEntry->zone != 0)
+ {
+ sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_REPUTATION_RANK:
+ {
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
+ if(!factionEntry)
+ {
+ sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_TEAM:
+ {
+ if (value1 != ALLIANCE && value1 != HORDE)
+ {
+ sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_SKILL:
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1);
+ if (!pSkill)
+ {
+ sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1);
+ return false;
+ }
+ if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() )
+ {
+ sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_QUESTREWARDED:
+ case CONDITION_QUESTTAKEN:
+ {
+ Quest const *Quest = objmgr.GetQuestTemplate(value1);
+ if (!Quest)
+ {
+ sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1);
+ return false;
+ }
+ if(value2)
+ sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
+ break;
+ }
+ case CONDITION_AD_COMMISSION_AURA:
+ {
+ if(value1)
+ sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1);
+ if(value2)
+ sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
+ break;
+ }
+ }
+ return true;
+}
+
+SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
+{
+ switch(pSkill->categoryId)
+ {
+ case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
+ case SKILL_CATEGORY_WEAPON:
+ if(pSkill->id!=SKILL_FIST_WEAPONS)
+ return SKILL_RANGE_LEVEL;
+ else
+ return SKILL_RANGE_MONO;
+ case SKILL_CATEGORY_ARMOR:
+ case SKILL_CATEGORY_CLASS:
+ if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING)
+ return SKILL_RANGE_MONO;
+ else
+ return SKILL_RANGE_LEVEL;
+ case SKILL_CATEGORY_SECONDARY:
+ case SKILL_CATEGORY_PROFESSION:
+ // not set skills for professions and racial abilities
+ if(IsProfessionSkill(pSkill->id))
+ return SKILL_RANGE_RANK;
+ else if(racial)
+ return SKILL_RANGE_NONE;
+ else
+ return SKILL_RANGE_MONO;
+ default:
+ case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
+ case SKILL_CATEGORY_NOT_DISPLAYED: //only GENEREC(DND)
+ return SKILL_RANGE_NONE;
+ }
+}
+
+const char* GetAreaTriggerScriptNameById(uint32 id)
+{
+ return objmgr.GetAreaTriggerScriptName(id);
+}
+
+
+bool LoadMangosStrings(DatabaseType& db, char const* table)
+{
+ // for scripting localized strings allowed use _only_ negative entries
+ return objmgr.LoadMangosStrings(db,table,false);
+}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
new file mode 100644
index 00000000000..f7b7d0a3cca
--- /dev/null
+++ b/src/game/ObjectMgr.h
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _OBJECTMGR_H
+#define _OBJECTMGR_H
+
+#include "Log.h"
+#include "Object.h"
+#include "Bag.h"
+#include "Creature.h"
+#include "Player.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
+#include "Corpse.h"
+#include "QuestDef.h"
+#include "Path.h"
+#include "ItemPrototype.h"
+#include "NPCHandler.h"
+#include "Database/DatabaseEnv.h"
+#include "AuctionHouseObject.h"
+#include "Mail.h"
+#include "Map.h"
+#include "ObjectAccessor.h"
+#include "ObjectDefines.h"
+#include "Policies/Singleton.h"
+#include "Database/SQLStorage.h"
+
+#include <string>
+#include <map>
+
+extern SQLStorage sCreatureStorage;
+extern SQLStorage sCreatureDataAddonStorage;
+extern SQLStorage sCreatureInfoAddonStorage;
+extern SQLStorage sCreatureModelStorage;
+extern SQLStorage sEquipmentStorage;
+extern SQLStorage sGOStorage;
+extern SQLStorage sPageTextStore;
+extern SQLStorage sItemStorage;
+extern SQLStorage sInstanceTemplate;
+
+class Group;
+class Guild;
+class ArenaTeam;
+class Path;
+class TransportPath;
+class Item;
+
+struct ScriptInfo
+{
+ uint32 id;
+ uint32 delay;
+ uint32 command;
+ uint32 datalong;
+ uint32 datalong2;
+ std::string datatext;
+ float x;
+ float y;
+ float z;
+ float o;
+};
+
+typedef std::multimap<uint32, ScriptInfo> ScriptMap;
+typedef std::map<uint32, ScriptMap > ScriptMapMap;
+extern ScriptMapMap sQuestEndScripts;
+extern ScriptMapMap sQuestStartScripts;
+extern ScriptMapMap sSpellScripts;
+extern ScriptMapMap sGameObjectScripts;
+extern ScriptMapMap sEventScripts;
+
+struct AreaTrigger
+{
+ uint8 requiredLevel;
+ uint32 requiredItem;
+ uint32 requiredItem2;
+ uint32 heroicKey;
+ uint32 heroicKey2;
+ uint32 requiredQuest;
+ std::string requiredFailedText;
+ uint32 target_mapId;
+ float target_X;
+ float target_Y;
+ float target_Z;
+ float target_Orientation;
+};
+
+typedef std::set<uint32> CellGuidSet;
+typedef std::map<uint32/*player guid*/,uint32/*instance*/> CellCorpseSet;
+struct CellObjectGuids
+{
+ CellGuidSet creatures;
+ CellGuidSet gameobjects;
+ CellCorpseSet corpses;
+};
+typedef HM_NAMESPACE::hash_map<uint32/*cell_id*/,CellObjectGuids> CellObjectGuidsMap;
+typedef HM_NAMESPACE::hash_map<uint32/*(mapid,spawnMode) pair*/,CellObjectGuidsMap> MapObjectGuids;
+
+typedef HM_NAMESPACE::hash_map<uint64/*(instance,guid) pair*/,time_t> RespawnTimes;
+
+struct MangosStringLocale
+{
+ std::vector<std::string> Content; // 0 -> default, i -> i-1 locale index
+};
+
+typedef HM_NAMESPACE::hash_map<uint32,CreatureData> CreatureDataMap;
+typedef HM_NAMESPACE::hash_map<uint32,GameObjectData> GameObjectDataMap;
+typedef HM_NAMESPACE::hash_map<uint32,CreatureLocale> CreatureLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,GameObjectLocale> GameObjectLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,ItemLocale> ItemLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,QuestLocale> QuestLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,NpcTextLocale> NpcTextLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,PageTextLocale> PageTextLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,MangosStringLocale> MangosStringLocaleMap;
+
+typedef std::multimap<uint32,uint32> QuestRelations;
+
+struct PetLevelInfo
+{
+ PetLevelInfo() : health(0), mana(0) { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
+ uint16 stats[MAX_STATS];
+ uint16 health;
+ uint16 mana;
+ uint16 armor;
+};
+
+struct ReputationOnKillEntry
+{
+ uint32 repfaction1;
+ uint32 repfaction2;
+ bool is_teamaward1;
+ uint32 reputration_max_cap1;
+ int32 repvalue1;
+ bool is_teamaward2;
+ uint32 reputration_max_cap2;
+ int32 repvalue2;
+ bool team_dependent;
+};
+
+struct PetCreateSpellEntry
+{
+ uint32 spellid[4];
+};
+
+#define WEATHER_SEASONS 4
+struct WeatherSeasonChances
+{
+ uint32 rainChance;
+ uint32 snowChance;
+ uint32 stormChance;
+};
+
+struct WeatherZoneChances
+{
+ WeatherSeasonChances data[WEATHER_SEASONS];
+};
+
+struct GraveYardData
+{
+ uint32 safeLocId;
+ uint32 team;
+};
+typedef std::multimap<uint32,GraveYardData> GraveYardMap;
+
+enum ConditionType
+{ // value1 value2 for the Condition enumed
+ CONDITION_NONE = 0, // 0 0
+ CONDITION_AURA = 1, // spell_id effindex
+ CONDITION_ITEM = 2, // item_id count
+ CONDITION_ITEM_EQUIPPED = 3, // item_id 0
+ CONDITION_ZONEID = 4, // zone_id 0
+ CONDITION_REPUTATION_RANK = 5, // faction_id min_rank
+ CONDITION_TEAM = 6, // player_team 0, (469 - Alliance 67 - Horde)
+ 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
+};
+
+#define MAX_CONDITION 11 // maximum value in ConditionType enum
+
+struct PlayerCondition
+{
+ ConditionType condition; // additional condition type
+ uint32 value1; // data for the condition - see ConditionType definition
+ uint32 value2;
+
+ PlayerCondition(uint8 _condition = 0, uint32 _value1 = 0, uint32 _value2 = 0)
+ : condition(ConditionType(_condition)), value1(_value1), value2(_value2) {}
+
+ static bool IsValid(ConditionType condition, uint32 value1, uint32 value2);
+ // Checks correctness of values
+ bool Meets(Player const * APlayer) const; // Checks if the player meets the condition
+ bool operator == (PlayerCondition const& lc) const
+ {
+ return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2);
+ }
+};
+
+enum SkillRangeType
+{
+ SKILL_RANGE_LANGUAGE, // 300..300
+ SKILL_RANGE_LEVEL, // 1..max skill for level
+ SKILL_RANGE_MONO, // 1..1, grey monolite bar
+ SKILL_RANGE_RANK, // 1..skill for known rank
+ SKILL_RANGE_NONE, // 0..0 always
+};
+
+SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial);
+
+#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 )
+
+bool normalizePlayerName(std::string& name);
+
+class PlayerDumpReader;
+
+class ObjectMgr
+{
+ friend class PlayerDumpReader;
+
+ public:
+ ObjectMgr();
+ ~ObjectMgr();
+
+ typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
+
+ typedef std::set< Group * > GroupSet;
+ typedef std::set< Guild * > GuildSet;
+ typedef std::set< ArenaTeam * > ArenaTeamSet;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Quest*> QuestMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, AreaTrigger> AreaTriggerMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, std::string> AreaTriggerScriptMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, ReputationOnKillEntry> RepOnKillMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, WeatherZoneChances> WeatherZoneMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, PetCreateSpellEntry> PetCreateSpellMap;
+
+ Player* GetPlayer(const char* name) const { return ObjectAccessor::Instance().FindPlayerByName(name);}
+ Player* GetPlayer(uint64 guid) const { return ObjectAccessor::FindPlayer(guid); }
+
+ static GameObjectInfo const *GetGameObjectInfo(uint32 id) { return sGOStorage.LookupEntry<GameObjectInfo>(id); }
+
+ void LoadGameobjectInfo();
+ void AddGameobjectInfo(GameObjectInfo *goinfo);
+
+ Group * GetGroupByLeader(const uint64 &guid) const;
+ void AddGroup(Group* group) { mGroupSet.insert( group ); }
+ void RemoveGroup(Group* group) { mGroupSet.erase( group ); }
+
+ Guild* GetGuildByLeader(uint64 const&guid) const;
+ Guild* GetGuildById(const uint32 GuildId) const;
+ Guild* GetGuildByName(std::string guildname) const;
+ std::string GetGuildNameById(const uint32 GuildId) const;
+ void AddGuild(Guild* guild) { mGuildSet.insert( guild ); }
+ void RemoveGuild(Guild* guild) { mGuildSet.erase( guild ); }
+
+ ArenaTeam* GetArenaTeamById(const uint32 ArenaTeamId) const;
+ ArenaTeam* GetArenaTeamByName(std::string ArenaTeamName) const;
+ ArenaTeam* GetArenaTeamByCapitan(uint64 const& guid) const;
+ void AddArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.insert( arenateam ); }
+ void RemoveArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.erase( arenateam ); }
+
+ static CreatureInfo const *GetCreatureTemplate( uint32 id );
+ CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid );
+ CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id);
+ uint32 ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data = NULL);
+ EquipmentInfo const *GetEquipmentInfo( uint32 entry );
+ static CreatureDataAddon const *GetCreatureAddon( uint32 lowguid )
+ {
+ return sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(lowguid);
+ }
+
+ static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry )
+ {
+ return sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(entry);
+ }
+
+ static ItemPrototype const* GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry<ItemPrototype>(id); }
+
+ static InstanceTemplate const* GetInstanceTemplate(uint32 map)
+ {
+ return sInstanceTemplate.LookupEntry<InstanceTemplate>(map);
+ }
+
+ Item* GetAItem(uint32 id)
+ {
+ ItemMap::const_iterator itr = mAitems.find(id);
+ if (itr != mAitems.end())
+ {
+ return itr->second;
+ }
+ return NULL;
+ }
+ void AddAItem(Item* it)
+ {
+ ASSERT( it );
+ ASSERT( mAitems.find(it->GetGUIDLow()) == mAitems.end());
+ mAitems[it->GetGUIDLow()] = it;
+ }
+ bool RemoveAItem(uint32 id)
+ {
+ ItemMap::iterator i = mAitems.find(id);
+ if (i == mAitems.end())
+ {
+ return false;
+ }
+ mAitems.erase(i);
+ return true;
+ }
+ AuctionHouseObject * GetAuctionsMap( uint32 location );
+
+ //auction messages
+ void SendAuctionWonMail( AuctionEntry * auction );
+ void SendAuctionSalePendingMail( AuctionEntry * auction );
+ void SendAuctionSuccessfulMail( AuctionEntry * auction );
+ void SendAuctionExpiredMail( AuctionEntry * auction );
+ static uint32 GetAuctionCut( uint32 location, uint32 highBid );
+ static uint32 GetAuctionDeposit(uint32 location, uint32 time, Item *pItem);
+ static uint32 GetAuctionOutBid(uint32 currentBid);
+
+ PetLevelInfo const* GetPetLevelInfo(uint32 creature_id, uint32 level) const;
+
+ PlayerClassInfo const* GetPlayerClassInfo(uint32 class_) const
+ {
+ if(class_ >= MAX_CLASSES) return NULL;
+ return &playerClassInfo[class_];
+ }
+ void GetPlayerClassLevelInfo(uint32 class_,uint32 level, PlayerClassLevelInfo* info) const;
+
+ PlayerInfo const* GetPlayerInfo(uint32 race, uint32 class_) const
+ {
+ if(race >= MAX_RACES) return NULL;
+ if(class_ >= MAX_CLASSES) return NULL;
+ PlayerInfo const* info = &playerInfo[race][class_];
+ if(info->displayId_m==0 || info->displayId_f==0) return NULL;
+ return info;
+ }
+ void GetPlayerLevelInfo(uint32 race, uint32 class_,uint32 level, PlayerLevelInfo* info) const;
+
+ uint64 GetPlayerGUIDByName(std::string name) const;
+ bool GetPlayerNameByGUID(const uint64 &guid, std::string &name) const;
+ uint32 GetPlayerTeamByGUID(const uint64 &guid) const;
+ uint32 GetPlayerAccountIdByGUID(const uint64 &guid) const;
+ uint32 GetSecurityByAccount(uint32 acc_id) const;
+ bool GetAccountNameByAccount(uint32 acc_id, std::string &name) const;
+ uint32 GetAccountByAccountName(std::string name) const;
+
+ uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid );
+ void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost);
+ uint16 GetTaxiMount( uint32 id, uint32 team );
+ void GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds );
+ void GetTransportPathNodes( uint32 path, TransportPath &pathnodes );
+
+ Quest const* GetQuestTemplate(uint32 quest_id) const
+ {
+ QuestMap::const_iterator itr = mQuestTemplates.find(quest_id);
+ return itr != mQuestTemplates.end() ? itr->second : NULL;
+ }
+ QuestMap const& GetQuestTemplates() const { return mQuestTemplates; }
+
+ uint32 GetQuestForAreaTrigger(uint32 Trigger_ID) const
+ {
+ QuestAreaTriggerMap::const_iterator itr = mQuestAreaTriggerMap.find(Trigger_ID);
+ if(itr != mQuestAreaTriggerMap.end())
+ return itr->second;
+ return 0;
+ }
+ bool IsTavernAreaTrigger(uint32 Trigger_ID) const { return mTavernAreaTriggerSet.count(Trigger_ID) != 0; }
+ bool IsGameObjectForQuests(uint32 entry) const { return mGameObjectForQuestSet.count(entry) != 0; }
+ bool IsGuildVaultGameObject(Player *player, uint64 guid) const
+ {
+ if(GameObject *go = ObjectAccessor::GetGameObject(*player, guid))
+ if(go->GetGoType() == GAMEOBJECT_TYPE_GUILD_BANK)
+ return true;
+ return false;
+ }
+
+ uint32 GetBattleMasterBG(uint32 entry) const
+ {
+ BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry);
+ if(itr != mBattleMastersMap.end())
+ return itr->second;
+ return 2; //BATTLEGROUND_WS - i will not add include only for constant usage!
+ }
+
+ void AddGossipText(GossipText *pGText);
+ GossipText *GetGossipText(uint32 Text_ID);
+
+ WorldSafeLocsEntry const *GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team);
+ bool AddGraveYardLink(uint32 id, uint32 zone, uint32 team, bool inDB = true);
+ void LoadGraveyardZones();
+ GraveYardData const* FindGraveYardData(uint32 id, uint32 zone);
+
+ AreaTrigger const* GetAreaTrigger(uint32 trigger) const
+ {
+ AreaTriggerMap::const_iterator itr = mAreaTriggers.find( trigger );
+ if( itr != mAreaTriggers.end( ) )
+ return &itr->second;
+ return NULL;
+ }
+
+ AreaTrigger const* GetGoBackTrigger(uint32 Map) const;
+
+ const char* GetAreaTriggerScriptName(uint32 id);
+
+ ReputationOnKillEntry const* GetReputationOnKilEntry(uint32 id) const
+ {
+ RepOnKillMap::const_iterator itr = mRepOnKill.find(id);
+ if(itr != mRepOnKill.end())
+ return &itr->second;
+ return NULL;
+ }
+
+ PetCreateSpellEntry const* GetPetCreateSpellEntry(uint32 id) const
+ {
+ PetCreateSpellMap::const_iterator itr = mPetCreateSpell.find(id);
+ if(itr != mPetCreateSpell.end())
+ return &itr->second;
+ return NULL;
+ }
+
+ void LoadGuilds();
+ void LoadArenaTeams();
+ void LoadGroups();
+ void LoadQuests();
+ void LoadQuestRelations()
+ {
+ LoadGameobjectQuestRelations();
+ LoadGameobjectInvolvedRelations();
+ LoadCreatureQuestRelations();
+ LoadCreatureInvolvedRelations();
+ }
+ void LoadGameobjectQuestRelations();
+ void LoadGameobjectInvolvedRelations();
+ void LoadCreatureQuestRelations();
+ void LoadCreatureInvolvedRelations();
+
+ QuestRelations mGOQuestRelations;
+ QuestRelations mGOQuestInvolvedRelations;
+ QuestRelations mCreatureQuestRelations;
+ QuestRelations mCreatureQuestInvolvedRelations;
+
+ void LoadGameObjectScripts();
+ void LoadQuestEndScripts();
+ void LoadQuestStartScripts();
+ void LoadEventScripts();
+ void LoadSpellScripts();
+
+ bool LoadMangosStrings(DatabaseType& db, char const* table, bool positive_entries);
+ bool LoadMangosStrings() { return LoadMangosStrings(WorldDatabase,"mangos_string",true); }
+ void LoadPetCreateSpells();
+ void LoadCreatureLocales();
+ void LoadCreatureTemplates();
+ void LoadCreatures();
+ void LoadCreatureRespawnTimes();
+ void LoadCreatureAddons();
+ void LoadCreatureModelInfo();
+ void LoadEquipmentTemplates();
+ void LoadGameObjectLocales();
+ void LoadGameobjects();
+ void LoadGameobjectRespawnTimes();
+ void LoadItemPrototypes();
+ void LoadItemLocales();
+ void LoadQuestLocales();
+ void LoadNpcTextLocales();
+ void LoadPageTextLocales();
+ void LoadInstanceTemplate();
+
+ void LoadGossipText();
+
+ void LoadAreaTriggerTeleports();
+ void LoadQuestAreaTriggers();
+ void LoadAreaTriggerScripts();
+ void LoadTavernAreaTriggers();
+ void LoadBattleMastersEntry();
+ void LoadGameObjectForQuests();
+
+ void LoadItemTexts();
+ void LoadPageTexts();
+
+ //load first auction items, because of check if item exists, when loading
+ void LoadAuctionItems();
+ void LoadAuctions();
+ void LoadPlayerInfo();
+ void LoadPetLevelInfo();
+ void LoadExplorationBaseXP();
+ void LoadPetNames();
+ void LoadPetNumber();
+ void LoadCorpses();
+ void LoadFishingBaseSkillLevel();
+
+ void LoadReputationOnKill();
+
+ void LoadWeatherZoneChances();
+
+ std::string GeneratePetName(uint32 entry);
+ uint32 GetBaseXP(uint32 level);
+
+ int32 GetFishingBaseSkillLevel(uint32 entry) const
+ {
+ FishingBaseSkillMap::const_iterator itr = mFishingBaseForArea.find(entry);
+ return itr != mFishingBaseForArea.end() ? itr->second : 0;
+ }
+
+ void ReturnOrDeleteOldMails(bool serverUp);
+
+ void SetHighestGuids();
+ uint32 GenerateLowGuid(HighGuid guidhigh);
+ uint32 GenerateAuctionID();
+ uint32 GenerateMailID();
+ uint32 GenerateItemTextID();
+ uint32 GeneratePetNumber();
+
+ uint32 CreateItemText(std::string text);
+ std::string GetItemText( uint32 id )
+ {
+ ItemTextMap::const_iterator itr = mItemTexts.find( id );
+ if ( itr != mItemTexts.end() )
+ return itr->second;
+ else
+ return "There is no info for this item";
+ }
+
+ typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
+ ExclusiveQuestGroups mExclusiveQuestGroups;
+
+ WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
+ {
+ WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
+ if(itr != mWeatherZoneMap.end())
+ return &itr->second;
+ else
+ return NULL;
+ }
+
+ CellObjectGuids const& GetCellObjectGuids(uint16 mapid, uint8 spawnMode, uint32 cell_id)
+ {
+ return mMapObjectGuids[MAKE_PAIR32(mapid,spawnMode)][cell_id];
+ }
+
+ CreatureData const* GetCreatureData(uint32 guid) const
+ {
+ CreatureDataMap::const_iterator itr = mCreatureDataMap.find(guid);
+ if(itr==mCreatureDataMap.end()) return NULL;
+ return &itr->second;
+ }
+ CreatureData& NewOrExistCreatureData(uint32 guid) { return mCreatureDataMap[guid]; }
+ void DeleteCreatureData(uint32 guid);
+ CreatureLocale const* GetCreatureLocale(uint32 entry) const
+ {
+ CreatureLocaleMap::const_iterator itr = mCreatureLocaleMap.find(entry);
+ if(itr==mCreatureLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ GameObjectLocale const* GetGameObjectLocale(uint32 entry) const
+ {
+ GameObjectLocaleMap::const_iterator itr = mGameObjectLocaleMap.find(entry);
+ if(itr==mGameObjectLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ ItemLocale const* GetItemLocale(uint32 entry) const
+ {
+ ItemLocaleMap::const_iterator itr = mItemLocaleMap.find(entry);
+ if(itr==mItemLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ QuestLocale const* GetQuestLocale(uint32 entry) const
+ {
+ QuestLocaleMap::const_iterator itr = mQuestLocaleMap.find(entry);
+ if(itr==mQuestLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ NpcTextLocale const* GetNpcTextLocale(uint32 entry) const
+ {
+ NpcTextLocaleMap::const_iterator itr = mNpcTextLocaleMap.find(entry);
+ if(itr==mNpcTextLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ PageTextLocale const* GetPageTextLocale(uint32 entry) const
+ {
+ PageTextLocaleMap::const_iterator itr = mPageTextLocaleMap.find(entry);
+ if(itr==mPageTextLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+
+ GameObjectData const* GetGOData(uint32 guid) const
+ {
+ GameObjectDataMap::const_iterator itr = mGameObjectDataMap.find(guid);
+ if(itr==mGameObjectDataMap.end()) return NULL;
+ return &itr->second;
+ }
+ GameObjectData& NewGOData(uint32 guid) { return mGameObjectDataMap[guid]; }
+ void DeleteGOData(uint32 guid);
+
+ MangosStringLocale const* GetMangosStringLocale(int32 entry) const
+ {
+ MangosStringLocaleMap::const_iterator itr = mMangosStringLocaleMap.find(entry);
+ if(itr==mMangosStringLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ const char *GetMangosString(int32 entry, int locale_idx) const;
+ const char *GetMangosStringForDBCLocale(int32 entry) const { return GetMangosString(entry,DBCLocaleIndex); }
+ void SetDBCLocaleIndex(uint32 lang) { DBCLocaleIndex = GetIndexForLocale(LocaleConstant(lang)); }
+
+ void AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance);
+ void DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid);
+
+ time_t GetCreatureRespawnTime(uint32 loguid, uint32 instance) { return mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)]; }
+ void SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t);
+ time_t GetGORespawnTime(uint32 loguid, uint32 instance) { return mGORespawnTimes[MAKE_PAIR64(loguid,instance)]; }
+ void SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t);
+ void DeleteRespawnTimeForInstance(uint32 instance);
+
+ // grid objects
+ void AddCreatureToGrid(uint32 guid, CreatureData const* data);
+ void RemoveCreatureFromGrid(uint32 guid, CreatureData const* data);
+ void AddGameobjectToGrid(uint32 guid, GameObjectData const* data);
+ void RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data);
+
+ // reserved names
+ void LoadReservedPlayersNames();
+ bool IsReservedName(std::string name) const
+ {
+ return m_ReservedNames.find(name) != m_ReservedNames.end();
+ }
+
+ // name with valid structure and symbols
+ static bool IsValidName( std::string name, bool create = false );
+ static bool IsValidCharterName( std::string name );
+ static bool IsValidPetName( std::string name );
+
+ static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names);
+
+ int GetIndexForLocale(LocaleConstant loc);
+ LocaleConstant GetLocaleForIndex(int i);
+ // guild bank tabs
+ const uint32 GetGuildBankTabPrice(uint8 Index) { return Index < GUILD_BANK_MAX_TABS ? mGuildBankTabPrice[Index] : 0; }
+
+ uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2);
+ bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const
+ {
+ if(condition_id >= mConditions.size())
+ return false;
+
+ return mConditions[condition_id].Meets(player);
+ }
+ protected:
+ uint32 m_auctionid;
+ uint32 m_mailid;
+ uint32 m_ItemTextId;
+
+ uint32 m_hiCharGuid;
+ uint32 m_hiCreatureGuid;
+ uint32 m_hiPetGuid;
+ uint32 m_hiItemGuid;
+ uint32 m_hiGoGuid;
+ uint32 m_hiDoGuid;
+ uint32 m_hiCorpseGuid;
+
+ uint32 m_hiPetNumber;
+
+ QuestMap mQuestTemplates;
+
+ typedef HM_NAMESPACE::hash_map<uint32, GossipText*> GossipTextMap;
+ typedef HM_NAMESPACE::hash_map<uint32, uint32> QuestAreaTriggerMap;
+ typedef HM_NAMESPACE::hash_map<uint32, uint32> BattleMastersMap;
+ typedef HM_NAMESPACE::hash_map<uint32, std::string> ItemTextMap;
+ typedef std::set<uint32> TavernAreaTriggerSet;
+ typedef std::set<uint32> GameObjectForQuestSet;
+
+ GroupSet mGroupSet;
+ GuildSet mGuildSet;
+ ArenaTeamSet mArenaTeamSet;
+
+ ItemMap mItems;
+ ItemMap mAitems;
+
+ ItemTextMap mItemTexts;
+
+ AuctionHouseObject mHordeAuctions;
+ AuctionHouseObject mAllianceAuctions;
+ AuctionHouseObject mNeutralAuctions;
+
+ QuestAreaTriggerMap mQuestAreaTriggerMap;
+ BattleMastersMap mBattleMastersMap;
+ TavernAreaTriggerSet mTavernAreaTriggerSet;
+ GameObjectForQuestSet mGameObjectForQuestSet;
+ GossipTextMap mGossipText;
+ AreaTriggerMap mAreaTriggers;
+ AreaTriggerScriptMap mAreaTriggerScripts;
+
+ RepOnKillMap mRepOnKill;
+
+ WeatherZoneMap mWeatherZoneMap;
+
+ PetCreateSpellMap mPetCreateSpell;
+
+ //character reserved names
+ typedef std::set<std::string> ReservedNamesMap;
+ ReservedNamesMap m_ReservedNames;
+
+ GraveYardMap mGraveYardMap;
+
+ typedef std::vector<LocaleConstant> LocalForIndex;
+ LocalForIndex m_LocalForIndex;
+ int GetOrNewIndexForLocale(LocaleConstant loc);
+
+ int DBCLocaleIndex;
+ private:
+ void LoadScripts(ScriptMapMap& scripts, char const* tablename);
+ void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
+ void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
+
+ typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
+ // PetLevelInfoMap[creature_id][level]
+ PetLevelInfoMap petInfo; // [creature_id][level]
+
+ PlayerClassInfo playerClassInfo[MAX_CLASSES];
+
+ void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const;
+ PlayerInfo playerInfo[MAX_RACES][MAX_CLASSES];
+
+ typedef std::map<uint32,uint32> BaseXPMap; // [area level][base xp]
+ BaseXPMap mBaseXPTable;
+
+ typedef std::map<uint32,int32> FishingBaseSkillMap; // [areaId][base skill level]
+ FishingBaseSkillMap mFishingBaseForArea;
+
+ typedef std::map<uint32,std::vector<std::string> > HalfNameMap;
+ HalfNameMap PetHalfName0;
+ HalfNameMap PetHalfName1;
+
+ MapObjectGuids mMapObjectGuids;
+ CreatureDataMap mCreatureDataMap;
+ CreatureLocaleMap mCreatureLocaleMap;
+ GameObjectDataMap mGameObjectDataMap;
+ GameObjectLocaleMap mGameObjectLocaleMap;
+ ItemLocaleMap mItemLocaleMap;
+ QuestLocaleMap mQuestLocaleMap;
+ NpcTextLocaleMap mNpcTextLocaleMap;
+ PageTextLocaleMap mPageTextLocaleMap;
+ MangosStringLocaleMap mMangosStringLocaleMap;
+ RespawnTimes mCreatureRespawnTimes;
+ RespawnTimes mGORespawnTimes;
+
+ typedef std::vector<uint32> GuildBankTabPriceMap;
+ GuildBankTabPriceMap mGuildBankTabPrice;
+
+ // Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required)
+ typedef std::vector<PlayerCondition> ConditionStore;
+ ConditionStore mConditions;
+
+};
+
+#define objmgr MaNGOS::Singleton<ObjectMgr>::Instance()
+#endif
+
+// scripting access functions
+bool MANGOS_DLL_SPEC LoadMangosStrings(DatabaseType& db, char const* table);
+MANGOS_DLL_SPEC const char* GetAreaTriggerScriptNameById(uint32 id);
diff --git a/src/game/ObjectPosSelector.cpp b/src/game/ObjectPosSelector.cpp
new file mode 100644
index 00000000000..5936152f9be
--- /dev/null
+++ b/src/game/ObjectPosSelector.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ObjectPosSelector.h"
+
+ObjectPosSelector::ObjectPosSelector(float x,float y,float size,float dist)
+: m_center_x(x),m_center_y(y),m_size(size),m_dist(dist)
+{
+ m_anglestep = acos(m_dist/(m_dist+2*m_size));
+
+ m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].end();
+ m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].end();
+
+ m_smallStepAngle[USED_POS_PLUS] = 0;
+ m_smallStepAngle[USED_POS_MINUS] = 0;
+
+ m_smallStepOk[USED_POS_PLUS] = false;
+ m_smallStepOk[USED_POS_MINUS] = false;
+
+ m_smallStepNextUsedPos[USED_POS_PLUS] = NULL;
+ m_smallStepNextUsedPos[USED_POS_MINUS] = NULL;
+}
+
+ObjectPosSelector::UsedPosList::value_type const* ObjectPosSelector::nextUsedPos(UsedPosType uptype)
+{
+ UsedPosList::const_iterator itr = m_nextUsedPos[uptype];
+ if(itr!=m_UsedPosLists[uptype].end())
+ ++itr;
+
+ if(itr==m_UsedPosLists[uptype].end())
+ {
+ if(!m_UsedPosLists[~uptype].empty())
+ return &*m_UsedPosLists[~uptype].rbegin();
+ else
+ return NULL;
+ }
+ else
+ return &*itr;
+}
+
+void ObjectPosSelector::AddUsedPos(float size,float angle,float dist)
+{
+ if(angle>=0)
+ m_UsedPosLists[USED_POS_PLUS].insert(UsedPosList::value_type(angle,UsedPos(1.0,size,dist)));
+ else
+ m_UsedPosLists[USED_POS_MINUS].insert(UsedPosList::value_type(-angle,UsedPos(-1.0,size,dist)));
+}
+
+void ObjectPosSelector::InitializeAngle()
+{
+ m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].begin();
+ m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].begin();
+
+ m_smallStepAngle[USED_POS_PLUS] = 0;
+ m_smallStepAngle[USED_POS_MINUS] = 0;
+
+ m_smallStepOk[USED_POS_PLUS] = true;
+ m_smallStepOk[USED_POS_MINUS] = true;
+}
+
+bool ObjectPosSelector::FirstAngle(float& angle)
+{
+ if(m_UsedPosLists[USED_POS_PLUS].empty() && !m_UsedPosLists[USED_POS_MINUS].empty() )
+ return NextAngleFor(*m_UsedPosLists[USED_POS_MINUS].begin(),1.0,USED_POS_PLUS,angle);
+ else if(m_UsedPosLists[USED_POS_MINUS].empty() && !m_UsedPosLists[USED_POS_PLUS].empty() )
+ return NextAngleFor(*m_UsedPosLists[USED_POS_PLUS].begin(),-1.0,USED_POS_MINUS,angle);
+
+ return false;
+}
+
+bool ObjectPosSelector::NextAngle(float& angle)
+{
+ while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() ||
+ m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() ||
+ m_smallStepOk[USED_POS_PLUS] || m_smallStepOk[USED_POS_MINUS] )
+ {
+ // calculate next possible angle
+ if(NextPosibleAngle(angle))
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjectPosSelector::NextUsedAngle(float& angle)
+{
+ while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() ||
+ m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() )
+ {
+ // calculate next possible angle
+ if(!NextPosibleAngle(angle))
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjectPosSelector::NextPosibleAngle( float& angle )
+{
+ // ++ direction less updated
+ if( m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() &&
+ (m_nextUsedPos[USED_POS_MINUS]==m_UsedPosLists[USED_POS_MINUS].end() || m_nextUsedPos[USED_POS_PLUS]->first <= m_nextUsedPos[USED_POS_MINUS]->first) )
+ {
+ bool ok;
+ if(m_smallStepOk[USED_POS_PLUS])
+ ok = NextSmallStepAngle(1.0,USED_POS_PLUS,angle);
+ else
+ ok = NextAngleFor(*m_nextUsedPos[USED_POS_PLUS],1.0,USED_POS_PLUS,angle);
+
+ if(!ok)
+ ++m_nextUsedPos[USED_POS_PLUS]; // increase. only at fail (original or checked)
+ return ok;
+ }
+ // -- direction less updated
+ else if( m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end())
+ {
+ bool ok;
+ if(m_smallStepOk[USED_POS_MINUS])
+ ok = NextSmallStepAngle(-1.0,USED_POS_MINUS,angle);
+ else
+ ok = NextAngleFor(*m_nextUsedPos[USED_POS_MINUS],-1.0,USED_POS_MINUS,angle);
+
+ if(!ok)
+ ++m_nextUsedPos[USED_POS_MINUS];
+ return ok;
+ }
+ else // both list empty
+ {
+ if( m_smallStepOk[USED_POS_PLUS] && (!m_smallStepOk[USED_POS_MINUS] || m_smallStepAngle[USED_POS_PLUS] <= m_smallStepAngle[USED_POS_MINUS]) )
+ {
+ return NextSmallStepAngle(1.0,USED_POS_PLUS,angle);
+ }
+ // -- direction less updated
+ else if( m_smallStepOk[USED_POS_MINUS] )
+ {
+ return NextSmallStepAngle(-1.0,USED_POS_MINUS,angle);
+ }
+ }
+
+ // no angles
+ return false;
+}
diff --git a/src/game/ObjectPosSelector.h b/src/game/ObjectPosSelector.h
new file mode 100644
index 00000000000..7423c0a741c
--- /dev/null
+++ b/src/game/ObjectPosSelector.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _OBJECT_POS_SELECTOR_H
+#define _OBJECT_POS_SELECTOR_H
+
+#include<Common.h>
+
+#include<map>
+
+enum UsedPosType { USED_POS_PLUS, USED_POS_MINUS };
+
+inline UsedPosType operator ~(UsedPosType uptype)
+{
+ return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS;
+}
+
+struct ObjectPosSelector
+{
+ struct UsedPos
+ {
+ UsedPos(float sign_, float size_,float dist_) : sign(sign_), size(size_),dist(dist_) {}
+
+ float sign;
+
+ float size; // size of point
+ float dist; // dist to central point (including central point size)
+ };
+
+ typedef std::multimap<float,UsedPos> UsedPosList; // abs(angle)->Node
+
+ ObjectPosSelector(float x,float y,float size,float dist);
+
+ void AddUsedPos(float size,float angle,float dist);
+ void InitializeAngle();
+
+ bool FirstAngle(float& angle);
+ bool NextAngle(float& angle);
+ bool NextUsedAngle(float& angle);
+
+ bool NextPosibleAngle( float& angle );
+
+ bool CheckAngle(UsedPosList::value_type const& nextUsedPos, float sign, float angle ) const
+ {
+ float angle_step2 = GetAngle(nextUsedPos.second);
+
+ float next_angle = nextUsedPos.first;
+ if(nextUsedPos.second.sign * sign < 0) // last node from diff. list (-pi+alpha)
+ next_angle = 2*M_PI-next_angle; // move to positive
+
+ return fabs(angle)+angle_step2 <= next_angle;
+ }
+
+ bool CheckOriginal() const
+ {
+ return (m_UsedPosLists[USED_POS_PLUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_PLUS].begin(),1.0,0)) &&
+ (m_UsedPosLists[USED_POS_MINUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_MINUS].begin(),-1.0,0));
+ }
+
+ bool IsNonBalanced() const { return m_UsedPosLists[USED_POS_PLUS].empty() != m_UsedPosLists[USED_POS_MINUS].empty(); }
+
+ bool NextAngleFor( UsedPosList::value_type const& usedPos, float sign, UsedPosType uptype, float &angle )
+ {
+ float angle_step = GetAngle(usedPos.second);
+
+ // next possible angle
+ angle = usedPos.first * usedPos.second.sign + angle_step * sign;
+
+ UsedPosList::value_type const* nextNode = nextUsedPos(uptype);
+ if(nextNode)
+ {
+ // if next node permit use selected angle, then do it
+ if(!CheckAngle(*nextNode, sign, angle))
+ {
+ m_smallStepOk[uptype] = false;
+ return false;
+ }
+ }
+
+ // possible more points
+ m_smallStepOk[uptype] = true;
+ m_smallStepAngle[uptype] = angle;
+ m_smallStepNextUsedPos[uptype] = nextNode;
+
+ return true;
+ }
+
+ bool NextSmallStepAngle( float sign, UsedPosType uptype, float &angle )
+ {
+ // next possible angle
+ angle = m_smallStepAngle[uptype] + m_anglestep * sign;
+
+ if(fabs(angle) > M_PI)
+ {
+ m_smallStepOk[uptype] = false;
+ return false;
+ }
+
+ if(m_smallStepNextUsedPos[uptype])
+ {
+ if(fabs(angle) >= m_smallStepNextUsedPos[uptype]->first)
+ {
+ m_smallStepOk[uptype] = false;
+ return false;
+ }
+
+ // if next node permit use selected angle, then do it
+ if(!CheckAngle(*m_smallStepNextUsedPos[uptype], sign, angle))
+ {
+ m_smallStepOk[uptype] = false;
+ return false;
+ }
+ }
+
+ // possible more points
+ m_smallStepAngle[uptype] = angle;
+ return true;
+ }
+
+ // next used post for m_nextUsedPos[uptype]
+ UsedPosList::value_type const* nextUsedPos(UsedPosType uptype);
+
+ // angle from used pos to next possible free pos
+ float GetAngle(UsedPos const& usedPos) const { return acos(m_dist/(usedPos.dist+usedPos.size+m_size)); }
+
+ float m_center_x;
+ float m_center_y;
+ float m_size; // size of object in center
+ float m_dist; // distance for searching pos (including central object size)
+ float m_anglestep;
+
+ UsedPosList m_UsedPosLists[2];
+ UsedPosList::const_iterator m_nextUsedPos[2];
+
+ // field for small step from first after next used pos until next pos
+ float m_smallStepAngle[2];
+ bool m_smallStepOk[2];
+ UsedPosList::value_type const* m_smallStepNextUsedPos[2];
+};
+#endif
diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp
new file mode 100644
index 00000000000..ed37eca8759
--- /dev/null
+++ b/src/game/Opcodes.cpp
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "Opcodes.h"
+#include "WorldSession.h"
+
+/// Correspondence between opcodes and their names
+OpcodeHandler opcodeTable[NUM_MSG_TYPES] =
+{
+ /*0x000*/ { "MSG_NULL_ACTION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x001*/ { "CMSG_BOOTME", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x002*/ { "CMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x003*/ { "SMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x004*/ { "CMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x005*/ { "SMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x006*/ { "CMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x007*/ { "SMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x008*/ { "CMSG_WORLD_TELEPORT", STATUS_LOGGEDIN, &WorldSession::HandleWorldTeleportOpcode },
+ /*0x009*/ { "CMSG_TELEPORT_TO_UNIT", STATUS_LOGGEDIN, &WorldSession::Handle_NULL },
+ /*0x00A*/ { "CMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x00B*/ { "SMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x00C*/ { "CMSG_DEBUG_CHANGECELLZONE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x00D*/ { "CMSG_EMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x00E*/ { "CMSG_UNEMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x00F*/ { "CMSG_RECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x010*/ { "CMSG_LEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x011*/ { "CMSG_CREATEMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x012*/ { "CMSG_DESTROYMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x013*/ { "CMSG_CREATEITEM", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x014*/ { "CMSG_CREATEGAMEOBJECT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x015*/ { "SMSG_CHECK_FOR_BOTS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x016*/ { "CMSG_MAKEMONSTERATTACKGUID", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x017*/ { "CMSG_BOT_DETECTED2", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x018*/ { "CMSG_FORCEACTION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x019*/ { "CMSG_FORCEACTIONONOTHER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x01A*/ { "CMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x01B*/ { "SMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x01C*/ { "CMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x01D*/ { "SMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x01E*/ { "SMSG_DEBUGINFOSPELLMISS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x01F*/ { "CMSG_WEATHER_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x020*/ { "CMSG_UNDRESSPLAYER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x021*/ { "CMSG_BEASTMASTER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x022*/ { "CMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x023*/ { "SMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x024*/ { "CMSG_CHEAT_SETMONEY", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x025*/ { "CMSG_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x026*/ { "CMSG_PET_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x027*/ { "CMSG_SET_WORLDSTATE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x028*/ { "CMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x029*/ { "CMSG_USE_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02A*/ { "CMSG_FLAG_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02B*/ { "CMSG_FLAG_QUEST_FINISH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02C*/ { "CMSG_CLEAR_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02D*/ { "CMSG_SEND_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02E*/ { "CMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x02F*/ { "SMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x030*/ { "CMSG_DISABLE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x031*/ { "CMSG_ADVANCE_SPAWN_TIME", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x032*/ { "CMSG_PVP_PORT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x033*/ { "CMSG_AUTH_SRP6_BEGIN", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x034*/ { "CMSG_AUTH_SRP6_PROOF", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x035*/ { "CMSG_AUTH_SRP6_RECODE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x036*/ { "CMSG_CHAR_CREATE", STATUS_AUTHED, &WorldSession::HandleCharCreateOpcode },
+ /*0x037*/ { "CMSG_CHAR_ENUM", STATUS_AUTHED, &WorldSession::HandleCharEnumOpcode },
+ /*0x038*/ { "CMSG_CHAR_DELETE", STATUS_AUTHED, &WorldSession::HandleCharDeleteOpcode },
+ /*0x039*/ { "SMSG_AUTH_SRP6_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x03A*/ { "SMSG_CHAR_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x03B*/ { "SMSG_CHAR_ENUM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x03C*/ { "SMSG_CHAR_DELETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x03D*/ { "CMSG_PLAYER_LOGIN", STATUS_AUTHED, &WorldSession::HandlePlayerLoginOpcode },
+ /*0x03E*/ { "SMSG_NEW_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x03F*/ { "SMSG_TRANSFER_PENDING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x040*/ { "SMSG_TRANSFER_ABORTED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x041*/ { "SMSG_CHARACTER_LOGIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x042*/ { "SMSG_LOGIN_SETTIMESPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x043*/ { "SMSG_GAMETIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x044*/ { "CMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x045*/ { "SMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x046*/ { "CMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x047*/ { "SMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x048*/ { "CMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x049*/ { "SMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x04A*/ { "CMSG_PLAYER_LOGOUT", STATUS_LOGGEDIN, &WorldSession::HandlePlayerLogoutOpcode },
+ /*0x04B*/ { "CMSG_LOGOUT_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleLogoutRequestOpcode },
+ /*0x04C*/ { "SMSG_LOGOUT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x04D*/ { "SMSG_LOGOUT_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x04E*/ { "CMSG_LOGOUT_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleLogoutCancelOpcode },
+ /*0x04F*/ { "SMSG_LOGOUT_CANCEL_ACK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x050*/ { "CMSG_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNameQueryOpcode },
+ /*0x051*/ { "SMSG_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x052*/ { "CMSG_PET_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetNameQuery },
+ /*0x053*/ { "SMSG_PET_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x054*/ { "CMSG_GUILD_QUERY", STATUS_AUTHED, &WorldSession::HandleGuildQueryOpcode },
+ /*0x055*/ { "SMSG_GUILD_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x056*/ { "CMSG_ITEM_QUERY_SINGLE", STATUS_LOGGEDIN, &WorldSession::HandleItemQuerySingleOpcode },
+ /*0x057*/ { "CMSG_ITEM_QUERY_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x058*/ { "SMSG_ITEM_QUERY_SINGLE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x059*/ { "SMSG_ITEM_QUERY_MULTIPLE_RESPONSE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x05A*/ { "CMSG_PAGE_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePageQueryOpcode },
+ /*0x05B*/ { "SMSG_PAGE_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x05C*/ { "CMSG_QUEST_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestQueryOpcode },
+ /*0x05D*/ { "SMSG_QUEST_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x05E*/ { "CMSG_GAMEOBJECT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectQueryOpcode },
+ /*0x05F*/ { "SMSG_GAMEOBJECT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x060*/ { "CMSG_CREATURE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCreatureQueryOpcode },
+ /*0x061*/ { "SMSG_CREATURE_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x062*/ { "CMSG_WHO", STATUS_LOGGEDIN, &WorldSession::HandleWhoOpcode },
+ /*0x063*/ { "SMSG_WHO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x064*/ { "CMSG_WHOIS", STATUS_LOGGEDIN, &WorldSession::HandleWhoisOpcode },
+ /*0x065*/ { "SMSG_WHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x066*/ { "CMSG_CONTACT_LIST", STATUS_LOGGEDIN, &WorldSession::HandleFriendListOpcode },
+ /*0x067*/ { "SMSG_CONTACT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x068*/ { "SMSG_FRIEND_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x069*/ { "CMSG_ADD_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleAddFriendOpcode },
+ /*0x06A*/ { "CMSG_DEL_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleDelFriendOpcode },
+ /*0x06B*/ { "CMSG_SET_CONTACT_NOTES", STATUS_LOGGEDIN, &WorldSession::HandleSetFriendNoteOpcode },
+ /*0x06C*/ { "CMSG_ADD_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleAddIgnoreOpcode },
+ /*0x06D*/ { "CMSG_DEL_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleDelIgnoreOpcode },
+ /*0x06E*/ { "CMSG_GROUP_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupInviteOpcode },
+ /*0x06F*/ { "SMSG_GROUP_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x070*/ { "CMSG_GROUP_CANCEL", STATUS_LOGGEDIN, &WorldSession::Handle_Depricated },
+ /*0x071*/ { "SMSG_GROUP_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x072*/ { "CMSG_GROUP_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGroupAcceptOpcode },
+ /*0x073*/ { "CMSG_GROUP_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGroupDeclineOpcode },
+ /*0x074*/ { "SMSG_GROUP_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x075*/ { "CMSG_GROUP_UNINVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteNameOpcode },
+ /*0x076*/ { "CMSG_GROUP_UNINVITE_GUID", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteGuidOpcode },
+ /*0x077*/ { "SMSG_GROUP_UNINVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x078*/ { "CMSG_GROUP_SET_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupSetLeaderOpcode },
+ /*0x079*/ { "SMSG_GROUP_SET_LEADER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x07A*/ { "CMSG_LOOT_METHOD", STATUS_LOGGEDIN, &WorldSession::HandleLootMethodOpcode },
+ /*0x07B*/ { "CMSG_GROUP_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGroupLeaveOpcode },
+ /*0x07C*/ { "SMSG_GROUP_DESTROYED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x07D*/ { "SMSG_GROUP_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x07E*/ { "SMSG_PARTY_MEMBER_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x07F*/ { "SMSG_PARTY_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x080*/ { "UMSG_UPDATE_GROUP_MEMBERS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x081*/ { "CMSG_GUILD_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildCreateOpcode },
+ /*0x082*/ { "CMSG_GUILD_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGuildInviteOpcode },
+ /*0x083*/ { "SMSG_GUILD_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x084*/ { "CMSG_GUILD_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGuildAcceptOpcode },
+ /*0x085*/ { "CMSG_GUILD_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDeclineOpcode },
+ /*0x086*/ { "SMSG_GUILD_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x087*/ { "CMSG_GUILD_INFO", STATUS_LOGGEDIN, &WorldSession::HandleGuildInfoOpcode },
+ /*0x088*/ { "SMSG_GUILD_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x089*/ { "CMSG_GUILD_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleGuildRosterOpcode },
+ /*0x08A*/ { "SMSG_GUILD_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x08B*/ { "CMSG_GUILD_PROMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildPromoteOpcode },
+ /*0x08C*/ { "CMSG_GUILD_DEMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDemoteOpcode },
+ /*0x08D*/ { "CMSG_GUILD_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaveOpcode },
+ /*0x08E*/ { "CMSG_GUILD_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildRemoveOpcode },
+ /*0x08F*/ { "CMSG_GUILD_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGuildDisbandOpcode },
+ /*0x090*/ { "CMSG_GUILD_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaderOpcode },
+ /*0x091*/ { "CMSG_GUILD_MOTD", STATUS_LOGGEDIN, &WorldSession::HandleGuildMOTDOpcode },
+ /*0x092*/ { "SMSG_GUILD_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x093*/ { "SMSG_GUILD_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x094*/ { "UMSG_UPDATE_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x095*/ { "CMSG_MESSAGECHAT", STATUS_LOGGEDIN, &WorldSession::HandleMessagechatOpcode },
+ /*0x096*/ { "SMSG_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x097*/ { "CMSG_JOIN_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoin },
+ /*0x098*/ { "CMSG_LEAVE_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelLeave },
+ /*0x099*/ { "SMSG_CHANNEL_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x09A*/ { "CMSG_CHANNEL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelList },
+ /*0x09B*/ { "SMSG_CHANNEL_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x09C*/ { "CMSG_CHANNEL_PASSWORD", STATUS_LOGGEDIN, &WorldSession::HandleChannelPassword },
+ /*0x09D*/ { "CMSG_CHANNEL_SET_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelSetOwner },
+ /*0x09E*/ { "CMSG_CHANNEL_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelOwner },
+ /*0x09F*/ { "CMSG_CHANNEL_MODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerator },
+ /*0x0A0*/ { "CMSG_CHANNEL_UNMODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmoderator },
+ /*0x0A1*/ { "CMSG_CHANNEL_MUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelMute },
+ /*0x0A2*/ { "CMSG_CHANNEL_UNMUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmute },
+ /*0x0A3*/ { "CMSG_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelInvite },
+ /*0x0A4*/ { "CMSG_CHANNEL_KICK", STATUS_LOGGEDIN, &WorldSession::HandleChannelKick },
+ /*0x0A5*/ { "CMSG_CHANNEL_BAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelBan },
+ /*0x0A6*/ { "CMSG_CHANNEL_UNBAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnban },
+ /*0x0A7*/ { "CMSG_CHANNEL_ANNOUNCEMENTS", STATUS_LOGGEDIN, &WorldSession::HandleChannelAnnounce },
+ /*0x0A8*/ { "CMSG_CHANNEL_MODERATE", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerate },
+ /*0x0A9*/ { "SMSG_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0AA*/ { "SMSG_DESTROY_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0AB*/ { "CMSG_USE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleUseItemOpcode },
+ /*0x0AC*/ { "CMSG_OPEN_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleOpenItemOpcode },
+ /*0x0AD*/ { "CMSG_READ_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleReadItem },
+ /*0x0AE*/ { "SMSG_READ_ITEM_OK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0AF*/ { "SMSG_READ_ITEM_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0B0*/ { "SMSG_ITEM_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0B1*/ { "CMSG_GAMEOBJ_USE", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectUseOpcode },
+ /*0x0B2*/ { "CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0B3*/ { "SMSG_GAMEOBJECT_CUSTOM_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0B4*/ { "CMSG_AREATRIGGER", STATUS_LOGGEDIN, &WorldSession::HandleAreaTriggerOpcode },
+ /*0x0B5*/ { "MSG_MOVE_START_FORWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0B6*/ { "MSG_MOVE_START_BACKWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0B7*/ { "MSG_MOVE_STOP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0B8*/ { "MSG_MOVE_START_STRAFE_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0B9*/ { "MSG_MOVE_START_STRAFE_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BA*/ { "MSG_MOVE_STOP_STRAFE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BB*/ { "MSG_MOVE_JUMP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BC*/ { "MSG_MOVE_START_TURN_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BD*/ { "MSG_MOVE_START_TURN_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BE*/ { "MSG_MOVE_STOP_TURN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0BF*/ { "MSG_MOVE_START_PITCH_UP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0C0*/ { "MSG_MOVE_START_PITCH_DOWN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0C1*/ { "MSG_MOVE_STOP_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0C2*/ { "MSG_MOVE_SET_RUN_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0C3*/ { "MSG_MOVE_SET_WALK_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0C4*/ { "MSG_MOVE_TOGGLE_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0C5*/ { "MSG_MOVE_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0C6*/ { "MSG_MOVE_TELEPORT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0C7*/ { "MSG_MOVE_TELEPORT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveTeleportAck },
+ /*0x0C8*/ { "MSG_MOVE_TOGGLE_FALL_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0C9*/ { "MSG_MOVE_FALL_LAND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0CA*/ { "MSG_MOVE_START_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0CB*/ { "MSG_MOVE_STOP_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0CC*/ { "MSG_MOVE_SET_RUN_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0CD*/ { "MSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0CE*/ { "MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0CF*/ { "MSG_MOVE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D0*/ { "MSG_MOVE_SET_WALK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D1*/ { "MSG_MOVE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D2*/ { "MSG_MOVE_SET_SWIM_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D3*/ { "MSG_MOVE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D4*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D5*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D6*/ { "MSG_MOVE_SET_ALL_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D7*/ { "MSG_MOVE_SET_TURN_RATE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D8*/ { "MSG_MOVE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0D9*/ { "MSG_MOVE_TOGGLE_COLLISION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0DA*/ { "MSG_MOVE_SET_FACING", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0DB*/ { "MSG_MOVE_SET_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0DC*/ { "MSG_MOVE_WORLDPORT_ACK", STATUS_TRANSFER_PENDING, &WorldSession::HandleMoveWorldportAckOpcode},
+ /*0x0DD*/ { "SMSG_MONSTER_MOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0DE*/ { "SMSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0DF*/ { "SMSG_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0E0*/ { "MSG_MOVE_SET_RAW_POSITION_ACK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0E1*/ { "CMSG_MOVE_SET_RAW_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0E2*/ { "SMSG_FORCE_RUN_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0E3*/ { "CMSG_FORCE_RUN_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck },
+ /*0x0E4*/ { "SMSG_FORCE_RUN_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0E5*/ { "CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck },
+ /*0x0E6*/ { "SMSG_FORCE_SWIM_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0E7*/ { "CMSG_FORCE_SWIM_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck },
+ /*0x0E8*/ { "SMSG_FORCE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0E9*/ { "CMSG_FORCE_MOVE_ROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveRootAck },
+ /*0x0EA*/ { "SMSG_FORCE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0EB*/ { "CMSG_FORCE_MOVE_UNROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveUnRootAck },
+ /*0x0EC*/ { "MSG_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0ED*/ { "MSG_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0EE*/ { "MSG_MOVE_HEARTBEAT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x0EF*/ { "SMSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0F0*/ { "CMSG_MOVE_KNOCK_BACK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveKnockBackAck },
+ /*0x0F1*/ { "MSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0F2*/ { "SMSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0F3*/ { "SMSG_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0F4*/ { "SMSG_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0F5*/ { "SMSG_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0F6*/ { "CMSG_MOVE_HOVER_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveHoverAck },
+ /*0x0F7*/ { "MSG_MOVE_HOVER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0F8*/ { "CMSG_TRIGGER_CINEMATIC_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0F9*/ { "CMSG_OPENING_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x0FA*/ { "SMSG_TRIGGER_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0FB*/ { "CMSG_NEXT_CINEMATIC_CAMERA", STATUS_LOGGEDIN, &WorldSession::HandleNextCinematicCamera },
+ /*0x0FC*/ { "CMSG_COMPLETE_CINEMATIC", STATUS_LOGGEDIN, &WorldSession::HandleCompleteCinema },
+ /*0x0FD*/ { "SMSG_TUTORIAL_FLAGS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x0FE*/ { "CMSG_TUTORIAL_FLAG", STATUS_LOGGEDIN, &WorldSession::HandleTutorialFlag },
+ /*0x0FF*/ { "CMSG_TUTORIAL_CLEAR", STATUS_LOGGEDIN, &WorldSession::HandleTutorialClear },
+ /*0x100*/ { "CMSG_TUTORIAL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleTutorialReset },
+ /*0x101*/ { "CMSG_STANDSTATECHANGE", STATUS_LOGGEDIN, &WorldSession::HandleStandStateChangeOpcode },
+ /*0x102*/ { "CMSG_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleEmoteOpcode },
+ /*0x103*/ { "SMSG_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x104*/ { "CMSG_TEXT_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleTextEmoteOpcode },
+ /*0x105*/ { "SMSG_TEXT_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x106*/ { "CMSG_AUTOEQUIP_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x107*/ { "CMSG_AUTOSTORE_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x108*/ { "CMSG_AUTOSTORE_LOOT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutostoreLootItemOpcode },
+ /*0x109*/ { "CMSG_STORE_LOOT_IN_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x10A*/ { "CMSG_AUTOEQUIP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemOpcode },
+ /*0x10B*/ { "CMSG_AUTOSTORE_BAG_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBagItemOpcode },
+ /*0x10C*/ { "CMSG_SWAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapItem },
+ /*0x10D*/ { "CMSG_SWAP_INV_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapInvItemOpcode },
+ /*0x10E*/ { "CMSG_SPLIT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSplitItemOpcode },
+ /*0x10F*/ { "CMSG_AUTOEQUIP_ITEM_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemSlotOpcode },
+ /*0x110*/ { "OBSOLETE_DROP_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x111*/ { "CMSG_DESTROYITEM", STATUS_LOGGEDIN, &WorldSession::HandleDestroyItemOpcode },
+ /*0x112*/ { "SMSG_INVENTORY_CHANGE_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x113*/ { "SMSG_OPEN_CONTAINER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x114*/ { "CMSG_INSPECT", STATUS_LOGGEDIN, &WorldSession::HandleInspectOpcode },
+ /*0x115*/ { "SMSG_INSPECT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x116*/ { "CMSG_INITIATE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleInitiateTradeOpcode },
+ /*0x117*/ { "CMSG_BEGIN_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBeginTradeOpcode },
+ /*0x118*/ { "CMSG_BUSY_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBusyTradeOpcode },
+ /*0x119*/ { "CMSG_IGNORE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleIgnoreTradeOpcode },
+ /*0x11A*/ { "CMSG_ACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleAcceptTradeOpcode },
+ /*0x11B*/ { "CMSG_UNACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleUnacceptTradeOpcode },
+ /*0x11C*/ { "CMSG_CANCEL_TRADE", STATUS_AUTHED, &WorldSession::HandleCancelTradeOpcode },
+ // also send after logout complete
+ /*0x11D*/ { "CMSG_SET_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeItemOpcode },
+ /*0x11E*/ { "CMSG_CLEAR_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleClearTradeItemOpcode },
+ /*0x11F*/ { "CMSG_SET_TRADE_GOLD", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeGoldOpcode },
+ /*0x120*/ { "SMSG_TRADE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x121*/ { "SMSG_TRADE_STATUS_EXTENDED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x122*/ { "SMSG_INITIALIZE_FACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x123*/ { "SMSG_SET_FACTION_VISIBLE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x124*/ { "SMSG_SET_FACTION_STANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x125*/ { "CMSG_SET_FACTION_ATWAR", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionAtWar },
+ /*0x126*/ { "CMSG_SET_FACTION_CHEAT", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionCheat },
+ /*0x127*/ { "SMSG_SET_PROFICIENCY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x128*/ { "CMSG_SET_ACTION_BUTTON", STATUS_LOGGEDIN, &WorldSession::HandleSetActionButtonOpcode },
+ /*0x129*/ { "SMSG_ACTION_BUTTONS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x12A*/ { "SMSG_INITIAL_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x12B*/ { "SMSG_LEARNED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x12C*/ { "SMSG_SUPERCEDED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x12D*/ { "CMSG_NEW_SPELL_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x12E*/ { "CMSG_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCastSpellOpcode },
+ /*0x12F*/ { "CMSG_CANCEL_CAST", STATUS_LOGGEDIN, &WorldSession::HandleCancelCastOpcode },
+ /*0x130*/ { "SMSG_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x131*/ { "SMSG_SPELL_START", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x132*/ { "SMSG_SPELL_GO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x133*/ { "SMSG_SPELL_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x134*/ { "SMSG_SPELL_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x135*/ { "SMSG_COOLDOWN_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x136*/ { "CMSG_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelAuraOpcode },
+ /*0x137*/ { "SMSG_UPDATE_AURA_DURATION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x138*/ { "SMSG_PET_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x139*/ { "MSG_CHANNEL_START", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x13A*/ { "MSG_CHANNEL_UPDATE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x13B*/ { "CMSG_CANCEL_CHANNELLING", STATUS_LOGGEDIN, &WorldSession::HandleCancelChanneling },
+ /*0x13C*/ { "SMSG_AI_REACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x13D*/ { "CMSG_SET_SELECTION", STATUS_LOGGEDIN, &WorldSession::HandleSetSelectionOpcode },
+ /*0x13E*/ { "CMSG_SET_TARGET_OBSOLETE", STATUS_LOGGEDIN, &WorldSession::HandleSetTargetOpcode },
+ /*0x13F*/ { "CMSG_UNUSED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x140*/ { "CMSG_UNUSED2", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x141*/ { "CMSG_ATTACKSWING", STATUS_LOGGEDIN, &WorldSession::HandleAttackSwingOpcode },
+ /*0x142*/ { "CMSG_ATTACKSTOP", STATUS_LOGGEDIN, &WorldSession::HandleAttackStopOpcode },
+ /*0x143*/ { "SMSG_ATTACKSTART", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x144*/ { "SMSG_ATTACKSTOP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x145*/ { "SMSG_ATTACKSWING_NOTINRANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x146*/ { "SMSG_ATTACKSWING_BADFACING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x147*/ { "SMSG_ATTACKSWING_NOTSTANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x148*/ { "SMSG_ATTACKSWING_DEADTARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x149*/ { "SMSG_ATTACKSWING_CANT_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14A*/ { "SMSG_ATTACKERSTATEUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14B*/ { "SMSG_VICTIMSTATEUPDATE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14C*/ { "SMSG_DAMAGE_DONE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14D*/ { "SMSG_DAMAGE_TAKEN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14E*/ { "SMSG_CANCEL_COMBAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x14F*/ { "SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x150*/ { "SMSG_SPELLHEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x151*/ { "SMSG_SPELLENERGIZELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x152*/ { "CMSG_SHEATHE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x153*/ { "CMSG_SAVE_PLAYER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x154*/ { "CMSG_SETDEATHBINDPOINT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x155*/ { "SMSG_BINDPOINTUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x156*/ { "CMSG_GETDEATHBINDZONE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x157*/ { "SMSG_BINDZONEREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x158*/ { "SMSG_PLAYERBOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x159*/ { "SMSG_CLIENT_CONTROL_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x15A*/ { "CMSG_REPOP_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleRepopRequestOpcode },
+ /*0x15B*/ { "SMSG_RESURRECT_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x15C*/ { "CMSG_RESURRECT_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleResurrectResponseOpcode },
+ /*0x15D*/ { "CMSG_LOOT", STATUS_LOGGEDIN, &WorldSession::HandleLootOpcode },
+ /*0x15E*/ { "CMSG_LOOT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleLootMoneyOpcode },
+ /*0x15F*/ { "CMSG_LOOT_RELEASE", STATUS_LOGGEDIN, &WorldSession::HandleLootReleaseOpcode },
+ /*0x160*/ { "SMSG_LOOT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x161*/ { "SMSG_LOOT_RELEASE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x162*/ { "SMSG_LOOT_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x163*/ { "SMSG_LOOT_MONEY_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x164*/ { "SMSG_LOOT_ITEM_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x165*/ { "SMSG_LOOT_CLEAR_MONEY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x166*/ { "SMSG_ITEM_PUSH_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x167*/ { "SMSG_DUEL_REQUESTED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x168*/ { "SMSG_DUEL_OUTOFBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x169*/ { "SMSG_DUEL_INBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x16A*/ { "SMSG_DUEL_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x16B*/ { "SMSG_DUEL_WINNER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x16C*/ { "CMSG_DUEL_ACCEPTED", STATUS_LOGGEDIN, &WorldSession::HandleDuelAcceptedOpcode },
+ /*0x16D*/ { "CMSG_DUEL_CANCELLED", STATUS_LOGGEDIN, &WorldSession::HandleDuelCancelledOpcode },
+ /*0x16E*/ { "SMSG_MOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x16F*/ { "SMSG_DISMOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x170*/ { "SMSG_PUREMOUNT_CANCELLED_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x171*/ { "CMSG_MOUNTSPECIAL_ANIM", STATUS_LOGGEDIN, &WorldSession::HandleMountSpecialAnimOpcode },
+ /*0x172*/ { "SMSG_MOUNTSPECIAL_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x173*/ { "SMSG_PET_TAME_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x174*/ { "CMSG_PET_SET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetSetAction },
+ /*0x175*/ { "CMSG_PET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetAction },
+ /*0x176*/ { "CMSG_PET_ABANDON", STATUS_LOGGEDIN, &WorldSession::HandlePetAbandon },
+ /*0x177*/ { "CMSG_PET_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetRename },
+ /*0x178*/ { "SMSG_PET_NAME_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x179*/ { "SMSG_PET_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x17A*/ { "SMSG_PET_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x17B*/ { "CMSG_GOSSIP_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleGossipHelloOpcode },
+ /*0x17C*/ { "CMSG_GOSSIP_SELECT_OPTION", STATUS_LOGGEDIN, &WorldSession::HandleGossipSelectOptionOpcode },
+ /*0x17D*/ { "SMSG_GOSSIP_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x17E*/ { "SMSG_GOSSIP_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x17F*/ { "CMSG_NPC_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNpcTextQueryOpcode },
+ /*0x180*/ { "SMSG_NPC_TEXT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x181*/ { "SMSG_NPC_WONT_TALK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x182*/ { "CMSG_QUESTGIVER_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverStatusQueryOpcode},
+ /*0x183*/ { "SMSG_QUESTGIVER_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x184*/ { "CMSG_QUESTGIVER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverHelloOpcode },
+ /*0x185*/ { "SMSG_QUESTGIVER_QUEST_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x186*/ { "CMSG_QUESTGIVER_QUERY_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverQuestQueryOpcode},
+ /*0x187*/ { "CMSG_QUESTGIVER_QUEST_AUTOLAUNCH", STATUS_LOGGEDIN, &WorldSession::HandleQuestAutoLaunch },
+ /*0x188*/ { "SMSG_QUESTGIVER_QUEST_DETAILS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x189*/ { "CMSG_QUESTGIVER_ACCEPT_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverAcceptQuestOpcode},
+ /*0x18A*/ { "CMSG_QUESTGIVER_COMPLETE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestComplete },
+ /*0x18B*/ { "SMSG_QUESTGIVER_REQUEST_ITEMS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x18C*/ { "CMSG_QUESTGIVER_REQUEST_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverRequestRewardOpcode},
+ /*0x18D*/ { "SMSG_QUESTGIVER_OFFER_REWARD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x18E*/ { "CMSG_QUESTGIVER_CHOOSE_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverChooseRewardOpcode},
+ /*0x18F*/ { "SMSG_QUESTGIVER_QUEST_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x190*/ { "CMSG_QUESTGIVER_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverCancel },
+ /*0x191*/ { "SMSG_QUESTGIVER_QUEST_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x192*/ { "SMSG_QUESTGIVER_QUEST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x193*/ { "CMSG_QUESTLOG_SWAP_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogSwapQuest },
+ /*0x194*/ { "CMSG_QUESTLOG_REMOVE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogRemoveQuest },
+ /*0x195*/ { "SMSG_QUESTLOG_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x196*/ { "SMSG_QUESTUPDATE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x197*/ { "SMSG_QUESTUPDATE_FAILEDTIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x198*/ { "SMSG_QUESTUPDATE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x199*/ { "SMSG_QUESTUPDATE_ADD_KILL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x19A*/ { "SMSG_QUESTUPDATE_ADD_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x19B*/ { "CMSG_QUEST_CONFIRM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleQuestConfirmAccept },
+ /*0x19C*/ { "SMSG_QUEST_CONFIRM_ACCEPT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x19D*/ { "CMSG_PUSHQUESTTOPARTY", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushToParty },
+ /*0x19E*/ { "CMSG_LIST_INVENTORY", STATUS_LOGGEDIN, &WorldSession::HandleListInventoryOpcode },
+ /*0x19F*/ { "SMSG_LIST_INVENTORY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1A0*/ { "CMSG_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSellItemOpcode },
+ /*0x1A1*/ { "SMSG_SELL_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1A2*/ { "CMSG_BUY_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemOpcode },
+ /*0x1A3*/ { "CMSG_BUY_ITEM_IN_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemInSlotOpcode },
+ /*0x1A4*/ { "SMSG_BUY_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1A5*/ { "SMSG_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1A6*/ { "CMSG_TAXICLEARALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1A7*/ { "CMSG_TAXIENABLEALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1A8*/ { "CMSG_TAXISHOWNODES", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1A9*/ { "SMSG_SHOWTAXINODES", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1AA*/ { "CMSG_TAXINODE_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNodeStatusQueryOpcode },
+ /*0x1AB*/ { "SMSG_TAXINODE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1AC*/ { "CMSG_TAXIQUERYAVAILABLENODES", STATUS_LOGGEDIN, &WorldSession::HandleTaxiQueryAvailableNodesOpcode},
+ /*0x1AD*/ { "CMSG_ACTIVATETAXI", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiOpcode },
+ /*0x1AE*/ { "SMSG_ACTIVATETAXIREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1AF*/ { "SMSG_NEW_TAXI_PATH", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B0*/ { "CMSG_TRAINER_LIST", STATUS_LOGGEDIN, &WorldSession::HandleTrainerListOpcode },
+ /*0x1B1*/ { "SMSG_TRAINER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B2*/ { "CMSG_TRAINER_BUY_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleTrainerBuySpellOpcode },
+ /*0x1B3*/ { "SMSG_TRAINER_BUY_SUCCEEDED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B4*/ { "SMSG_TRAINER_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B5*/ { "CMSG_BINDER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBinderActivateOpcode },
+ /*0x1B6*/ { "SMSG_PLAYERBINDERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B7*/ { "CMSG_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBankerActivateOpcode },
+ /*0x1B8*/ { "SMSG_SHOW_BANK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1B9*/ { "CMSG_BUY_BANK_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyBankSlotOpcode },
+ /*0x1BA*/ { "SMSG_BUY_BANK_SLOT_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1BB*/ { "CMSG_PETITION_SHOWLIST", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowListOpcode },
+ /*0x1BC*/ { "SMSG_PETITION_SHOWLIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1BD*/ { "CMSG_PETITION_BUY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionBuyOpcode },
+ /*0x1BE*/ { "CMSG_PETITION_SHOW_SIGNATURES", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowSignOpcode },
+ /*0x1BF*/ { "SMSG_PETITION_SHOW_SIGNATURES", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1C0*/ { "CMSG_PETITION_SIGN", STATUS_LOGGEDIN, &WorldSession::HandlePetitionSignOpcode },
+ /*0x1C1*/ { "SMSG_PETITION_SIGN_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1C2*/ { "MSG_PETITION_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandlePetitionDeclineOpcode },
+ /*0x1C3*/ { "CMSG_OFFER_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleOfferPetitionOpcode },
+ /*0x1C4*/ { "CMSG_TURN_IN_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleTurnInPetitionOpcode },
+ /*0x1C5*/ { "SMSG_TURN_IN_PETITION_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1C6*/ { "CMSG_PETITION_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionQueryOpcode },
+ /*0x1C7*/ { "SMSG_PETITION_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1C8*/ { "SMSG_FISH_NOT_HOOKED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1C9*/ { "SMSG_FISH_ESCAPED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1CA*/ { "CMSG_BUG", STATUS_LOGGEDIN, &WorldSession::HandleBugOpcode },
+ /*0x1CB*/ { "SMSG_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1CC*/ { "CMSG_PLAYED_TIME", STATUS_LOGGEDIN, &WorldSession::HandlePlayedTime },
+ /*0x1CD*/ { "SMSG_PLAYED_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1CE*/ { "CMSG_QUERY_TIME", STATUS_LOGGEDIN, &WorldSession::HandleQueryTimeOpcode },
+ /*0x1CF*/ { "SMSG_QUERY_TIME_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D0*/ { "SMSG_LOG_XPGAIN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D1*/ { "SMSG_AURACASTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D2*/ { "CMSG_RECLAIM_CORPSE", STATUS_LOGGEDIN, &WorldSession::HandleCorpseReclaimOpcode },
+ /*0x1D3*/ { "CMSG_WRAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleWrapItemOpcode },
+ /*0x1D4*/ { "SMSG_LEVELUP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D5*/ { "MSG_MINIMAP_PING", STATUS_LOGGEDIN, &WorldSession::HandleMinimapPingOpcode },
+ /*0x1D6*/ { "SMSG_RESISTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D7*/ { "SMSG_ENCHANTMENTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1D8*/ { "CMSG_SET_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1D9*/ { "SMSG_START_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1DA*/ { "SMSG_PAUSE_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1DB*/ { "SMSG_STOP_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1DC*/ { "CMSG_PING", STATUS_NEVER, &WorldSession::Handle_EarlyProccess },
+ /*0x1DD*/ { "SMSG_PONG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1DE*/ { "SMSG_CLEAR_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1DF*/ { "SMSG_GAMEOBJECT_PAGETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1E0*/ { "CMSG_SETSHEATHED", STATUS_LOGGEDIN, &WorldSession::HandleSetSheathedOpcode },
+ /*0x1E1*/ { "SMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1E2*/ { "SMSG_SPELL_DELAYED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1E3*/ { "CMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1E4*/ { "SMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1E5*/ { "CMSG_GHOST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1E6*/ { "CMSG_GM_INVIS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1E7*/ { "SMSG_INVALID_PROMOTION_CODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1E8*/ { "MSG_GM_BIND_OTHER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1E9*/ { "MSG_GM_SUMMON", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1EA*/ { "SMSG_ITEM_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1EB*/ { "SMSG_ITEM_ENCHANT_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1EC*/ { "SMSG_AUTH_CHALLENGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1ED*/ { "CMSG_AUTH_SESSION", STATUS_NEVER, &WorldSession::Handle_EarlyProccess },
+ /*0x1EE*/ { "SMSG_AUTH_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1EF*/ { "MSG_GM_SHOWLABEL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1F0*/ { "CMSG_PET_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleAddDynamicTargetObsoleteOpcode},
+ /*0x1F1*/ { "MSG_SAVE_GUILD_EMBLEM", STATUS_LOGGEDIN, &WorldSession::HandleGuildSaveEmblemOpcode },
+ /*0x1F2*/ { "MSG_TABARDVENDOR_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleTabardVendorActivateOpcode},
+ /*0x1F3*/ { "SMSG_PLAY_SPELL_VISUAL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1F4*/ { "CMSG_ZONEUPDATE", STATUS_LOGGEDIN, &WorldSession::HandleZoneUpdateOpcode },
+ /*0x1F5*/ { "SMSG_PARTYKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1F6*/ { "SMSG_COMPRESSED_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1F7*/ { "SMSG_PLAY_SPELL_IMPACT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1F8*/ { "SMSG_EXPLORATION_EXPERIENCE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1F9*/ { "CMSG_GM_SET_SECURITY_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1FA*/ { "CMSG_GM_NUKE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1FB*/ { "MSG_RANDOM_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleRandomRollOpcode },
+ /*0x1FC*/ { "SMSG_ENVIRONMENTALDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1FD*/ { "CMSG_RWHOIS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x1FE*/ { "SMSG_RWHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x1FF*/ { "MSG_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLookingForGroup },
+ /*0x200*/ { "CMSG_SET_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleSetLfgOpcode },
+ /*0x201*/ { "CMSG_UNLEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x202*/ { "CMSG_UNLEARN_SKILL", STATUS_LOGGEDIN, &WorldSession::HandleUnlearnSkillOpcode },
+ /*0x203*/ { "SMSG_REMOVED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x204*/ { "CMSG_DECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x205*/ { "CMSG_GMTICKET_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketCreateOpcode },
+ /*0x206*/ { "SMSG_GMTICKET_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x207*/ { "CMSG_GMTICKET_UPDATETEXT", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketUpdateTextOpcode },
+ /*0x208*/ { "SMSG_GMTICKET_UPDATETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x209*/ { "SMSG_ACCOUNT_DATA_TIMES", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x20A*/ { "CMSG_REQUEST_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleRequestAccountData },
+ /*0x20B*/ { "CMSG_UPDATE_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleUpdateAccountData },
+ /*0x20C*/ { "SMSG_UPDATE_ACCOUNT_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x20D*/ { "SMSG_CLEAR_FAR_SIGHT_IMMEDIATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x20E*/ { "SMSG_POWERGAINLOG_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x20F*/ { "CMSG_GM_TEACH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x210*/ { "CMSG_GM_CREATE_ITEM_TARGET", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x211*/ { "CMSG_GMTICKET_GETTICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketGetTicketOpcode },
+ /*0x212*/ { "SMSG_GMTICKET_GETTICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x213*/ { "CMSG_UNLEARN_TALENTS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x214*/ { "SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x215*/ { "SMSG_GAMEOBJECT_DESPAWN_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x216*/ { "MSG_CORPSE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCorpseQueryOpcode },
+ /*0x217*/ { "CMSG_GMTICKET_DELETETICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketDeleteOpcode },
+ /*0x218*/ { "SMSG_GMTICKET_DELETETICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x219*/ { "SMSG_CHAT_WRONG_FACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x21A*/ { "CMSG_GMTICKET_SYSTEMSTATUS", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketSystemStatusOpcode},
+ /*0x21B*/ { "SMSG_GMTICKET_SYSTEMSTATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x21C*/ { "CMSG_SPIRIT_HEALER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleSpiritHealerActivateOpcode},
+ /*0x21D*/ { "CMSG_SET_STAT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x21E*/ { "SMSG_SET_REST_START", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x21F*/ { "CMSG_SKILL_BUY_STEP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x220*/ { "CMSG_SKILL_BUY_RANK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x221*/ { "CMSG_XP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x222*/ { "SMSG_SPIRIT_HEALER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x223*/ { "CMSG_CHARACTER_POINT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x224*/ { "SMSG_GOSSIP_POI", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x225*/ { "CMSG_CHAT_IGNORED", STATUS_LOGGEDIN, &WorldSession::HandleChatIgnoredOpcode },
+ /*0x226*/ { "CMSG_GM_VISION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x227*/ { "CMSG_SERVER_COMMAND", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x228*/ { "CMSG_GM_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x229*/ { "CMSG_GM_REVEALTO", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22A*/ { "CMSG_GM_RESURRECT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22B*/ { "CMSG_GM_SUMMONMOB", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22C*/ { "CMSG_GM_MOVECORPSE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22D*/ { "CMSG_GM_FREEZE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22E*/ { "CMSG_GM_UBERINVIS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x22F*/ { "CMSG_GM_REQUEST_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x230*/ { "SMSG_GM_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x231*/ { "CMSG_GUILD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildRankOpcode },
+ /*0x232*/ { "CMSG_GUILD_ADD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildAddRankOpcode },
+ /*0x233*/ { "CMSG_GUILD_DEL_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildDelRankOpcode },
+ /*0x234*/ { "CMSG_GUILD_SET_PUBLIC_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetPublicNoteOpcode },
+ /*0x235*/ { "CMSG_GUILD_SET_OFFICER_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetOfficerNoteOpcode },
+ /*0x236*/ { "SMSG_LOGIN_VERIFY_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x237*/ { "CMSG_CLEAR_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x238*/ { "CMSG_SEND_MAIL", STATUS_LOGGEDIN, &WorldSession::HandleSendMail },
+ /*0x239*/ { "SMSG_SEND_MAIL_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x23A*/ { "CMSG_GET_MAIL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleGetMail },
+ /*0x23B*/ { "SMSG_MAIL_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x23C*/ { "CMSG_BATTLEFIELD_LIST", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundListOpcode },
+ /*0x23D*/ { "SMSG_BATTLEFIELD_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x23E*/ { "CMSG_BATTLEFIELD_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x23F*/ { "SMSG_BATTLEFIELD_WIN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x240*/ { "SMSG_BATTLEFIELD_LOSE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x241*/ { "CMSG_TAXICLEARNODE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x242*/ { "CMSG_TAXIENABLENODE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x243*/ { "CMSG_ITEM_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemTextQuery },
+ /*0x244*/ { "SMSG_ITEM_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x245*/ { "CMSG_MAIL_TAKE_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleTakeMoney },
+ /*0x246*/ { "CMSG_MAIL_TAKE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleTakeItem },
+ /*0x247*/ { "CMSG_MAIL_MARK_AS_READ", STATUS_LOGGEDIN, &WorldSession::HandleMarkAsRead },
+ /*0x248*/ { "CMSG_MAIL_RETURN_TO_SENDER", STATUS_LOGGEDIN, &WorldSession::HandleReturnToSender },
+ /*0x249*/ { "CMSG_MAIL_DELETE", STATUS_LOGGEDIN, &WorldSession::HandleMailDelete },
+ /*0x24A*/ { "CMSG_MAIL_CREATE_TEXT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleMailCreateTextItem },
+ /*0x24B*/ { "SMSG_SPELLLOGMISS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x24C*/ { "SMSG_SPELLLOGEXECUTE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x24D*/ { "SMSG_DEBUGAURAPROC", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x24E*/ { "SMSG_PERIODICAURALOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x24F*/ { "SMSG_SPELLDAMAGESHIELD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x250*/ { "SMSG_SPELLNONMELEEDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x251*/ { "CMSG_LEARN_TALENT", STATUS_LOGGEDIN, &WorldSession::HandleLearnTalentOpcode },
+ /*0x252*/ { "SMSG_RESURRECT_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x253*/ { "CMSG_TOGGLE_PVP", STATUS_LOGGEDIN, &WorldSession::HandleTogglePvP },
+ /*0x254*/ { "SMSG_ZONE_UNDER_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x255*/ { "MSG_AUCTION_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleAuctionHelloOpcode },
+ /*0x256*/ { "CMSG_AUCTION_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionSellItem },
+ /*0x257*/ { "CMSG_AUCTION_REMOVE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionRemoveItem },
+ /*0x258*/ { "CMSG_AUCTION_LIST_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListItems },
+ /*0x259*/ { "CMSG_AUCTION_LIST_OWNER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListOwnerItems },
+ /*0x25A*/ { "CMSG_AUCTION_PLACE_BID", STATUS_LOGGEDIN, &WorldSession::HandleAuctionPlaceBid },
+ /*0x25B*/ { "SMSG_AUCTION_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x25C*/ { "SMSG_AUCTION_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x25D*/ { "SMSG_AUCTION_OWNER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x25E*/ { "SMSG_AUCTION_BIDDER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x25F*/ { "SMSG_AUCTION_OWNER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x260*/ { "SMSG_PROCRESIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x261*/ { "SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE",STATUS_NEVER,&WorldSession::Handle_ServerSide },
+ /*0x262*/ { "SMSG_DISPEL_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x263*/ { "SMSG_SPELLORDAMAGE_IMMUNE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x264*/ { "CMSG_AUCTION_LIST_BIDDER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListBidderItems },
+ /*0x265*/ { "SMSG_AUCTION_BIDDER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x266*/ { "SMSG_SET_FLAT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x267*/ { "SMSG_SET_PCT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x268*/ { "CMSG_SET_AMMO", STATUS_LOGGEDIN, &WorldSession::HandleSetAmmoOpcode },
+ /*0x269*/ { "SMSG_CORPSE_RECLAIM_DELAY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x26A*/ { "CMSG_SET_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleSetActiveMoverOpcode },
+ /*0x26B*/ { "CMSG_PET_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandlePetCancelAuraOpcode },
+ /*0x26C*/ { "CMSG_PLAYER_AI_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x26D*/ { "CMSG_CANCEL_AUTO_REPEAT_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCancelAutoRepeatSpellOpcode},
+ /*0x26E*/ { "MSG_GM_ACCOUNT_ONLINE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x26F*/ { "MSG_LIST_STABLED_PETS", STATUS_LOGGEDIN, &WorldSession::HandleListStabledPetsOpcode },
+ /*0x270*/ { "CMSG_STABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStablePet },
+ /*0x271*/ { "CMSG_UNSTABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleUnstablePet },
+ /*0x272*/ { "CMSG_BUY_STABLE_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyStableSlot },
+ /*0x273*/ { "SMSG_STABLE_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x274*/ { "CMSG_STABLE_REVIVE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableRevivePet },
+ /*0x275*/ { "CMSG_STABLE_SWAP_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableSwapPet },
+ /*0x276*/ { "MSG_QUEST_PUSH_RESULT", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushResult },
+ /*0x277*/ { "SMSG_PLAY_MUSIC", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x278*/ { "SMSG_PLAY_OBJECT_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x279*/ { "CMSG_REQUEST_PET_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestPetInfoOpcode },
+ /*0x27A*/ { "CMSG_FAR_SIGHT", STATUS_LOGGEDIN, &WorldSession::HandleFarSightOpcode },
+ /*0x27B*/ { "SMSG_SPELLDISPELLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x27C*/ { "SMSG_DAMAGE_CALC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x27D*/ { "CMSG_ENABLE_DAMAGE_LOG", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x27E*/ { "CMSG_GROUP_CHANGE_SUB_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleGroupChangeSubGroupOpcode },
+ /*0x27F*/ { "CMSG_REQUEST_PARTY_MEMBER_STATS", STATUS_LOGGEDIN, &WorldSession::HandleRequestPartyMemberStatsOpcode},
+ /*0x280*/ { "CMSG_GROUP_SWAP_SUB_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x281*/ { "CMSG_RESET_FACTION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x282*/ { "CMSG_AUTOSTORE_BANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBankItemOpcode },
+ /*0x283*/ { "CMSG_AUTOBANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoBankItemOpcode },
+ /*0x284*/ { "MSG_QUERY_NEXT_MAIL_TIME", STATUS_LOGGEDIN, &WorldSession::HandleMsgQueryNextMailtime },
+ /*0x285*/ { "SMSG_RECEIVED_MAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x286*/ { "SMSG_RAID_GROUP_ONLY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x287*/ { "CMSG_SET_DURABILITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x288*/ { "CMSG_SET_PVP_RANK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x289*/ { "CMSG_ADD_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x28A*/ { "CMSG_DEL_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x28B*/ { "CMSG_SET_PVP_TITLE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x28C*/ { "SMSG_PVP_CREDIT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x28D*/ { "SMSG_AUCTION_REMOVED_NOTIFICATION",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x28E*/ { "CMSG_GROUP_RAID_CONVERT", STATUS_LOGGEDIN, &WorldSession::HandleRaidConvertOpcode },
+ /*0x28F*/ { "CMSG_GROUP_ASSISTANT_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupAssistantOpcode },
+ /*0x290*/ { "CMSG_BUYBACK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuybackItem },
+ /*0x291*/ { "SMSG_SERVER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x292*/ { "CMSG_MEETINGSTONE_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x293*/ { "CMSG_MEETINGSTONE_LEAVE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x294*/ { "CMSG_MEETINGSTONE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x295*/ { "SMSG_MEETINGSTONE_SETQUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x296*/ { "CMSG_MEETINGSTONE_INFO", STATUS_LOGGEDIN, &WorldSession::HandleMeetingStoneInfo },
+ /*0x297*/ { "SMSG_MEETINGSTONE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x298*/ { "SMSG_MEETINGSTONE_IN_PROGRESS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x299*/ { "SMSG_MEETINGSTONE_MEMBER_ADDED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x29A*/ { "CMSG_GMTICKETSYSTEM_TOGGLE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x29B*/ { "CMSG_CANCEL_GROWTH_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelGrowthAuraOpcode },
+ /*0x29C*/ { "SMSG_CANCEL_AUTO_REPEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x29D*/ { "SMSG_STANDSTATE_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x29E*/ { "SMSG_LOOT_ALL_PASSED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x29F*/ { "SMSG_LOOT_ROLL_WON", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A0*/ { "CMSG_LOOT_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleLootRoll },
+ /*0x2A1*/ { "SMSG_LOOT_START_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A2*/ { "SMSG_LOOT_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A3*/ { "CMSG_LOOT_MASTER_GIVE", STATUS_LOGGEDIN, &WorldSession::HandleLootMasterGiveOpcode },
+ /*0x2A4*/ { "SMSG_LOOT_MASTER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A5*/ { "SMSG_SET_FORCED_REACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A6*/ { "SMSG_SPELL_FAILED_OTHER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A7*/ { "SMSG_GAMEOBJECT_RESET_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2A8*/ { "CMSG_REPAIR_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleRepairItemOpcode },
+ /*0x2A9*/ { "SMSG_CHAT_PLAYER_NOT_FOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2AA*/ { "MSG_TALENT_WIPE_CONFIRM", STATUS_LOGGEDIN, &WorldSession::HandleTalentWipeOpcode },
+ /*0x2AB*/ { "SMSG_SUMMON_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2AC*/ { "CMSG_SUMMON_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleSummonResponseOpcode },
+ /*0x2AD*/ { "MSG_MOVE_TOGGLE_GRAVITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2AE*/ { "SMSG_MONSTER_MOVE_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2AF*/ { "SMSG_PET_BROKEN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2B0*/ { "MSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2B1*/ { "MSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2B2*/ { "CMSG_SERVER_BROADCAST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2B3*/ { "CMSG_SELF_RES", STATUS_LOGGEDIN, &WorldSession::HandleSelfResOpcode },
+ /*0x2B4*/ { "SMSG_FEIGN_DEATH_RESISTED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2B5*/ { "CMSG_RUN_SCRIPT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2B6*/ { "SMSG_SCRIPT_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2B7*/ { "SMSG_DUEL_COUNTDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2B8*/ { "SMSG_AREA_TRIGGER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2B9*/ { "CMSG_TOGGLE_HELM", STATUS_LOGGEDIN, &WorldSession::HandleToggleHelmOpcode },
+ /*0x2BA*/ { "CMSG_TOGGLE_CLOAK", STATUS_LOGGEDIN, &WorldSession::HandleToggleCloakOpcode },
+ /*0x2BB*/ { "SMSG_MEETINGSTONE_JOINFAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2BC*/ { "SMSG_PLAYER_SKINNED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2BD*/ { "SMSG_DURABILITY_DAMAGE_DEATH", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2BE*/ { "CMSG_SET_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2BF*/ { "CMSG_SET_ACTIONBAR_TOGGLES", STATUS_AUTHED, &WorldSession::HandleSetActionBar },
+ /*0x2C0*/ { "UMSG_DELETE_GUILD_CHARTER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2C1*/ { "MSG_PETITION_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetitionRenameOpcode },
+ /*0x2C2*/ { "SMSG_INIT_WORLD_STATES", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2C3*/ { "SMSG_UPDATE_WORLD_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2C4*/ { "CMSG_ITEM_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemNameQueryOpcode },
+ /*0x2C5*/ { "SMSG_ITEM_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2C6*/ { "SMSG_PET_ACTION_FEEDBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2C7*/ { "CMSG_CHAR_RENAME", STATUS_AUTHED, &WorldSession::HandleChangePlayerNameOpcode },
+ /*0x2C8*/ { "SMSG_CHAR_RENAME", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2C9*/ { "CMSG_MOVE_SPLINE_DONE", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNextDestinationOpcode },
+ /*0x2CA*/ { "CMSG_MOVE_FALL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x2CB*/ { "SMSG_INSTANCE_SAVE_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2CC*/ { "SMSG_RAID_INSTANCE_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2CD*/ { "CMSG_REQUEST_RAID_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestRaidInfoOpcode },
+ /*0x2CE*/ { "CMSG_MOVE_TIME_SKIPPED", STATUS_LOGGEDIN, &WorldSession::HandleMoveTimeSkippedOpcode },
+ /*0x2CF*/ { "CMSG_MOVE_FEATHER_FALL_ACK", STATUS_LOGGEDIN, &WorldSession::HandleFeatherFallAck },
+ /*0x2D0*/ { "CMSG_MOVE_WATER_WALK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveWaterWalkAck },
+ /*0x2D1*/ { "CMSG_MOVE_NOT_ACTIVE_MOVER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2D2*/ { "SMSG_PLAY_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2D3*/ { "CMSG_BATTLEFIELD_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleBattlefieldStatusOpcode },
+ /*0x2D4*/ { "SMSG_BATTLEFIELD_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2D5*/ { "CMSG_BATTLEFIELD_PORT", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPortOpcode},
+ /*0x2D6*/ { "MSG_INSPECT_HONOR_STATS", STATUS_LOGGEDIN, &WorldSession::HandleInspectHonorStatsOpcode },
+ /*0x2D7*/ { "CMSG_BATTLEMASTER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundHelloOpcode },
+ /*0x2D8*/ { "CMSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2D9*/ { "CMSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2DA*/ { "SMSG_FORCE_WALK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2DB*/ { "CMSG_FORCE_WALK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck },
+ /*0x2DC*/ { "SMSG_FORCE_SWIM_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2DD*/ { "CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck },
+ /*0x2DE*/ { "SMSG_FORCE_TURN_RATE_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2DF*/ { "CMSG_FORCE_TURN_RATE_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck },
+ /*0x2E0*/ { "MSG_PVP_LOG_DATA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPVPlogdataOpcode},
+ /*0x2E1*/ { "CMSG_LEAVE_BATTLEFIELD", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundLeaveOpcode },
+ /*0x2E2*/ { "CMSG_AREA_SPIRIT_HEALER_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueryOpcode},
+ /*0x2E3*/ { "CMSG_AREA_SPIRIT_HEALER_QUEUE", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueueOpcode},
+ /*0x2E4*/ { "SMSG_AREA_SPIRIT_HEALER_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2E5*/ { "CMSG_GM_UNTEACH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2E6*/ { "SMSG_WARDEN_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2E7*/ { "CMSG_WARDEN_DATA", STATUS_LOGGEDIN, &WorldSession::HandleWardenDataOpcode },
+ /*0x2E8*/ { "SMSG_GROUP_JOINED_BATTLEGROUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2E9*/ { "MSG_BATTLEGROUND_PLAYER_POSITIONS",STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPositionsOpcode},
+ /*0x2EA*/ { "CMSG_PET_STOP_ATTACK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2EB*/ { "SMSG_BINDER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2EC*/ { "SMSG_BATTLEGROUND_PLAYER_JOINED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2ED*/ { "SMSG_BATTLEGROUND_PLAYER_LEFT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2EE*/ { "CMSG_BATTLEMASTER_JOIN", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundJoinOpcode },
+ /*0x2EF*/ { "SMSG_ADDON_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F0*/ { "CMSG_PET_UNLEARN", STATUS_LOGGEDIN, &WorldSession::HandlePetUnlearnOpcode },
+ /*0x2F1*/ { "SMSG_PET_UNLEARN_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F2*/ { "SMSG_PARTY_MEMBER_STATS_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F3*/ { "CMSG_PET_SPELL_AUTOCAST", STATUS_LOGGEDIN, &WorldSession::HandlePetSpellAutocastOpcode },
+ /*0x2F4*/ { "SMSG_WEATHER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F5*/ { "SMSG_PLAY_TIME_WARNING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F6*/ { "SMSG_MINIGAME_SETUP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F7*/ { "SMSG_MINIGAME_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2F8*/ { "CMSG_MINIGAME_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x2F9*/ { "SMSG_MINIGAME_MOVE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2FA*/ { "SMSG_RAID_INSTANCE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2FB*/ { "SMSG_COMPRESSED_MOVES", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2FC*/ { "CMSG_GUILD_INFO_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildChangeInfoOpcode },
+ /*0x2FD*/ { "SMSG_CHAT_RESTRICTED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2FE*/ { "SMSG_SPLINE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x2FF*/ { "SMSG_SPLINE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x300*/ { "SMSG_SPLINE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x301*/ { "SMSG_SPLINE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x302*/ { "SMSG_SPLINE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x303*/ { "SMSG_SPLINE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x304*/ { "SMSG_SPLINE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x305*/ { "SMSG_SPLINE_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x306*/ { "SMSG_SPLINE_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x307*/ { "SMSG_SPLINE_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x308*/ { "SMSG_SPLINE_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x309*/ { "SMSG_SPLINE_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30A*/ { "SMSG_SPLINE_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30B*/ { "SMSG_SPLINE_MOVE_START_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30C*/ { "SMSG_SPLINE_MOVE_STOP_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30D*/ { "SMSG_SPLINE_MOVE_SET_RUN_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30E*/ { "SMSG_SPLINE_MOVE_SET_WALK_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x30F*/ { "CMSG_GM_NUKE_ACCOUNT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x310*/ { "MSG_GM_DESTROY_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x311*/ { "CMSG_GM_DESTROY_ONLINE_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x312*/ { "CMSG_ACTIVATETAXIEXPRESS", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiFarOpcode },
+ /*0x313*/ { "SMSG_SET_FACTION_ATWAR", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x314*/ { "SMSG_GAMETIMEBIAS_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x315*/ { "CMSG_DEBUG_ACTIONS_START", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x316*/ { "CMSG_DEBUG_ACTIONS_STOP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x317*/ { "CMSG_SET_FACTION_INACTIVE", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionInactiveOpcode},
+ /*0x318*/ { "CMSG_SET_WATCHED_FACTION", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionIndexOpcode},
+ /*0x319*/ { "MSG_MOVE_TIME_SKIPPED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x31A*/ { "SMSG_SPLINE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x31B*/ { "CMSG_SET_EXPLORATION_ALL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x31C*/ { "SMSG_INVALIDATE_PLAYER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x31D*/ { "CMSG_RESET_INSTANCES", STATUS_LOGGEDIN, &WorldSession::HandleResetInstancesOpcode },
+ /*0x31E*/ { "SMSG_INSTANCE_RESET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x31F*/ { "SMSG_INSTANCE_RESET_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x320*/ { "SMSG_UPDATE_LAST_INSTANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x321*/ { "MSG_RAID_TARGET_UPDATE", STATUS_LOGGEDIN, &WorldSession::HandleRaidIconTargetOpcode },
+ /*0x322*/ { "MSG_RAID_READY_CHECK", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckOpcode },
+ /*0x323*/ { "CMSG_LUA_USAGE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x324*/ { "SMSG_PET_ACTION_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x325*/ { "SMSG_PET_DISMISS_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x326*/ { "SMSG_GHOSTEE_GONE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x327*/ { "CMSG_GM_UPDATE_TICKET_STATUS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x328*/ { "SMSG_GM_TICKET_STATUS_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x329*/ { "MSG_SET_DUNGEON_DIFFICULTY", STATUS_LOGGEDIN, &WorldSession::HandleDungeonDifficultyOpcode },
+ /*0x32A*/ { "CMSG_GMSURVEY_SUBMIT", STATUS_LOGGEDIN, &WorldSession::HandleGMSurveySubmit },
+ /*0x32B*/ { "SMSG_UPDATE_INSTANCE_OWNERSHIP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x32C*/ { "CMSG_IGNORE_KNOCKBACK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x32D*/ { "SMSG_CHAT_PLAYER_AMBIGUOUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x32E*/ { "MSG_DELAY_GHOST_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x32F*/ { "SMSG_SPELLINSTAKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x330*/ { "SMSG_SPELL_UPDATE_CHAIN_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x331*/ { "CMSG_CHAT_FILTERED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x332*/ { "SMSG_EXPECTED_SPAM_RECORDS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x333*/ { "SMSG_SPELLSTEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x334*/ { "CMSG_LOTTERY_QUERY_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x335*/ { "SMSG_LOTTERY_QUERY_RESULT_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x336*/ { "CMSG_BUY_LOTTERY_TICKET_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x337*/ { "SMSG_LOTTERY_RESULT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x338*/ { "SMSG_CHARACTER_PROFILE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x339*/ { "SMSG_CHARACTER_PROFILE_REALM_CONNECTED",STATUS_NEVER,&WorldSession::Handle_ServerSide },
+ /*0x33A*/ { "SMSG_DEFENSE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x33B*/ { "SMSG_INSTANCE_DIFFICULTY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x33C*/ { "MSG_GM_RESETINSTANCELIMIT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x33D*/ { "SMSG_MOTD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x33E*/ { "SMSG_MOVE_SET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x33F*/ { "SMSG_MOVE_UNSET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x340*/ { "CMSG_MOVE_FLIGHT_ACK_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x341*/ { "MSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x342*/ { "MSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x343*/ { "SMSG_MOVE_SET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x344*/ { "SMSG_MOVE_UNSET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x345*/ { "CMSG_MOVE_SET_CAN_FLY_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveFlyModeChangeAckOpcode},
+ /*0x346*/ { "CMSG_MOVE_SET_FLY", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x347*/ { "CMSG_SOCKET_GEMS", STATUS_LOGGEDIN, &WorldSession::HandleSocketOpcode },
+ /*0x348*/ { "CMSG_ARENA_TEAM_CREATE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x349*/ { "SMSG_ARENA_TEAM_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x34A*/ { "UMSG_UPDATE_ARENA_TEAM_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x34B*/ { "CMSG_ARENA_TEAM_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamQueryOpcode },
+ /*0x34C*/ { "SMSG_ARENA_TEAM_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x34D*/ { "CMSG_ARENA_TEAM_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRosterOpcode },
+ /*0x34E*/ { "SMSG_ARENA_TEAM_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x34F*/ { "CMSG_ARENA_TEAM_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamAddMemberOpcode },
+ /*0x350*/ { "SMSG_ARENA_TEAM_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x351*/ { "CMSG_ARENA_TEAM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteAcceptOpcode},
+ /*0x352*/ { "CMSG_ARENA_TEAM_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteDeclineOpcode},
+ /*0x353*/ { "CMSG_ARENA_TEAM_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamLeaveOpcode },
+ /*0x354*/ { "CMSG_ARENA_TEAM_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRemoveFromTeamOpcode},
+ /*0x355*/ { "CMSG_ARENA_TEAM_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamDisbandOpcode },
+ /*0x356*/ { "CMSG_ARENA_TEAM_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamPromoteToCaptainOpcode},
+ /*0x357*/ { "SMSG_ARENA_TEAM_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x358*/ { "CMSG_BATTLEMASTER_JOIN_ARENA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundArenaJoin },
+ /*0x359*/ { "MSG_MOVE_START_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x35A*/ { "MSG_MOVE_STOP_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x35C*/ { "CMSG_LFG_SET_AUTOJOIN", STATUS_AUTHED, &WorldSession::HandleLfgAutoJoinOpcode },
+ /*0x35D*/ { "CMSG_LFG_CLEAR_AUTOJOIN", STATUS_LOGGEDIN, &WorldSession::HandleLfgCancelAutoJoinOpcode },
+ /*0x35E*/ { "CMSG_LFM_SET_AUTOFILL", STATUS_AUTHED, &WorldSession::HandleLfmAutoAddMembersOpcode },
+ /*0x35F*/ { "CMSG_LFM_CLEAR_AUTOFILL", STATUS_LOGGEDIN, &WorldSession::HandleLfmCancelAutoAddmembersOpcode},
+ /*0x360*/ { "CMSG_ACCEPT_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x361*/ { "CMSG_DECLINE_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x362*/ { "CMSG_CANCEL_PENDING_LFG", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x363*/ { "CMSG_CLEAR_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLfgClearOpcode },
+ /*0x364*/ { "CMSG_CLEAR_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetNoneOpcode },
+ /*0x365*/ { "CMSG_SET_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetOpcode },
+ /*0x366*/ { "CMSG_SET_LFG_COMMENT", STATUS_LOGGEDIN, &WorldSession::HandleLfgSetCommentOpcode },
+ /*0x367*/ { "SMSG_LFG_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x368*/ { "SMSG_LFG_OTHER_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x369*/ { "SMSG_LFG_AUTOJOIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36A*/ { "SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36B*/ { "SMSG_LFG_LEADER_IS_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36C*/ { "SMSG_LFG_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36D*/ { "SMSG_LFG_UPDATE_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36E*/ { "SMSG_LFG_UPDATE_LFG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x36F*/ { "SMSG_LFG_UPDATE_QUEUED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x370*/ { "SMSG_LFG_PENDING_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x371*/ { "SMSG_LFG_PENDING_MATCH", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x372*/ { "SMSG_LFG_PENDING_MATCH_DONE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x373*/ { "SMSG_TITLE_EARNED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x374*/ { "CMSG_SET_TITLE", STATUS_LOGGEDIN, &WorldSession::HandleChooseTitleOpcode },
+ /*0x375*/ { "CMSG_CANCEL_MOUNT_AURA", STATUS_LOGGEDIN, &WorldSession::HandleDismountOpcode },
+ /*0x376*/ { "SMSG_ARENA_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x377*/ { "MSG_INSPECT_ARENA_TEAMS", STATUS_LOGGEDIN, &WorldSession::HandleInspectArenaStatsOpcode },
+ /*0x378*/ { "SMSG_DEATH_RELEASE_LOC", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x379*/ { "CMSG_CANCEL_TEMP_ENCHANTMENT", STATUS_LOGGEDIN, &WorldSession::HandleCancelTempItemEnchantmentOpcode},
+ /*0x37A*/ { "SMSG_FORCED_DEATH_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x37B*/ { "CMSG_CHEAT_SET_HONOR_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x37C*/ { "CMSG_CHEAT_SET_ARENA_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x37D*/ { "MSG_MOVE_SET_FLIGHT_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x37E*/ { "MSG_MOVE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x37F*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x380*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x381*/ { "SMSG_FORCE_FLIGHT_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x382*/ { "CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck },
+ /*0x383*/ { "SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x384*/ { "CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck },
+ /*0x385*/ { "SMSG_SPLINE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x386*/ { "SMSG_SPLINE_SET_FLIGHT_BACK_SPEED",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x387*/ { "CMSG_MAELSTROM_INVALIDATE_CACHE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x388*/ { "SMSG_FLIGHT_SPLINE_SYNC", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x389*/ { "CMSG_SET_TAXI_BENCHMARK_MODE", STATUS_AUTHED, &WorldSession::HandleSetTaxiBenchmarkOpcode },
+ /*0x38A*/ { "SMSG_JOINED_BATTLEGROUND_QUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x38B*/ { "SMSG_REALM_SPLIT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x38C*/ { "CMSG_REALM_SPLIT", STATUS_AUTHED, &WorldSession::HandleRealmStateRequestOpcode },
+ /*0x38D*/ { "CMSG_MOVE_CHNG_TRANSPORT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x38E*/ { "MSG_PARTY_ASSIGNMENT", STATUS_LOGGEDIN, &WorldSession::HandleGroupPromoteOpcode },
+ /*0x38F*/ { "SMSG_OFFER_PETITION_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x391*/ { "CMSG_TIME_SYNC_RESP", STATUS_LOGGEDIN, &WorldSession::HandleAllowMoveAckOpcode },
+ /*0x392*/ { "CMSG_SEND_LOCAL_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x393*/ { "CMSG_SEND_GENERAL_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x394*/ { "CMSG_SEND_COMBAT_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x395*/ { "CMSG_MAELSTROM_GM_SENT_MAIL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x396*/ { "SMSG_RESET_FAILED_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x397*/ { "SMSG_REAL_GROUP_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x398*/ { "SMSG_LFG_DISABLED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x399*/ { "CMSG_ACTIVE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x39A*/ { "CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x39B*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE",STATUS_NEVER,&WorldSession::Handle_ServerSide },
+ /*0x39C*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE",STATUS_NEVER,&WorldSession::Handle_ServerSide},
+ /*0x39D*/ { "SMSG_UPDATE_COMBO_POINTS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x39E*/ { "SMSG_VOICE_SESSION_ROSTER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x39F*/ { "SMSG_VOICE_SESSION_LEAVE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A0*/ { "SMSG_VOICE_SESSION_ADJUST_PRIORITY",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A1*/ { "CMSG_VOICE_SET_TALKER_MUTED_REQUEST",STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3A2*/ { "SMSG_VOICE_SET_TALKER_MUTED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A3*/ { "SMSG_INIT_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A4*/ { "SMSG_SET_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A5*/ { "SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A6*/ { "SMSG_CLEAR_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3A7*/ { "MSG_MOVE_START_DESCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes },
+ /*0x3A8*/ { "CMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3A9*/ { "SMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3AA*/ { "SMSG_SPELL_CHANCE_PROC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3AB*/ { "CMSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3AC*/ { "SMSG_DISMOUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3AD*/ { "MSG_MOVE_UPDATE_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3AE*/ { "MSG_RAID_READY_CHECK_CONFIRM", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3AF*/ { "CMSG_VOICE_SESSION_ENABLE", STATUS_AUTHED, &WorldSession::HandleVoiceSettingsOpcode },
+ /*0x3B0*/ { "SMSG_VOICE_PARENTAL_CONTROLS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3B1*/ { "CMSG_GM_WHISPER", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3B2*/ { "SMSG_GM_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3B3*/ { "MSG_GM_GEARRATING", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3B4*/ { "CMSG_COMMENTATOR_ENABLE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3B5*/ { "SMSG_COMMENTATOR_STATE_CHANGED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3B6*/ { "CMSG_COMMENTATOR_GET_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3B7*/ { "SMSG_COMMENTATOR_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3B8*/ { "CMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3B9*/ { "SMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3BA*/ { "SMSG_COMMENTATOR_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3BB*/ { "CMSG_COMMENTATOR_ENTER_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3BC*/ { "CMSG_COMMENTATOR_EXIT_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3BD*/ { "CMSG_COMMENTATOR_INSTANCE_COMMAND",STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3BE*/ { "SMSG_CLEAR_TARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3BF*/ { "CMSG_BOT_DETECTED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3C0*/ { "SMSG_CROSSED_INEBRIATION_THRESHOLD",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3C1*/ { "CMSG_CHEAT_PLAYER_LOGIN", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3C2*/ { "CMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3C3*/ { "SMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3C4*/ { "SMSG_KICK_REASON", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3C5*/ { "MSG_RAID_READY_CHECK_FINISHED", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckFinishOpcode},
+ /*0x3C6*/ { "CMSG_COMPLAIN", STATUS_LOGGEDIN, &WorldSession::HandleReportSpamOpcode },
+ /*0x3C7*/ { "SMSG_COMPLAIN_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3C8*/ { "SMSG_FEATURE_SYSTEM_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3C9*/ { "CMSG_GM_SHOW_COMPLAINTS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CA*/ { "CMSG_GM_UNSQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CB*/ { "CMSG_CHANNEL_SILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CC*/ { "CMSG_CHANNEL_SILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CD*/ { "CMSG_CHANNEL_UNSILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CE*/ { "CMSG_CHANNEL_UNSILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3CF*/ { "CMSG_TARGET_CAST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3D0*/ { "CMSG_TARGET_SCRIPT_CAST", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3D1*/ { "CMSG_CHANNEL_DISPLAY_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelRosterQuery },
+ /*0x3D2*/ { "CMSG_SET_ACTIVE_VOICE_CHANNEL", STATUS_AUTHED, &WorldSession::HandleChannelVoiceChatQuery },
+ /*0x3D3*/ { "CMSG_GET_CHANNEL_MEMBER_COUNT", STATUS_LOGGEDIN, &WorldSession::HandleChannelInfoQuery },
+ /*0x3D4*/ { "SMSG_CHANNEL_MEMBER_COUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3D5*/ { "CMSG_CHANNEL_VOICE_ON", STATUS_LOGGEDIN, &WorldSession::HandleChannelEnableVoiceOpcode },
+ /*0x3D6*/ { "CMSG_CHANNEL_VOICE_OFF", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3D7*/ { "CMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3D8*/ { "SMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3D9*/ { "SMSG_AVAILABLE_VOICE_CHANNEL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3DA*/ { "CMSG_ADD_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3DB*/ { "CMSG_DEL_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3DC*/ { "CMSG_PARTY_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3DD*/ { "CMSG_PARTY_UNSILENCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3DE*/ { "MSG_NOTIFY_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3DF*/ { "SMSG_COMSAT_RECONNECT_TRY", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3E0*/ { "SMSG_COMSAT_DISCONNECT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3E1*/ { "SMSG_COMSAT_CONNECT_FAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3E2*/ { "SMSG_VOICE_CHAT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3E3*/ { "CMSG_REPORT_PVP_AFK", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundReportAFK },
+ /*0x3E4*/ { "CMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3E5*/ { "CMSG_GUILD_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankQuery },
+ /*0x3E6*/ { "CMSG_GUILD_BANK_QUERY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabColon },
+ /*0x3E7*/ { "SMSG_GUILD_BANK_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3E8*/ { "CMSG_GUILD_BANK_SWAP_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDepositItem },
+ /*0x3E9*/ { "CMSG_GUILD_BANK_BUY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankBuyTab },
+ /*0x3EA*/ { "CMSG_GUILD_BANK_UPDATE_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankModifyTab },
+ /*0x3EB*/ { "CMSG_GUILD_BANK_DEPOSIT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDeposit },
+ /*0x3EC*/ { "CMSG_GUILD_BANK_WITHDRAW_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankWithdraw },
+ /*0x3ED*/ { "MSG_GUILD_BANK_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankLog },
+ /*0x3EE*/ { "CMSG_SET_CHANNEL_WATCH", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoinNotify },
+ /*0x3EF*/ { "SMSG_USERLIST_ADD", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F0*/ { "SMSG_USERLIST_REMOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F1*/ { "SMSG_USERLIST_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F2*/ { "CMSG_CLEAR_CHANNEL_WATCH", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3F3*/ { "SMSG_INSPECT_TALENT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F4*/ { "SMSG_GOGOGO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F5*/ { "SMSG_ECHO_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F6*/ { "CMSG_SET_TITLE_SUFFIX", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3F7*/ { "CMSG_SPELLCLICK", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3F8*/ { "SMSG_LOOT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3F9*/ { "CMSG_GM_CHARACTER_RESTORE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3FA*/ { "CMSG_GM_CHARACTER_SAVE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x3FB*/ { "SMSG_VOICESESSION_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x3FC*/ { "MSG_GUILD_PERMISSIONS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetRights },
+ /*0x3FD*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetMoneyAmount },
+ /*0x3FE*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogOpcode },
+ /*0x3FF*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x400*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x401*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x402*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x403*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK",STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x404*/ { "CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_NULL },
+ /*0x405*/ { "SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_ServerSide },
+ /*0x406*/ { "CMSG_KEEP_ALIVE", STATUS_NEVER, &WorldSession::Handle_EarlyProccess },
+ /*0x407*/ { "SMSG_RAID_READY_CHECK_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x408*/ { "CMSG_OPT_OUT_OF_LOOT", STATUS_AUTHED, &WorldSession::HandleGroupPassOnLootOpcode },
+ /*0x409*/ { "MSG_QUERY_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabText },
+ /*0x40A*/ { "CMSG_SET_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankSetTabText },
+ /*0x40B*/ { "CMSG_SET_GRANTABLE_LEVELS", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x40C*/ { "CMSG_GRANT_LEVEL", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x40D*/ { "CMSG_REFER_A_FRIEND", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x40E*/ { "MSG_GM_CHANGE_ARENA_RATING", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x40F*/ { "CMSG_DECLINE_CHANNEL_INVITE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x410*/ { "CMSG_GROUPACTION_THROTTLED", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x411*/ { "SMSG_OVERRIDE_LIGHT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x412*/ { "SMSG_TOTEM_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x413*/ { "CMSG_TOTEM_DESTROYED", STATUS_LOGGEDIN, &WorldSession::HandleTotemDestroy },
+ /*0x414*/ { "CMSG_EXPIRE_RAID_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x415*/ { "CMSG_NO_SPELL_VARIANCE", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x416*/ { "CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY",STATUS_LOGGEDIN,&WorldSession::HandleQuestgiverStatusQueryMultipleOpcode},
+ /*0x417*/ { "SMSG_QUESTGIVER_STATUS_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x418*/ { "CMSG_SET_PLAYER_DECLINED_NAMES", STATUS_AUTHED, &WorldSession::HandleDeclinedPlayerNameOpcode },
+ /*0x419*/ { "SMSG_SET_PLAYER_DECLINED_NAMES_RESULT",STATUS_NEVER,&WorldSession::Handle_ServerSide },
+ /*0x41A*/ { "CMSG_QUERY_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x41B*/ { "CMSG_CLEAR_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x41C*/ { "SMSG_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x41D*/ { "SMSG_SEND_UNLEARN_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x41E*/ { "SMSG_PROPOSE_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x41F*/ { "CMSG_ACCEPT_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_NULL },
+ /*0x420*/ { "SMSG_REFER_A_FRIEND_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x421*/ { "SMSG_SPLINE_MOVE_SET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x422*/ { "SMSG_SPLINE_MOVE_UNSET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+ /*0x423*/ { "SMSG_SUMMON_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide },
+};
diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h
new file mode 100644
index 00000000000..423fad2351f
--- /dev/null
+++ b/src/game/Opcodes.h
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w
+/// @{
+/// \file
+
+#ifndef _OPCODES_H
+#define _OPCODES_H
+
+#include "Common.h"
+
+/// List of Opcodes
+enum Opcodes
+{
+ MSG_NULL_ACTION = 0x000,
+ CMSG_BOOTME = 0x001,
+ CMSG_DBLOOKUP = 0x002,
+ SMSG_DBLOOKUP = 0x003,
+ CMSG_QUERY_OBJECT_POSITION = 0x004,
+ SMSG_QUERY_OBJECT_POSITION = 0x005,
+ CMSG_QUERY_OBJECT_ROTATION = 0x006,
+ SMSG_QUERY_OBJECT_ROTATION = 0x007,
+ CMSG_WORLD_TELEPORT = 0x008,
+ CMSG_TELEPORT_TO_UNIT = 0x009,
+ CMSG_ZONE_MAP = 0x00A,
+ SMSG_ZONE_MAP = 0x00B,
+ CMSG_DEBUG_CHANGECELLZONE = 0x00C,
+ CMSG_EMBLAZON_TABARD_OBSOLETE = 0x00D,
+ CMSG_UNEMBLAZON_TABARD_OBSOLETE = 0x00E,
+ CMSG_RECHARGE = 0x00F,
+ CMSG_LEARN_SPELL = 0x010,
+ CMSG_CREATEMONSTER = 0x011,
+ CMSG_DESTROYMONSTER = 0x012,
+ CMSG_CREATEITEM = 0x013,
+ CMSG_CREATEGAMEOBJECT = 0x014,
+ SMSG_CHECK_FOR_BOTS = 0x015,
+ CMSG_MAKEMONSTERATTACKGUID = 0x016,
+ CMSG_BOT_DETECTED2 = 0x017,
+ CMSG_FORCEACTION = 0x018,
+ CMSG_FORCEACTIONONOTHER = 0x019,
+ CMSG_FORCEACTIONSHOW = 0x01A,
+ SMSG_FORCEACTIONSHOW = 0x01B,
+ CMSG_PETGODMODE = 0x01C,
+ SMSG_PETGODMODE = 0x01D,
+ SMSG_DEBUGINFOSPELLMISS_OBSOLETE = 0x01E,
+ CMSG_WEATHER_SPEED_CHEAT = 0x01F,
+ CMSG_UNDRESSPLAYER = 0x020,
+ CMSG_BEASTMASTER = 0x021,
+ CMSG_GODMODE = 0x022,
+ SMSG_GODMODE = 0x023,
+ CMSG_CHEAT_SETMONEY = 0x024,
+ CMSG_LEVEL_CHEAT = 0x025,
+ CMSG_PET_LEVEL_CHEAT = 0x026,
+ CMSG_SET_WORLDSTATE = 0x027,
+ CMSG_COOLDOWN_CHEAT = 0x028,
+ CMSG_USE_SKILL_CHEAT = 0x029,
+ CMSG_FLAG_QUEST = 0x02A,
+ CMSG_FLAG_QUEST_FINISH = 0x02B,
+ CMSG_CLEAR_QUEST = 0x02C,
+ CMSG_SEND_EVENT = 0x02D,
+ CMSG_DEBUG_AISTATE = 0x02E,
+ SMSG_DEBUG_AISTATE = 0x02F,
+ CMSG_DISABLE_PVP_CHEAT = 0x030,
+ CMSG_ADVANCE_SPAWN_TIME = 0x031,
+ CMSG_PVP_PORT_OBSOLETE = 0x032,
+ CMSG_AUTH_SRP6_BEGIN = 0x033,
+ CMSG_AUTH_SRP6_PROOF = 0x034,
+ CMSG_AUTH_SRP6_RECODE = 0x035,
+ CMSG_CHAR_CREATE = 0x036,
+ CMSG_CHAR_ENUM = 0x037,
+ CMSG_CHAR_DELETE = 0x038,
+ SMSG_AUTH_SRP6_RESPONSE = 0x039,
+ SMSG_CHAR_CREATE = 0x03A,
+ SMSG_CHAR_ENUM = 0x03B,
+ SMSG_CHAR_DELETE = 0x03C,
+ CMSG_PLAYER_LOGIN = 0x03D,
+ SMSG_NEW_WORLD = 0x03E,
+ SMSG_TRANSFER_PENDING = 0x03F,
+ SMSG_TRANSFER_ABORTED = 0x040,
+ SMSG_CHARACTER_LOGIN_FAILED = 0x041,
+ SMSG_LOGIN_SETTIMESPEED = 0x042,
+ SMSG_GAMETIME_UPDATE = 0x043,
+ CMSG_GAMETIME_SET = 0x044,
+ SMSG_GAMETIME_SET = 0x045,
+ CMSG_GAMESPEED_SET = 0x046,
+ SMSG_GAMESPEED_SET = 0x047,
+ CMSG_SERVERTIME = 0x048,
+ SMSG_SERVERTIME = 0x049,
+ CMSG_PLAYER_LOGOUT = 0x04A,
+ CMSG_LOGOUT_REQUEST = 0x04B,
+ SMSG_LOGOUT_RESPONSE = 0x04C,
+ SMSG_LOGOUT_COMPLETE = 0x04D,
+ CMSG_LOGOUT_CANCEL = 0x04E,
+ SMSG_LOGOUT_CANCEL_ACK = 0x04F,
+ CMSG_NAME_QUERY = 0x050,
+ SMSG_NAME_QUERY_RESPONSE = 0x051,
+ CMSG_PET_NAME_QUERY = 0x052,
+ SMSG_PET_NAME_QUERY_RESPONSE = 0x053,
+ CMSG_GUILD_QUERY = 0x054,
+ SMSG_GUILD_QUERY_RESPONSE = 0x055,
+ CMSG_ITEM_QUERY_SINGLE = 0x056,
+ CMSG_ITEM_QUERY_MULTIPLE = 0x057,
+ SMSG_ITEM_QUERY_SINGLE_RESPONSE = 0x058,
+ SMSG_ITEM_QUERY_MULTIPLE_RESPONSE = 0x059,
+ CMSG_PAGE_TEXT_QUERY = 0x05A,
+ SMSG_PAGE_TEXT_QUERY_RESPONSE = 0x05B,
+ CMSG_QUEST_QUERY = 0x05C,
+ SMSG_QUEST_QUERY_RESPONSE = 0x05D,
+ CMSG_GAMEOBJECT_QUERY = 0x05E,
+ SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F,
+ CMSG_CREATURE_QUERY = 0x060,
+ SMSG_CREATURE_QUERY_RESPONSE = 0x061,
+ CMSG_WHO = 0x062,
+ SMSG_WHO = 0x063,
+ CMSG_WHOIS = 0x064,
+ SMSG_WHOIS = 0x065,
+ CMSG_CONTACT_LIST = 0x066,
+ SMSG_CONTACT_LIST = 0x067,
+ SMSG_FRIEND_STATUS = 0x068,
+ CMSG_ADD_FRIEND = 0x069,
+ CMSG_DEL_FRIEND = 0x06A,
+ CMSG_SET_CONTACT_NOTES = 0x06B,
+ CMSG_ADD_IGNORE = 0x06C,
+ CMSG_DEL_IGNORE = 0x06D,
+ CMSG_GROUP_INVITE = 0x06E,
+ SMSG_GROUP_INVITE = 0x06F,
+ CMSG_GROUP_CANCEL = 0x070,
+ SMSG_GROUP_CANCEL = 0x071,
+ CMSG_GROUP_ACCEPT = 0x072,
+ CMSG_GROUP_DECLINE = 0x073,
+ SMSG_GROUP_DECLINE = 0x074,
+ CMSG_GROUP_UNINVITE = 0x075,
+ CMSG_GROUP_UNINVITE_GUID = 0x076,
+ SMSG_GROUP_UNINVITE = 0x077,
+ CMSG_GROUP_SET_LEADER = 0x078,
+ SMSG_GROUP_SET_LEADER = 0x079,
+ CMSG_LOOT_METHOD = 0x07A,
+ CMSG_GROUP_DISBAND = 0x07B,
+ SMSG_GROUP_DESTROYED = 0x07C,
+ SMSG_GROUP_LIST = 0x07D,
+ SMSG_PARTY_MEMBER_STATS = 0x07E,
+ SMSG_PARTY_COMMAND_RESULT = 0x07F,
+ UMSG_UPDATE_GROUP_MEMBERS = 0x080,
+ CMSG_GUILD_CREATE = 0x081,
+ CMSG_GUILD_INVITE = 0x082,
+ SMSG_GUILD_INVITE = 0x083,
+ CMSG_GUILD_ACCEPT = 0x084,
+ CMSG_GUILD_DECLINE = 0x085,
+ SMSG_GUILD_DECLINE = 0x086,
+ CMSG_GUILD_INFO = 0x087,
+ SMSG_GUILD_INFO = 0x088,
+ CMSG_GUILD_ROSTER = 0x089,
+ SMSG_GUILD_ROSTER = 0x08A,
+ CMSG_GUILD_PROMOTE = 0x08B,
+ CMSG_GUILD_DEMOTE = 0x08C,
+ CMSG_GUILD_LEAVE = 0x08D,
+ CMSG_GUILD_REMOVE = 0x08E,
+ CMSG_GUILD_DISBAND = 0x08F,
+ CMSG_GUILD_LEADER = 0x090,
+ CMSG_GUILD_MOTD = 0x091,
+ SMSG_GUILD_EVENT = 0x092,
+ SMSG_GUILD_COMMAND_RESULT = 0x093,
+ UMSG_UPDATE_GUILD = 0x094,
+ CMSG_MESSAGECHAT = 0x095,
+ SMSG_MESSAGECHAT = 0x096,
+ CMSG_JOIN_CHANNEL = 0x097,
+ CMSG_LEAVE_CHANNEL = 0x098,
+ SMSG_CHANNEL_NOTIFY = 0x099,
+ CMSG_CHANNEL_LIST = 0x09A,
+ SMSG_CHANNEL_LIST = 0x09B,
+ CMSG_CHANNEL_PASSWORD = 0x09C,
+ CMSG_CHANNEL_SET_OWNER = 0x09D,
+ CMSG_CHANNEL_OWNER = 0x09E,
+ CMSG_CHANNEL_MODERATOR = 0x09F,
+ CMSG_CHANNEL_UNMODERATOR = 0x0A0,
+ CMSG_CHANNEL_MUTE = 0x0A1,
+ CMSG_CHANNEL_UNMUTE = 0x0A2,
+ CMSG_CHANNEL_INVITE = 0x0A3,
+ CMSG_CHANNEL_KICK = 0x0A4,
+ CMSG_CHANNEL_BAN = 0x0A5,
+ CMSG_CHANNEL_UNBAN = 0x0A6,
+ CMSG_CHANNEL_ANNOUNCEMENTS = 0x0A7,
+ CMSG_CHANNEL_MODERATE = 0x0A8,
+ SMSG_UPDATE_OBJECT = 0x0A9,
+ SMSG_DESTROY_OBJECT = 0x0AA,
+ CMSG_USE_ITEM = 0x0AB,
+ CMSG_OPEN_ITEM = 0x0AC,
+ CMSG_READ_ITEM = 0x0AD,
+ SMSG_READ_ITEM_OK = 0x0AE,
+ SMSG_READ_ITEM_FAILED = 0x0AF,
+ SMSG_ITEM_COOLDOWN = 0x0B0,
+ CMSG_GAMEOBJ_USE = 0x0B1,
+ CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE = 0x0B2,
+ SMSG_GAMEOBJECT_CUSTOM_ANIM = 0x0B3,
+ CMSG_AREATRIGGER = 0x0B4,
+ MSG_MOVE_START_FORWARD = 0x0B5,
+ MSG_MOVE_START_BACKWARD = 0x0B6,
+ MSG_MOVE_STOP = 0x0B7,
+ MSG_MOVE_START_STRAFE_LEFT = 0x0B8,
+ MSG_MOVE_START_STRAFE_RIGHT = 0x0B9,
+ MSG_MOVE_STOP_STRAFE = 0x0BA,
+ MSG_MOVE_JUMP = 0x0BB,
+ MSG_MOVE_START_TURN_LEFT = 0x0BC,
+ MSG_MOVE_START_TURN_RIGHT = 0x0BD,
+ MSG_MOVE_STOP_TURN = 0x0BE,
+ MSG_MOVE_START_PITCH_UP = 0x0BF,
+ MSG_MOVE_START_PITCH_DOWN = 0x0C0,
+ MSG_MOVE_STOP_PITCH = 0x0C1,
+ MSG_MOVE_SET_RUN_MODE = 0x0C2,
+ MSG_MOVE_SET_WALK_MODE = 0x0C3,
+ MSG_MOVE_TOGGLE_LOGGING = 0x0C4,
+ MSG_MOVE_TELEPORT = 0x0C5,
+ MSG_MOVE_TELEPORT_CHEAT = 0x0C6,
+ MSG_MOVE_TELEPORT_ACK = 0x0C7,
+ MSG_MOVE_TOGGLE_FALL_LOGGING = 0x0C8,
+ MSG_MOVE_FALL_LAND = 0x0C9,
+ MSG_MOVE_START_SWIM = 0x0CA,
+ MSG_MOVE_STOP_SWIM = 0x0CB,
+ MSG_MOVE_SET_RUN_SPEED_CHEAT = 0x0CC,
+ MSG_MOVE_SET_RUN_SPEED = 0x0CD,
+ MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT = 0x0CE,
+ MSG_MOVE_SET_RUN_BACK_SPEED = 0x0CF,
+ MSG_MOVE_SET_WALK_SPEED_CHEAT = 0x0D0,
+ MSG_MOVE_SET_WALK_SPEED = 0x0D1,
+ MSG_MOVE_SET_SWIM_SPEED_CHEAT = 0x0D2,
+ MSG_MOVE_SET_SWIM_SPEED = 0x0D3,
+ MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT = 0x0D4,
+ MSG_MOVE_SET_SWIM_BACK_SPEED = 0x0D5,
+ MSG_MOVE_SET_ALL_SPEED_CHEAT = 0x0D6,
+ MSG_MOVE_SET_TURN_RATE_CHEAT = 0x0D7,
+ MSG_MOVE_SET_TURN_RATE = 0x0D8,
+ MSG_MOVE_TOGGLE_COLLISION_CHEAT = 0x0D9,
+ MSG_MOVE_SET_FACING = 0x0DA,
+ MSG_MOVE_SET_PITCH = 0x0DB,
+ MSG_MOVE_WORLDPORT_ACK = 0x0DC,
+ SMSG_MONSTER_MOVE = 0x0DD,
+ SMSG_MOVE_WATER_WALK = 0x0DE,
+ SMSG_MOVE_LAND_WALK = 0x0DF,
+ MSG_MOVE_SET_RAW_POSITION_ACK = 0x0E0,
+ CMSG_MOVE_SET_RAW_POSITION = 0x0E1,
+ SMSG_FORCE_RUN_SPEED_CHANGE = 0x0E2,
+ CMSG_FORCE_RUN_SPEED_CHANGE_ACK = 0x0E3,
+ SMSG_FORCE_RUN_BACK_SPEED_CHANGE = 0x0E4,
+ CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK = 0x0E5,
+ SMSG_FORCE_SWIM_SPEED_CHANGE = 0x0E6,
+ CMSG_FORCE_SWIM_SPEED_CHANGE_ACK = 0x0E7,
+ SMSG_FORCE_MOVE_ROOT = 0x0E8,
+ CMSG_FORCE_MOVE_ROOT_ACK = 0x0E9,
+ SMSG_FORCE_MOVE_UNROOT = 0x0EA,
+ CMSG_FORCE_MOVE_UNROOT_ACK = 0x0EB,
+ MSG_MOVE_ROOT = 0x0EC,
+ MSG_MOVE_UNROOT = 0x0ED,
+ MSG_MOVE_HEARTBEAT = 0x0EE,
+ SMSG_MOVE_KNOCK_BACK = 0x0EF,
+ CMSG_MOVE_KNOCK_BACK_ACK = 0x0F0,
+ MSG_MOVE_KNOCK_BACK = 0x0F1,
+ SMSG_MOVE_FEATHER_FALL = 0x0F2,
+ SMSG_MOVE_NORMAL_FALL = 0x0F3,
+ SMSG_MOVE_SET_HOVER = 0x0F4,
+ SMSG_MOVE_UNSET_HOVER = 0x0F5,
+ CMSG_MOVE_HOVER_ACK = 0x0F6,
+ MSG_MOVE_HOVER = 0x0F7,
+ CMSG_TRIGGER_CINEMATIC_CHEAT = 0x0F8,
+ CMSG_OPENING_CINEMATIC = 0x0F9,
+ SMSG_TRIGGER_CINEMATIC = 0x0FA,
+ CMSG_NEXT_CINEMATIC_CAMERA = 0x0FB,
+ CMSG_COMPLETE_CINEMATIC = 0x0FC,
+ SMSG_TUTORIAL_FLAGS = 0x0FD,
+ CMSG_TUTORIAL_FLAG = 0x0FE,
+ CMSG_TUTORIAL_CLEAR = 0x0FF,
+ CMSG_TUTORIAL_RESET = 0x100,
+ CMSG_STANDSTATECHANGE = 0x101,
+ CMSG_EMOTE = 0x102,
+ SMSG_EMOTE = 0x103,
+ CMSG_TEXT_EMOTE = 0x104,
+ SMSG_TEXT_EMOTE = 0x105,
+ CMSG_AUTOEQUIP_GROUND_ITEM = 0x106,
+ CMSG_AUTOSTORE_GROUND_ITEM = 0x107,
+ CMSG_AUTOSTORE_LOOT_ITEM = 0x108,
+ CMSG_STORE_LOOT_IN_SLOT = 0x109,
+ CMSG_AUTOEQUIP_ITEM = 0x10A,
+ CMSG_AUTOSTORE_BAG_ITEM = 0x10B,
+ CMSG_SWAP_ITEM = 0x10C,
+ CMSG_SWAP_INV_ITEM = 0x10D,
+ CMSG_SPLIT_ITEM = 0x10E,
+ CMSG_AUTOEQUIP_ITEM_SLOT = 0x10F,
+ OBSOLETE_DROP_ITEM = 0x110,
+ CMSG_DESTROYITEM = 0x111,
+ SMSG_INVENTORY_CHANGE_FAILURE = 0x112,
+ SMSG_OPEN_CONTAINER = 0x113,
+ CMSG_INSPECT = 0x114,
+ SMSG_INSPECT = 0x115,
+ CMSG_INITIATE_TRADE = 0x116,
+ CMSG_BEGIN_TRADE = 0x117,
+ CMSG_BUSY_TRADE = 0x118,
+ CMSG_IGNORE_TRADE = 0x119,
+ CMSG_ACCEPT_TRADE = 0x11A,
+ CMSG_UNACCEPT_TRADE = 0x11B,
+ CMSG_CANCEL_TRADE = 0x11C,
+ CMSG_SET_TRADE_ITEM = 0x11D,
+ CMSG_CLEAR_TRADE_ITEM = 0x11E,
+ CMSG_SET_TRADE_GOLD = 0x11F,
+ SMSG_TRADE_STATUS = 0x120,
+ SMSG_TRADE_STATUS_EXTENDED = 0x121,
+ SMSG_INITIALIZE_FACTIONS = 0x122,
+ SMSG_SET_FACTION_VISIBLE = 0x123,
+ SMSG_SET_FACTION_STANDING = 0x124,
+ CMSG_SET_FACTION_ATWAR = 0x125,
+ CMSG_SET_FACTION_CHEAT = 0x126,
+ SMSG_SET_PROFICIENCY = 0x127,
+ CMSG_SET_ACTION_BUTTON = 0x128,
+ SMSG_ACTION_BUTTONS = 0x129,
+ SMSG_INITIAL_SPELLS = 0x12A,
+ SMSG_LEARNED_SPELL = 0x12B,
+ SMSG_SUPERCEDED_SPELL = 0x12C,
+ CMSG_NEW_SPELL_SLOT = 0x12D,
+ CMSG_CAST_SPELL = 0x12E,
+ CMSG_CANCEL_CAST = 0x12F,
+ SMSG_CAST_FAILED = 0x130,
+ SMSG_SPELL_START = 0x131,
+ SMSG_SPELL_GO = 0x132,
+ SMSG_SPELL_FAILURE = 0x133,
+ SMSG_SPELL_COOLDOWN = 0x134,
+ SMSG_COOLDOWN_EVENT = 0x135,
+ CMSG_CANCEL_AURA = 0x136,
+ SMSG_UPDATE_AURA_DURATION = 0x137,
+ SMSG_PET_CAST_FAILED = 0x138,
+ MSG_CHANNEL_START = 0x139,
+ MSG_CHANNEL_UPDATE = 0x13A,
+ CMSG_CANCEL_CHANNELLING = 0x13B,
+ SMSG_AI_REACTION = 0x13C,
+ CMSG_SET_SELECTION = 0x13D,
+ CMSG_SET_TARGET_OBSOLETE = 0x13E,
+ CMSG_UNUSED = 0x13F,
+ CMSG_UNUSED2 = 0x140,
+ CMSG_ATTACKSWING = 0x141,
+ CMSG_ATTACKSTOP = 0x142,
+ SMSG_ATTACKSTART = 0x143,
+ SMSG_ATTACKSTOP = 0x144,
+ SMSG_ATTACKSWING_NOTINRANGE = 0x145,
+ SMSG_ATTACKSWING_BADFACING = 0x146,
+ SMSG_ATTACKSWING_NOTSTANDING = 0x147,
+ SMSG_ATTACKSWING_DEADTARGET = 0x148,
+ SMSG_ATTACKSWING_CANT_ATTACK = 0x149,
+ SMSG_ATTACKERSTATEUPDATE = 0x14A,
+ SMSG_VICTIMSTATEUPDATE_OBSOLETE = 0x14B,
+ SMSG_DAMAGE_DONE_OBSOLETE = 0x14C,
+ SMSG_DAMAGE_TAKEN_OBSOLETE = 0x14D,
+ SMSG_CANCEL_COMBAT = 0x14E,
+ SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE = 0x14F,
+ SMSG_SPELLHEALLOG = 0x150,
+ SMSG_SPELLENERGIZELOG = 0x151,
+ CMSG_SHEATHE_OBSOLETE = 0x152,
+ CMSG_SAVE_PLAYER = 0x153,
+ CMSG_SETDEATHBINDPOINT = 0x154,
+ SMSG_BINDPOINTUPDATE = 0x155,
+ CMSG_GETDEATHBINDZONE = 0x156,
+ SMSG_BINDZONEREPLY = 0x157,
+ SMSG_PLAYERBOUND = 0x158,
+ SMSG_CLIENT_CONTROL_UPDATE = 0x159,
+ CMSG_REPOP_REQUEST = 0x15A,
+ SMSG_RESURRECT_REQUEST = 0x15B,
+ CMSG_RESURRECT_RESPONSE = 0x15C,
+ CMSG_LOOT = 0x15D,
+ CMSG_LOOT_MONEY = 0x15E,
+ CMSG_LOOT_RELEASE = 0x15F,
+ SMSG_LOOT_RESPONSE = 0x160,
+ SMSG_LOOT_RELEASE_RESPONSE = 0x161,
+ SMSG_LOOT_REMOVED = 0x162,
+ SMSG_LOOT_MONEY_NOTIFY = 0x163,
+ SMSG_LOOT_ITEM_NOTIFY = 0x164,
+ SMSG_LOOT_CLEAR_MONEY = 0x165,
+ SMSG_ITEM_PUSH_RESULT = 0x166,
+ SMSG_DUEL_REQUESTED = 0x167,
+ SMSG_DUEL_OUTOFBOUNDS = 0x168,
+ SMSG_DUEL_INBOUNDS = 0x169,
+ SMSG_DUEL_COMPLETE = 0x16A,
+ SMSG_DUEL_WINNER = 0x16B,
+ CMSG_DUEL_ACCEPTED = 0x16C,
+ CMSG_DUEL_CANCELLED = 0x16D,
+ SMSG_MOUNTRESULT = 0x16E,
+ SMSG_DISMOUNTRESULT = 0x16F,
+ SMSG_PUREMOUNT_CANCELLED_OBSOLETE = 0x170,
+ CMSG_MOUNTSPECIAL_ANIM = 0x171,
+ SMSG_MOUNTSPECIAL_ANIM = 0x172,
+ SMSG_PET_TAME_FAILURE = 0x173,
+ CMSG_PET_SET_ACTION = 0x174,
+ CMSG_PET_ACTION = 0x175,
+ CMSG_PET_ABANDON = 0x176,
+ CMSG_PET_RENAME = 0x177,
+ SMSG_PET_NAME_INVALID = 0x178,
+ SMSG_PET_SPELLS = 0x179,
+ SMSG_PET_MODE = 0x17A,
+ CMSG_GOSSIP_HELLO = 0x17B,
+ CMSG_GOSSIP_SELECT_OPTION = 0x17C,
+ SMSG_GOSSIP_MESSAGE = 0x17D,
+ SMSG_GOSSIP_COMPLETE = 0x17E,
+ CMSG_NPC_TEXT_QUERY = 0x17F,
+ SMSG_NPC_TEXT_UPDATE = 0x180,
+ SMSG_NPC_WONT_TALK = 0x181,
+ CMSG_QUESTGIVER_STATUS_QUERY = 0x182,
+ SMSG_QUESTGIVER_STATUS = 0x183,
+ CMSG_QUESTGIVER_HELLO = 0x184,
+ SMSG_QUESTGIVER_QUEST_LIST = 0x185,
+ CMSG_QUESTGIVER_QUERY_QUEST = 0x186,
+ CMSG_QUESTGIVER_QUEST_AUTOLAUNCH = 0x187,
+ SMSG_QUESTGIVER_QUEST_DETAILS = 0x188,
+ CMSG_QUESTGIVER_ACCEPT_QUEST = 0x189,
+ CMSG_QUESTGIVER_COMPLETE_QUEST = 0x18A,
+ SMSG_QUESTGIVER_REQUEST_ITEMS = 0x18B,
+ CMSG_QUESTGIVER_REQUEST_REWARD = 0x18C,
+ SMSG_QUESTGIVER_OFFER_REWARD = 0x18D,
+ CMSG_QUESTGIVER_CHOOSE_REWARD = 0x18E,
+ SMSG_QUESTGIVER_QUEST_INVALID = 0x18F,
+ CMSG_QUESTGIVER_CANCEL = 0x190,
+ SMSG_QUESTGIVER_QUEST_COMPLETE = 0x191,
+ SMSG_QUESTGIVER_QUEST_FAILED = 0x192,
+ CMSG_QUESTLOG_SWAP_QUEST = 0x193,
+ CMSG_QUESTLOG_REMOVE_QUEST = 0x194,
+ SMSG_QUESTLOG_FULL = 0x195,
+ SMSG_QUESTUPDATE_FAILED = 0x196,
+ SMSG_QUESTUPDATE_FAILEDTIMER = 0x197,
+ SMSG_QUESTUPDATE_COMPLETE = 0x198,
+ SMSG_QUESTUPDATE_ADD_KILL = 0x199,
+ SMSG_QUESTUPDATE_ADD_ITEM = 0x19A,
+ CMSG_QUEST_CONFIRM_ACCEPT = 0x19B,
+ SMSG_QUEST_CONFIRM_ACCEPT = 0x19C,
+ CMSG_PUSHQUESTTOPARTY = 0x19D,
+ CMSG_LIST_INVENTORY = 0x19E,
+ SMSG_LIST_INVENTORY = 0x19F,
+ CMSG_SELL_ITEM = 0x1A0,
+ SMSG_SELL_ITEM = 0x1A1,
+ CMSG_BUY_ITEM = 0x1A2,
+ CMSG_BUY_ITEM_IN_SLOT = 0x1A3,
+ SMSG_BUY_ITEM = 0x1A4,
+ SMSG_BUY_FAILED = 0x1A5,
+ CMSG_TAXICLEARALLNODES = 0x1A6,
+ CMSG_TAXIENABLEALLNODES = 0x1A7,
+ CMSG_TAXISHOWNODES = 0x1A8,
+ SMSG_SHOWTAXINODES = 0x1A9,
+ CMSG_TAXINODE_STATUS_QUERY = 0x1AA,
+ SMSG_TAXINODE_STATUS = 0x1AB,
+ CMSG_TAXIQUERYAVAILABLENODES = 0x1AC,
+ CMSG_ACTIVATETAXI = 0x1AD,
+ SMSG_ACTIVATETAXIREPLY = 0x1AE,
+ SMSG_NEW_TAXI_PATH = 0x1AF,
+ CMSG_TRAINER_LIST = 0x1B0,
+ SMSG_TRAINER_LIST = 0x1B1,
+ CMSG_TRAINER_BUY_SPELL = 0x1B2,
+ SMSG_TRAINER_BUY_SUCCEEDED = 0x1B3,
+ SMSG_TRAINER_BUY_FAILED = 0x1B4,
+ CMSG_BINDER_ACTIVATE = 0x1B5,
+ SMSG_PLAYERBINDERROR = 0x1B6,
+ CMSG_BANKER_ACTIVATE = 0x1B7,
+ SMSG_SHOW_BANK = 0x1B8,
+ CMSG_BUY_BANK_SLOT = 0x1B9,
+ SMSG_BUY_BANK_SLOT_RESULT = 0x1BA,
+ CMSG_PETITION_SHOWLIST = 0x1BB,
+ SMSG_PETITION_SHOWLIST = 0x1BC,
+ CMSG_PETITION_BUY = 0x1BD,
+ CMSG_PETITION_SHOW_SIGNATURES = 0x1BE,
+ SMSG_PETITION_SHOW_SIGNATURES = 0x1BF,
+ CMSG_PETITION_SIGN = 0x1C0,
+ SMSG_PETITION_SIGN_RESULTS = 0x1C1,
+ MSG_PETITION_DECLINE = 0x1C2,
+ CMSG_OFFER_PETITION = 0x1C3,
+ CMSG_TURN_IN_PETITION = 0x1C4,
+ SMSG_TURN_IN_PETITION_RESULTS = 0x1C5,
+ CMSG_PETITION_QUERY = 0x1C6,
+ SMSG_PETITION_QUERY_RESPONSE = 0x1C7,
+ SMSG_FISH_NOT_HOOKED = 0x1C8,
+ SMSG_FISH_ESCAPED = 0x1C9,
+ CMSG_BUG = 0x1CA,
+ SMSG_NOTIFICATION = 0x1CB,
+ CMSG_PLAYED_TIME = 0x1CC,
+ SMSG_PLAYED_TIME = 0x1CD,
+ CMSG_QUERY_TIME = 0x1CE,
+ SMSG_QUERY_TIME_RESPONSE = 0x1CF,
+ SMSG_LOG_XPGAIN = 0x1D0,
+ SMSG_AURACASTLOG = 0x1D1,
+ CMSG_RECLAIM_CORPSE = 0x1D2,
+ CMSG_WRAP_ITEM = 0x1D3,
+ SMSG_LEVELUP_INFO = 0x1D4,
+ MSG_MINIMAP_PING = 0x1D5,
+ SMSG_RESISTLOG = 0x1D6,
+ SMSG_ENCHANTMENTLOG = 0x1D7,
+ CMSG_SET_SKILL_CHEAT = 0x1D8,
+ SMSG_START_MIRROR_TIMER = 0x1D9,
+ SMSG_PAUSE_MIRROR_TIMER = 0x1DA,
+ SMSG_STOP_MIRROR_TIMER = 0x1DB,
+ CMSG_PING = 0x1DC,
+ SMSG_PONG = 0x1DD,
+ SMSG_CLEAR_COOLDOWN = 0x1DE,
+ SMSG_GAMEOBJECT_PAGETEXT = 0x1DF,
+ CMSG_SETSHEATHED = 0x1E0,
+ SMSG_COOLDOWN_CHEAT = 0x1E1,
+ SMSG_SPELL_DELAYED = 0x1E2,
+ CMSG_PLAYER_MACRO_OBSOLETE = 0x1E3,
+ SMSG_PLAYER_MACRO_OBSOLETE = 0x1E4,
+ CMSG_GHOST = 0x1E5,
+ CMSG_GM_INVIS = 0x1E6,
+ SMSG_INVALID_PROMOTION_CODE = 0x1E7,
+ MSG_GM_BIND_OTHER = 0x1E8,
+ MSG_GM_SUMMON = 0x1E9,
+ SMSG_ITEM_TIME_UPDATE = 0x1EA,
+ SMSG_ITEM_ENCHANT_TIME_UPDATE = 0x1EB,
+ SMSG_AUTH_CHALLENGE = 0x1EC,
+ CMSG_AUTH_SESSION = 0x1ED,
+ SMSG_AUTH_RESPONSE = 0x1EE,
+ MSG_GM_SHOWLABEL = 0x1EF,
+ CMSG_PET_CAST_SPELL = 0x1F0,
+ MSG_SAVE_GUILD_EMBLEM = 0x1F1,
+ MSG_TABARDVENDOR_ACTIVATE = 0x1F2,
+ SMSG_PLAY_SPELL_VISUAL = 0x1F3,
+ CMSG_ZONEUPDATE = 0x1F4,
+ SMSG_PARTYKILLLOG = 0x1F5,
+ SMSG_COMPRESSED_UPDATE_OBJECT = 0x1F6,
+ SMSG_PLAY_SPELL_IMPACT = 0x1F7,
+ SMSG_EXPLORATION_EXPERIENCE = 0x1F8,
+ CMSG_GM_SET_SECURITY_GROUP = 0x1F9,
+ CMSG_GM_NUKE = 0x1FA,
+ MSG_RANDOM_ROLL = 0x1FB,
+ SMSG_ENVIRONMENTALDAMAGELOG = 0x1FC,
+ CMSG_RWHOIS_OBSOLETE = 0x1FD,
+ SMSG_RWHOIS = 0x1FE,
+ MSG_LOOKING_FOR_GROUP = 0x1FF,
+ CMSG_SET_LOOKING_FOR_GROUP = 0x200,
+ CMSG_UNLEARN_SPELL = 0x201,
+ CMSG_UNLEARN_SKILL = 0x202,
+ SMSG_REMOVED_SPELL = 0x203,
+ CMSG_DECHARGE = 0x204,
+ CMSG_GMTICKET_CREATE = 0x205,
+ SMSG_GMTICKET_CREATE = 0x206,
+ CMSG_GMTICKET_UPDATETEXT = 0x207,
+ SMSG_GMTICKET_UPDATETEXT = 0x208,
+ SMSG_ACCOUNT_DATA_TIMES = 0x209,
+ CMSG_REQUEST_ACCOUNT_DATA = 0x20A,
+ CMSG_UPDATE_ACCOUNT_DATA = 0x20B,
+ SMSG_UPDATE_ACCOUNT_DATA = 0x20C,
+ SMSG_CLEAR_FAR_SIGHT_IMMEDIATE = 0x20D,
+ SMSG_POWERGAINLOG_OBSOLETE = 0x20E,
+ CMSG_GM_TEACH = 0x20F,
+ CMSG_GM_CREATE_ITEM_TARGET = 0x210,
+ CMSG_GMTICKET_GETTICKET = 0x211,
+ SMSG_GMTICKET_GETTICKET = 0x212,
+ CMSG_UNLEARN_TALENTS = 0x213,
+ SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE = 0x214,
+ SMSG_GAMEOBJECT_DESPAWN_ANIM = 0x215,
+ MSG_CORPSE_QUERY = 0x216,
+ CMSG_GMTICKET_DELETETICKET = 0x217,
+ SMSG_GMTICKET_DELETETICKET = 0x218,
+ SMSG_CHAT_WRONG_FACTION = 0x219,
+ CMSG_GMTICKET_SYSTEMSTATUS = 0x21A,
+ SMSG_GMTICKET_SYSTEMSTATUS = 0x21B,
+ CMSG_SPIRIT_HEALER_ACTIVATE = 0x21C,
+ CMSG_SET_STAT_CHEAT = 0x21D,
+ SMSG_SET_REST_START = 0x21E,
+ CMSG_SKILL_BUY_STEP = 0x21F,
+ CMSG_SKILL_BUY_RANK = 0x220,
+ CMSG_XP_CHEAT = 0x221,
+ SMSG_SPIRIT_HEALER_CONFIRM = 0x222,
+ CMSG_CHARACTER_POINT_CHEAT = 0x223,
+ SMSG_GOSSIP_POI = 0x224,
+ CMSG_CHAT_IGNORED = 0x225,
+ CMSG_GM_VISION = 0x226,
+ CMSG_SERVER_COMMAND = 0x227,
+ CMSG_GM_SILENCE = 0x228,
+ CMSG_GM_REVEALTO = 0x229,
+ CMSG_GM_RESURRECT = 0x22A,
+ CMSG_GM_SUMMONMOB = 0x22B,
+ CMSG_GM_MOVECORPSE = 0x22C,
+ CMSG_GM_FREEZE = 0x22D,
+ CMSG_GM_UBERINVIS = 0x22E,
+ CMSG_GM_REQUEST_PLAYER_INFO = 0x22F,
+ SMSG_GM_PLAYER_INFO = 0x230,
+ CMSG_GUILD_RANK = 0x231,
+ CMSG_GUILD_ADD_RANK = 0x232,
+ CMSG_GUILD_DEL_RANK = 0x233,
+ CMSG_GUILD_SET_PUBLIC_NOTE = 0x234,
+ CMSG_GUILD_SET_OFFICER_NOTE = 0x235,
+ SMSG_LOGIN_VERIFY_WORLD = 0x236,
+ CMSG_CLEAR_EXPLORATION = 0x237,
+ CMSG_SEND_MAIL = 0x238,
+ SMSG_SEND_MAIL_RESULT = 0x239,
+ CMSG_GET_MAIL_LIST = 0x23A,
+ SMSG_MAIL_LIST_RESULT = 0x23B,
+ CMSG_BATTLEFIELD_LIST = 0x23C,
+ SMSG_BATTLEFIELD_LIST = 0x23D,
+ CMSG_BATTLEFIELD_JOIN = 0x23E,
+ SMSG_BATTLEFIELD_WIN_OBSOLETE = 0x23F,
+ SMSG_BATTLEFIELD_LOSE_OBSOLETE = 0x240,
+ CMSG_TAXICLEARNODE = 0x241,
+ CMSG_TAXIENABLENODE = 0x242,
+ CMSG_ITEM_TEXT_QUERY = 0x243,
+ SMSG_ITEM_TEXT_QUERY_RESPONSE = 0x244,
+ CMSG_MAIL_TAKE_MONEY = 0x245,
+ CMSG_MAIL_TAKE_ITEM = 0x246,
+ CMSG_MAIL_MARK_AS_READ = 0x247,
+ CMSG_MAIL_RETURN_TO_SENDER = 0x248,
+ CMSG_MAIL_DELETE = 0x249,
+ CMSG_MAIL_CREATE_TEXT_ITEM = 0x24A,
+ SMSG_SPELLLOGMISS = 0x24B,
+ SMSG_SPELLLOGEXECUTE = 0x24C,
+ SMSG_DEBUGAURAPROC = 0x24D,
+ SMSG_PERIODICAURALOG = 0x24E,
+ SMSG_SPELLDAMAGESHIELD = 0x24F,
+ SMSG_SPELLNONMELEEDAMAGELOG = 0x250,
+ CMSG_LEARN_TALENT = 0x251,
+ SMSG_RESURRECT_FAILED = 0x252,
+ CMSG_TOGGLE_PVP = 0x253,
+ SMSG_ZONE_UNDER_ATTACK = 0x254,
+ MSG_AUCTION_HELLO = 0x255,
+ CMSG_AUCTION_SELL_ITEM = 0x256,
+ CMSG_AUCTION_REMOVE_ITEM = 0x257,
+ CMSG_AUCTION_LIST_ITEMS = 0x258,
+ CMSG_AUCTION_LIST_OWNER_ITEMS = 0x259,
+ CMSG_AUCTION_PLACE_BID = 0x25A,
+ SMSG_AUCTION_COMMAND_RESULT = 0x25B,
+ SMSG_AUCTION_LIST_RESULT = 0x25C,
+ SMSG_AUCTION_OWNER_LIST_RESULT = 0x25D,
+ SMSG_AUCTION_BIDDER_NOTIFICATION = 0x25E,
+ SMSG_AUCTION_OWNER_NOTIFICATION = 0x25F,
+ SMSG_PROCRESIST = 0x260,
+ SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE = 0x261,
+ SMSG_DISPEL_FAILED = 0x262,
+ SMSG_SPELLORDAMAGE_IMMUNE = 0x263,
+ CMSG_AUCTION_LIST_BIDDER_ITEMS = 0x264,
+ SMSG_AUCTION_BIDDER_LIST_RESULT = 0x265,
+ SMSG_SET_FLAT_SPELL_MODIFIER = 0x266,
+ SMSG_SET_PCT_SPELL_MODIFIER = 0x267,
+ CMSG_SET_AMMO = 0x268,
+ SMSG_CORPSE_RECLAIM_DELAY = 0x269,
+ CMSG_SET_ACTIVE_MOVER = 0x26A,
+ CMSG_PET_CANCEL_AURA = 0x26B,
+ CMSG_PLAYER_AI_CHEAT = 0x26C,
+ CMSG_CANCEL_AUTO_REPEAT_SPELL = 0x26D,
+ MSG_GM_ACCOUNT_ONLINE = 0x26E,
+ MSG_LIST_STABLED_PETS = 0x26F,
+ CMSG_STABLE_PET = 0x270,
+ CMSG_UNSTABLE_PET = 0x271,
+ CMSG_BUY_STABLE_SLOT = 0x272,
+ SMSG_STABLE_RESULT = 0x273,
+ CMSG_STABLE_REVIVE_PET = 0x274,
+ CMSG_STABLE_SWAP_PET = 0x275,
+ MSG_QUEST_PUSH_RESULT = 0x276,
+ SMSG_PLAY_MUSIC = 0x277,
+ SMSG_PLAY_OBJECT_SOUND = 0x278,
+ CMSG_REQUEST_PET_INFO = 0x279,
+ CMSG_FAR_SIGHT = 0x27A,
+ SMSG_SPELLDISPELLOG = 0x27B,
+ SMSG_DAMAGE_CALC_LOG = 0x27C,
+ CMSG_ENABLE_DAMAGE_LOG = 0x27D,
+ CMSG_GROUP_CHANGE_SUB_GROUP = 0x27E,
+ CMSG_REQUEST_PARTY_MEMBER_STATS = 0x27F,
+ CMSG_GROUP_SWAP_SUB_GROUP = 0x280,
+ CMSG_RESET_FACTION_CHEAT = 0x281,
+ CMSG_AUTOSTORE_BANK_ITEM = 0x282,
+ CMSG_AUTOBANK_ITEM = 0x283,
+ MSG_QUERY_NEXT_MAIL_TIME = 0x284,
+ SMSG_RECEIVED_MAIL = 0x285,
+ SMSG_RAID_GROUP_ONLY = 0x286,
+ CMSG_SET_DURABILITY_CHEAT = 0x287,
+ CMSG_SET_PVP_RANK_CHEAT = 0x288,
+ CMSG_ADD_PVP_MEDAL_CHEAT = 0x289,
+ CMSG_DEL_PVP_MEDAL_CHEAT = 0x28A,
+ CMSG_SET_PVP_TITLE = 0x28B,
+ SMSG_PVP_CREDIT = 0x28C,
+ SMSG_AUCTION_REMOVED_NOTIFICATION = 0x28D,
+ CMSG_GROUP_RAID_CONVERT = 0x28E,
+ CMSG_GROUP_ASSISTANT_LEADER = 0x28F,
+ CMSG_BUYBACK_ITEM = 0x290,
+ SMSG_SERVER_MESSAGE = 0x291,
+ CMSG_MEETINGSTONE_JOIN = 0x292,
+ CMSG_MEETINGSTONE_LEAVE = 0x293,
+ CMSG_MEETINGSTONE_CHEAT = 0x294,
+ SMSG_MEETINGSTONE_SETQUEUE = 0x295,
+ CMSG_MEETINGSTONE_INFO = 0x296,
+ SMSG_MEETINGSTONE_COMPLETE = 0x297,
+ SMSG_MEETINGSTONE_IN_PROGRESS = 0x298,
+ SMSG_MEETINGSTONE_MEMBER_ADDED = 0x299,
+ CMSG_GMTICKETSYSTEM_TOGGLE = 0x29A,
+ CMSG_CANCEL_GROWTH_AURA = 0x29B,
+ SMSG_CANCEL_AUTO_REPEAT = 0x29C,
+ SMSG_STANDSTATE_UPDATE = 0x29D,
+ SMSG_LOOT_ALL_PASSED = 0x29E,
+ SMSG_LOOT_ROLL_WON = 0x29F,
+ CMSG_LOOT_ROLL = 0x2A0,
+ SMSG_LOOT_START_ROLL = 0x2A1,
+ SMSG_LOOT_ROLL = 0x2A2,
+ CMSG_LOOT_MASTER_GIVE = 0x2A3,
+ SMSG_LOOT_MASTER_LIST = 0x2A4,
+ SMSG_SET_FORCED_REACTIONS = 0x2A5,
+ SMSG_SPELL_FAILED_OTHER = 0x2A6,
+ SMSG_GAMEOBJECT_RESET_STATE = 0x2A7,
+ CMSG_REPAIR_ITEM = 0x2A8,
+ SMSG_CHAT_PLAYER_NOT_FOUND = 0x2A9,
+ MSG_TALENT_WIPE_CONFIRM = 0x2AA,
+ SMSG_SUMMON_REQUEST = 0x2AB,
+ CMSG_SUMMON_RESPONSE = 0x2AC,
+ MSG_MOVE_TOGGLE_GRAVITY_CHEAT = 0x2AD,
+ SMSG_MONSTER_MOVE_TRANSPORT = 0x2AE,
+ SMSG_PET_BROKEN = 0x2AF,
+ MSG_MOVE_FEATHER_FALL = 0x2B0,
+ MSG_MOVE_WATER_WALK = 0x2B1,
+ CMSG_SERVER_BROADCAST = 0x2B2,
+ CMSG_SELF_RES = 0x2B3,
+ SMSG_FEIGN_DEATH_RESISTED = 0x2B4,
+ CMSG_RUN_SCRIPT = 0x2B5,
+ SMSG_SCRIPT_MESSAGE = 0x2B6,
+ SMSG_DUEL_COUNTDOWN = 0x2B7,
+ SMSG_AREA_TRIGGER_MESSAGE = 0x2B8,
+ CMSG_TOGGLE_HELM = 0x2B9,
+ CMSG_TOGGLE_CLOAK = 0x2BA,
+ SMSG_MEETINGSTONE_JOINFAILED = 0x2BB,
+ SMSG_PLAYER_SKINNED = 0x2BC,
+ SMSG_DURABILITY_DAMAGE_DEATH = 0x2BD,
+ CMSG_SET_EXPLORATION = 0x2BE,
+ CMSG_SET_ACTIONBAR_TOGGLES = 0x2BF,
+ UMSG_DELETE_GUILD_CHARTER = 0x2C0,
+ MSG_PETITION_RENAME = 0x2C1,
+ SMSG_INIT_WORLD_STATES = 0x2C2,
+ SMSG_UPDATE_WORLD_STATE = 0x2C3,
+ CMSG_ITEM_NAME_QUERY = 0x2C4,
+ SMSG_ITEM_NAME_QUERY_RESPONSE = 0x2C5,
+ SMSG_PET_ACTION_FEEDBACK = 0x2C6,
+ CMSG_CHAR_RENAME = 0x2C7,
+ SMSG_CHAR_RENAME = 0x2C8,
+ CMSG_MOVE_SPLINE_DONE = 0x2C9,
+ CMSG_MOVE_FALL_RESET = 0x2CA,
+ SMSG_INSTANCE_SAVE_CREATED = 0x2CB,
+ SMSG_RAID_INSTANCE_INFO = 0x2CC,
+ CMSG_REQUEST_RAID_INFO = 0x2CD,
+ CMSG_MOVE_TIME_SKIPPED = 0x2CE,
+ CMSG_MOVE_FEATHER_FALL_ACK = 0x2CF,
+ CMSG_MOVE_WATER_WALK_ACK = 0x2D0,
+ CMSG_MOVE_NOT_ACTIVE_MOVER = 0x2D1,
+ SMSG_PLAY_SOUND = 0x2D2,
+ CMSG_BATTLEFIELD_STATUS = 0x2D3,
+ SMSG_BATTLEFIELD_STATUS = 0x2D4,
+ CMSG_BATTLEFIELD_PORT = 0x2D5,
+ MSG_INSPECT_HONOR_STATS = 0x2D6,
+ CMSG_BATTLEMASTER_HELLO = 0x2D7,
+ CMSG_MOVE_START_SWIM_CHEAT = 0x2D8,
+ CMSG_MOVE_STOP_SWIM_CHEAT = 0x2D9,
+ SMSG_FORCE_WALK_SPEED_CHANGE = 0x2DA,
+ CMSG_FORCE_WALK_SPEED_CHANGE_ACK = 0x2DB,
+ SMSG_FORCE_SWIM_BACK_SPEED_CHANGE = 0x2DC,
+ CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK = 0x2DD,
+ SMSG_FORCE_TURN_RATE_CHANGE = 0x2DE,
+ CMSG_FORCE_TURN_RATE_CHANGE_ACK = 0x2DF,
+ MSG_PVP_LOG_DATA = 0x2E0,
+ CMSG_LEAVE_BATTLEFIELD = 0x2E1,
+ CMSG_AREA_SPIRIT_HEALER_QUERY = 0x2E2,
+ CMSG_AREA_SPIRIT_HEALER_QUEUE = 0x2E3,
+ SMSG_AREA_SPIRIT_HEALER_TIME = 0x2E4,
+ CMSG_GM_UNTEACH = 0x2E5,
+ SMSG_WARDEN_DATA = 0x2E6,
+ CMSG_WARDEN_DATA = 0x2E7,
+ SMSG_GROUP_JOINED_BATTLEGROUND = 0x2E8,
+ MSG_BATTLEGROUND_PLAYER_POSITIONS = 0x2E9,
+ CMSG_PET_STOP_ATTACK = 0x2EA,
+ SMSG_BINDER_CONFIRM = 0x2EB,
+ SMSG_BATTLEGROUND_PLAYER_JOINED = 0x2EC,
+ SMSG_BATTLEGROUND_PLAYER_LEFT = 0x2ED,
+ CMSG_BATTLEMASTER_JOIN = 0x2EE,
+ SMSG_ADDON_INFO = 0x2EF,
+ CMSG_PET_UNLEARN = 0x2F0,
+ SMSG_PET_UNLEARN_CONFIRM = 0x2F1,
+ SMSG_PARTY_MEMBER_STATS_FULL = 0x2F2,
+ CMSG_PET_SPELL_AUTOCAST = 0x2F3,
+ SMSG_WEATHER = 0x2F4,
+ SMSG_PLAY_TIME_WARNING = 0x2F5,
+ SMSG_MINIGAME_SETUP = 0x2F6,
+ SMSG_MINIGAME_STATE = 0x2F7,
+ CMSG_MINIGAME_MOVE = 0x2F8,
+ SMSG_MINIGAME_MOVE_FAILED = 0x2F9,
+ SMSG_RAID_INSTANCE_MESSAGE = 0x2FA,
+ SMSG_COMPRESSED_MOVES = 0x2FB,
+ CMSG_GUILD_INFO_TEXT = 0x2FC,
+ SMSG_CHAT_RESTRICTED = 0x2FD,
+ SMSG_SPLINE_SET_RUN_SPEED = 0x2FE,
+ SMSG_SPLINE_SET_RUN_BACK_SPEED = 0x2FF,
+ SMSG_SPLINE_SET_SWIM_SPEED = 0x300,
+ SMSG_SPLINE_SET_WALK_SPEED = 0x301,
+ SMSG_SPLINE_SET_SWIM_BACK_SPEED = 0x302,
+ SMSG_SPLINE_SET_TURN_RATE = 0x303,
+ SMSG_SPLINE_MOVE_UNROOT = 0x304,
+ SMSG_SPLINE_MOVE_FEATHER_FALL = 0x305,
+ SMSG_SPLINE_MOVE_NORMAL_FALL = 0x306,
+ SMSG_SPLINE_MOVE_SET_HOVER = 0x307,
+ SMSG_SPLINE_MOVE_UNSET_HOVER = 0x308,
+ SMSG_SPLINE_MOVE_WATER_WALK = 0x309,
+ SMSG_SPLINE_MOVE_LAND_WALK = 0x30A,
+ SMSG_SPLINE_MOVE_START_SWIM = 0x30B,
+ SMSG_SPLINE_MOVE_STOP_SWIM = 0x30C,
+ SMSG_SPLINE_MOVE_SET_RUN_MODE = 0x30D,
+ SMSG_SPLINE_MOVE_SET_WALK_MODE = 0x30E,
+ CMSG_GM_NUKE_ACCOUNT = 0x30F,
+ MSG_GM_DESTROY_CORPSE = 0x310,
+ CMSG_GM_DESTROY_ONLINE_CORPSE = 0x311,
+ CMSG_ACTIVATETAXIEXPRESS = 0x312,
+ SMSG_SET_FACTION_ATWAR = 0x313,
+ SMSG_GAMETIMEBIAS_SET = 0x314,
+ CMSG_DEBUG_ACTIONS_START = 0x315,
+ CMSG_DEBUG_ACTIONS_STOP = 0x316,
+ CMSG_SET_FACTION_INACTIVE = 0x317,
+ CMSG_SET_WATCHED_FACTION = 0x318,
+ MSG_MOVE_TIME_SKIPPED = 0x319,
+ SMSG_SPLINE_MOVE_ROOT = 0x31A,
+ CMSG_SET_EXPLORATION_ALL = 0x31B,
+ SMSG_INVALIDATE_PLAYER = 0x31C,
+ CMSG_RESET_INSTANCES = 0x31D,
+ SMSG_INSTANCE_RESET = 0x31E,
+ SMSG_INSTANCE_RESET_FAILED = 0x31F,
+ SMSG_UPDATE_LAST_INSTANCE = 0x320,
+ MSG_RAID_TARGET_UPDATE = 0x321,
+ MSG_RAID_READY_CHECK = 0x322,
+ CMSG_LUA_USAGE = 0x323,
+ SMSG_PET_ACTION_SOUND = 0x324,
+ SMSG_PET_DISMISS_SOUND = 0x325,
+ SMSG_GHOSTEE_GONE = 0x326,
+ CMSG_GM_UPDATE_TICKET_STATUS = 0x327,
+ SMSG_GM_TICKET_STATUS_UPDATE = 0x328,
+ MSG_SET_DUNGEON_DIFFICULTY = 0x329,
+ CMSG_GMSURVEY_SUBMIT = 0x32A,
+ SMSG_UPDATE_INSTANCE_OWNERSHIP = 0x32B,
+ CMSG_IGNORE_KNOCKBACK_CHEAT = 0x32C,
+ SMSG_CHAT_PLAYER_AMBIGUOUS = 0x32D,
+ MSG_DELAY_GHOST_TELEPORT = 0x32E,
+ SMSG_SPELLINSTAKILLLOG = 0x32F,
+ SMSG_SPELL_UPDATE_CHAIN_TARGETS = 0x330,
+ CMSG_CHAT_FILTERED = 0x331,
+ SMSG_EXPECTED_SPAM_RECORDS = 0x332,
+ SMSG_SPELLSTEALLOG = 0x333,
+ CMSG_LOTTERY_QUERY_OBSOLETE = 0x334,
+ SMSG_LOTTERY_QUERY_RESULT_OBSOLETE = 0x335,
+ CMSG_BUY_LOTTERY_TICKET_OBSOLETE = 0x336,
+ SMSG_LOTTERY_RESULT_OBSOLETE = 0x337,
+ SMSG_CHARACTER_PROFILE = 0x338,
+ SMSG_CHARACTER_PROFILE_REALM_CONNECTED = 0x339,
+ SMSG_DEFENSE_MESSAGE = 0x33A,
+ SMSG_INSTANCE_DIFFICULTY = 0x33B,
+ MSG_GM_RESETINSTANCELIMIT = 0x33C,
+ SMSG_MOTD = 0x33D,
+ SMSG_MOVE_SET_FLIGHT_OBSOLETE = 0x33E,
+ SMSG_MOVE_UNSET_FLIGHT_OBSOLETE = 0x33F,
+ CMSG_MOVE_FLIGHT_ACK_OBSOLETE = 0x340,
+ MSG_MOVE_START_SWIM_CHEAT = 0x341,
+ MSG_MOVE_STOP_SWIM_CHEAT = 0x342,
+ SMSG_MOVE_SET_CAN_FLY = 0x343,
+ SMSG_MOVE_UNSET_CAN_FLY = 0x344,
+ CMSG_MOVE_SET_CAN_FLY_ACK = 0x345,
+ CMSG_MOVE_SET_FLY = 0x346,
+ CMSG_SOCKET_GEMS = 0x347,
+ CMSG_ARENA_TEAM_CREATE = 0x348,
+ SMSG_ARENA_TEAM_COMMAND_RESULT = 0x349,
+ UMSG_UPDATE_ARENA_TEAM_OBSOLETE = 0x34A,
+ CMSG_ARENA_TEAM_QUERY = 0x34B,
+ SMSG_ARENA_TEAM_QUERY_RESPONSE = 0x34C,
+ CMSG_ARENA_TEAM_ROSTER = 0x34D,
+ SMSG_ARENA_TEAM_ROSTER = 0x34E,
+ CMSG_ARENA_TEAM_INVITE = 0x34F,
+ SMSG_ARENA_TEAM_INVITE = 0x350,
+ CMSG_ARENA_TEAM_ACCEPT = 0x351,
+ CMSG_ARENA_TEAM_DECLINE = 0x352,
+ CMSG_ARENA_TEAM_LEAVE = 0x353,
+ CMSG_ARENA_TEAM_REMOVE = 0x354,
+ CMSG_ARENA_TEAM_DISBAND = 0x355,
+ CMSG_ARENA_TEAM_LEADER = 0x356,
+ SMSG_ARENA_TEAM_EVENT = 0x357,
+ CMSG_BATTLEMASTER_JOIN_ARENA = 0x358,
+ MSG_MOVE_START_ASCEND = 0x359,
+ MSG_MOVE_STOP_ASCEND = 0x35A,
+ SMSG_ARENA_TEAM_STATS = 0x35B,
+ CMSG_LFG_SET_AUTOJOIN = 0x35C,
+ CMSG_LFG_CLEAR_AUTOJOIN = 0x35D,
+ CMSG_LFM_SET_AUTOFILL = 0x35E,
+ CMSG_LFM_CLEAR_AUTOFILL = 0x35F,
+ CMSG_ACCEPT_LFG_MATCH = 0x360,
+ CMSG_DECLINE_LFG_MATCH = 0x361,
+ CMSG_CANCEL_PENDING_LFG = 0x362,
+ CMSG_CLEAR_LOOKING_FOR_GROUP = 0x363,
+ CMSG_CLEAR_LOOKING_FOR_MORE = 0x364,
+ CMSG_SET_LOOKING_FOR_MORE = 0x365,
+ CMSG_SET_LFG_COMMENT = 0x366,
+ SMSG_LFG_TIMEDOUT = 0x367,
+ SMSG_LFG_OTHER_TIMEDOUT = 0x368,
+ SMSG_LFG_AUTOJOIN_FAILED = 0x369,
+ SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER = 0x36A,
+ SMSG_LFG_LEADER_IS_LFM = 0x36B,
+ SMSG_LFG_UPDATE = 0x36C,
+ SMSG_LFG_UPDATE_LFM = 0x36D,
+ SMSG_LFG_UPDATE_LFG = 0x36E,
+ SMSG_LFG_UPDATE_QUEUED = 0x36F,
+ SMSG_LFG_PENDING_INVITE = 0x370,
+ SMSG_LFG_PENDING_MATCH = 0x371,
+ SMSG_LFG_PENDING_MATCH_DONE = 0x372,
+ SMSG_TITLE_EARNED = 0x373,
+ CMSG_SET_TITLE = 0x374,
+ CMSG_CANCEL_MOUNT_AURA = 0x375,
+ SMSG_ARENA_ERROR = 0x376,
+ MSG_INSPECT_ARENA_TEAMS = 0x377,
+ SMSG_DEATH_RELEASE_LOC = 0x378,
+ CMSG_CANCEL_TEMP_ENCHANTMENT = 0x379,
+ SMSG_FORCED_DEATH_UPDATE = 0x37A,
+ CMSG_CHEAT_SET_HONOR_CURRENCY = 0x37B,
+ CMSG_CHEAT_SET_ARENA_CURRENCY = 0x37C,
+ MSG_MOVE_SET_FLIGHT_SPEED_CHEAT = 0x37D,
+ MSG_MOVE_SET_FLIGHT_SPEED = 0x37E,
+ MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT = 0x37F,
+ MSG_MOVE_SET_FLIGHT_BACK_SPEED = 0x380,
+ SMSG_FORCE_FLIGHT_SPEED_CHANGE = 0x381,
+ CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK = 0x382,
+ SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE = 0x383,
+ CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK = 0x384,
+ SMSG_SPLINE_SET_FLIGHT_SPEED = 0x385,
+ SMSG_SPLINE_SET_FLIGHT_BACK_SPEED = 0x386,
+ CMSG_MAELSTROM_INVALIDATE_CACHE = 0x387,
+ SMSG_FLIGHT_SPLINE_SYNC = 0x388,
+ CMSG_SET_TAXI_BENCHMARK_MODE = 0x389,
+ SMSG_JOINED_BATTLEGROUND_QUEUE = 0x38A,
+ SMSG_REALM_SPLIT = 0x38B,
+ CMSG_REALM_SPLIT = 0x38C,
+ CMSG_MOVE_CHNG_TRANSPORT = 0x38D,
+ MSG_PARTY_ASSIGNMENT = 0x38E,
+ SMSG_OFFER_PETITION_ERROR = 0x38F,
+ SMSG_TIME_SYNC_REQ = 0x390,
+ CMSG_TIME_SYNC_RESP = 0x391,
+ CMSG_SEND_LOCAL_EVENT = 0x392,
+ CMSG_SEND_GENERAL_TRIGGER = 0x393,
+ CMSG_SEND_COMBAT_TRIGGER = 0x394,
+ CMSG_MAELSTROM_GM_SENT_MAIL = 0x395,
+ SMSG_RESET_FAILED_NOTIFY = 0x396,
+ SMSG_REAL_GROUP_UPDATE = 0x397,
+ SMSG_LFG_DISABLED = 0x398,
+ CMSG_ACTIVE_PVP_CHEAT = 0x399,
+ CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY = 0x39A,
+ SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE = 0x39B,
+ SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE = 0x39C,
+ SMSG_UPDATE_COMBO_POINTS = 0x39D,
+ SMSG_VOICE_SESSION_ROSTER_UPDATE = 0x39E,
+ SMSG_VOICE_SESSION_LEAVE = 0x39F,
+ SMSG_VOICE_SESSION_ADJUST_PRIORITY = 0x3A0,
+ CMSG_VOICE_SET_TALKER_MUTED_REQUEST = 0x3A1,
+ SMSG_VOICE_SET_TALKER_MUTED = 0x3A2,
+ SMSG_INIT_EXTRA_AURA_INFO = 0x3A3,
+ SMSG_SET_EXTRA_AURA_INFO = 0x3A4,
+ SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE = 0x3A5,
+ SMSG_CLEAR_EXTRA_AURA_INFO = 0x3A6,
+ MSG_MOVE_START_DESCEND = 0x3A7,
+ CMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A8,
+ SMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A9,
+ SMSG_SPELL_CHANCE_PROC_LOG = 0x3AA,
+ CMSG_MOVE_SET_RUN_SPEED = 0x3AB,
+ SMSG_DISMOUNT = 0x3AC,
+ MSG_MOVE_UPDATE_CAN_FLY = 0x3AD,
+ MSG_RAID_READY_CHECK_CONFIRM = 0x3AE,
+ CMSG_VOICE_SESSION_ENABLE = 0x3AF,
+ SMSG_VOICE_PARENTAL_CONTROLS = 0x3B0,
+ CMSG_GM_WHISPER = 0x3B1,
+ SMSG_GM_MESSAGECHAT = 0x3B2,
+ MSG_GM_GEARRATING = 0x3B3,
+ CMSG_COMMENTATOR_ENABLE = 0x3B4,
+ SMSG_COMMENTATOR_STATE_CHANGED = 0x3B5,
+ CMSG_COMMENTATOR_GET_MAP_INFO = 0x3B6,
+ SMSG_COMMENTATOR_MAP_INFO = 0x3B7,
+ CMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B8,
+ SMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B9,
+ SMSG_COMMENTATOR_PLAYER_INFO = 0x3BA,
+ CMSG_COMMENTATOR_ENTER_INSTANCE = 0x3BB,
+ CMSG_COMMENTATOR_EXIT_INSTANCE = 0x3BC,
+ CMSG_COMMENTATOR_INSTANCE_COMMAND = 0x3BD,
+ SMSG_CLEAR_TARGET = 0x3BE,
+ CMSG_BOT_DETECTED = 0x3BF,
+ SMSG_CROSSED_INEBRIATION_THRESHOLD = 0x3C0,
+ CMSG_CHEAT_PLAYER_LOGIN = 0x3C1,
+ CMSG_CHEAT_PLAYER_LOOKUP = 0x3C2,
+ SMSG_CHEAT_PLAYER_LOOKUP = 0x3C3,
+ SMSG_KICK_REASON = 0x3C4,
+ MSG_RAID_READY_CHECK_FINISHED = 0x3C5,
+ CMSG_COMPLAIN = 0x3C6,
+ SMSG_COMPLAIN_RESULT = 0x3C7,
+ SMSG_FEATURE_SYSTEM_STATUS = 0x3C8,
+ CMSG_GM_SHOW_COMPLAINTS = 0x3C9,
+ CMSG_GM_UNSQUELCH = 0x3CA,
+ CMSG_CHANNEL_SILENCE_VOICE = 0x3CB,
+ CMSG_CHANNEL_SILENCE_ALL = 0x3CC,
+ CMSG_CHANNEL_UNSILENCE_VOICE = 0x3CD,
+ CMSG_CHANNEL_UNSILENCE_ALL = 0x3CE,
+ CMSG_TARGET_CAST = 0x3CF,
+ CMSG_TARGET_SCRIPT_CAST = 0x3D0,
+ CMSG_CHANNEL_DISPLAY_LIST = 0x3D1,
+ CMSG_SET_ACTIVE_VOICE_CHANNEL = 0x3D2,
+ CMSG_GET_CHANNEL_MEMBER_COUNT = 0x3D3,
+ SMSG_CHANNEL_MEMBER_COUNT = 0x3D4,
+ CMSG_CHANNEL_VOICE_ON = 0x3D5,
+ CMSG_CHANNEL_VOICE_OFF = 0x3D6,
+ CMSG_DEBUG_LIST_TARGETS = 0x3D7,
+ SMSG_DEBUG_LIST_TARGETS = 0x3D8,
+ SMSG_AVAILABLE_VOICE_CHANNEL = 0x3D9,
+ CMSG_ADD_VOICE_IGNORE = 0x3DA,
+ CMSG_DEL_VOICE_IGNORE = 0x3DB,
+ CMSG_PARTY_SILENCE = 0x3DC,
+ CMSG_PARTY_UNSILENCE = 0x3DD,
+ MSG_NOTIFY_PARTY_SQUELCH = 0x3DE,
+ SMSG_COMSAT_RECONNECT_TRY = 0x3DF,
+ SMSG_COMSAT_DISCONNECT = 0x3E0,
+ SMSG_COMSAT_CONNECT_FAIL = 0x3E1,
+ SMSG_VOICE_CHAT_STATUS = 0x3E2,
+ CMSG_REPORT_PVP_AFK = 0x3E3,
+ CMSG_REPORT_PVP_AFK_RESULT = 0x3E4,
+ CMSG_GUILD_BANKER_ACTIVATE = 0x3E5,
+ CMSG_GUILD_BANK_QUERY_TAB = 0x3E6,
+ SMSG_GUILD_BANK_LIST = 0x3E7,
+ CMSG_GUILD_BANK_SWAP_ITEMS = 0x3E8,
+ CMSG_GUILD_BANK_BUY_TAB = 0x3E9,
+ CMSG_GUILD_BANK_UPDATE_TAB = 0x3EA,
+ CMSG_GUILD_BANK_DEPOSIT_MONEY = 0x3EB,
+ CMSG_GUILD_BANK_WITHDRAW_MONEY = 0x3EC,
+ MSG_GUILD_BANK_LOG_QUERY = 0x3ED,
+ CMSG_SET_CHANNEL_WATCH = 0x3EE,
+ SMSG_USERLIST_ADD = 0x3EF,
+ SMSG_USERLIST_REMOVE = 0x3F0,
+ SMSG_USERLIST_UPDATE = 0x3F1,
+ CMSG_CLEAR_CHANNEL_WATCH = 0x3F2,
+ SMSG_INSPECT_TALENT = 0x3F3,
+ SMSG_GOGOGO_OBSOLETE = 0x3F4,
+ SMSG_ECHO_PARTY_SQUELCH = 0x3F5,
+ CMSG_SET_TITLE_SUFFIX = 0x3F6,
+ CMSG_SPELLCLICK = 0x3F7,
+ SMSG_LOOT_LIST = 0x3F8,
+ CMSG_GM_CHARACTER_RESTORE = 0x3F9,
+ CMSG_GM_CHARACTER_SAVE = 0x3FA,
+ SMSG_VOICESESSION_FULL = 0x3FB,
+ MSG_GUILD_PERMISSIONS = 0x3FC,
+ MSG_GUILD_BANK_MONEY_WITHDRAWN = 0x3FD,
+ MSG_GUILD_EVENT_LOG_QUERY = 0x3FE,
+ CMSG_MAELSTROM_RENAME_GUILD = 0x3FF,
+ CMSG_GET_MIRRORIMAGE_DATA = 0x400,
+ SMSG_MIRRORIMAGE_DATA = 0x401,
+ SMSG_FORCE_DISPLAY_UPDATE = 0x402,
+ SMSG_SPELL_CHANCE_RESIST_PUSHBACK = 0x403,
+ CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x404,
+ SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x405,
+ CMSG_KEEP_ALIVE = 0x406,
+ SMSG_RAID_READY_CHECK_ERROR = 0x407,
+ CMSG_OPT_OUT_OF_LOOT = 0x408,
+ MSG_QUERY_GUILD_BANK_TEXT = 0x409,
+ CMSG_SET_GUILD_BANK_TEXT = 0x40A,
+ CMSG_SET_GRANTABLE_LEVELS = 0x40B,
+ CMSG_GRANT_LEVEL = 0x40C,
+ CMSG_REFER_A_FRIEND = 0x40D,
+ MSG_GM_CHANGE_ARENA_RATING = 0x40E,
+ CMSG_DECLINE_CHANNEL_INVITE = 0x40F,
+ CMSG_GROUPACTION_THROTTLED = 0x410,
+ SMSG_OVERRIDE_LIGHT = 0x411,
+ SMSG_TOTEM_CREATED = 0x412,
+ CMSG_TOTEM_DESTROYED = 0x413,
+ CMSG_EXPIRE_RAID_INSTANCE = 0x414,
+ CMSG_NO_SPELL_VARIANCE = 0x415,
+ CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x416,
+ SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x417,
+ CMSG_SET_PLAYER_DECLINED_NAMES = 0x418,
+ SMSG_SET_PLAYER_DECLINED_NAMES_RESULT = 0x419,
+ CMSG_QUERY_SERVER_BUCK_DATA = 0x41A,
+ CMSG_CLEAR_SERVER_BUCK_DATA = 0x41B,
+ SMSG_SERVER_BUCK_DATA = 0x41C,
+ SMSG_SEND_UNLEARN_SPELLS = 0x41D,
+ SMSG_PROPOSE_LEVEL_GRANT = 0x41E,
+ CMSG_ACCEPT_LEVEL_GRANT = 0x41F,
+ SMSG_REFER_A_FRIEND_FAILURE = 0x420,
+ SMSG_SPLINE_MOVE_SET_FLYING = 0x421,
+ SMSG_SPLINE_MOVE_UNSET_FLYING = 0x422,
+ SMSG_SUMMON_CANCEL = 0x423
+};
+
+// Don't forget to change this value and add opcode name to Opcodes.cpp when you add new opcode!
+#define NUM_MSG_TYPES 0x424
+
+/// Player state
+enum SessionStatus
+{
+ STATUS_AUTHED = 0, ///< Player authenticated
+ STATUS_LOGGEDIN, ///< Player in game
+ STATUS_TRANSFER_PENDING, ///< Player transferring to another map
+ STATUS_NEVER ///< Opcode not accepted from client (deprecated or server side only)
+};
+
+class WorldSession;
+class WorldPacket;
+
+struct OpcodeHandler
+{
+ char const* name;
+ SessionStatus status;
+ void (WorldSession::*handler)(WorldPacket& recvPacket);
+};
+
+extern OpcodeHandler opcodeTable[NUM_MSG_TYPES];
+
+/// Lookup opcode name for human understandable logging
+inline const char* LookupOpcodeName(uint16 id)
+{
+ if (id >= NUM_MSG_TYPES)
+ return "Received unknown opcode, it's more than max!";
+ return opcodeTable[id].name;
+}
+#endif
+/// @}
diff --git a/src/game/Path.h b/src/game/Path.h
new file mode 100644
index 00000000000..2085e567def
--- /dev/null
+++ b/src/game/Path.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_PATH_H
+#define MANGOSSERVER_PATH_H
+
+#include "Common.h"
+#include <vector>
+
+class Path
+{
+ public:
+ struct PathNode
+ {
+ float x,y,z;
+ };
+
+ void SetLength(const unsigned int sz)
+ {
+ i_nodes.resize( sz );
+ }
+
+ unsigned int Size() const { return i_nodes.size(); }
+ bool Empty() const { return i_nodes.empty(); }
+ void Resize(unsigned int sz) { i_nodes.resize(sz); }
+ void Clear(void) { i_nodes.clear(); }
+ PathNode const* GetNodes(uint32 start = 0) const { return &i_nodes[start]; }
+ float GetTotalLength() const { return GetTotalLength(0,Size()); }
+ float GetTotalLength(uint32 start, uint32 end) const
+ {
+ float len = 0, xd, yd, zd;
+ for(unsigned int idx=start+1; idx < end; ++idx)
+ {
+ xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x;
+ yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y;
+ zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z;
+ len += sqrtf( xd*xd + yd*yd + zd*zd );
+ }
+ return len;
+ }
+
+ float GetPassedLength(uint32 curnode, float x, float y, float z)
+ {
+ float len = 0, xd, yd, zd;
+ for(unsigned int idx=1; idx < curnode; ++idx)
+ {
+ xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x;
+ yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y;
+ zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z;
+ len += sqrtf( xd*xd + yd*yd + zd*zd );
+ }
+
+ if(curnode > 0)
+ {
+ xd = x - i_nodes[curnode-1].x;
+ yd = y - i_nodes[curnode-1].y;
+ zd = z - i_nodes[curnode-1].z;
+ len += sqrtf( xd*xd + yd*yd + zd*zd );
+ }
+
+ return len;
+ }
+
+ PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; }
+ const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; }
+
+ protected:
+ std::vector<PathNode> i_nodes;
+};
+#endif
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp
new file mode 100644
index 00000000000..c80bbc8f673
--- /dev/null
+++ b/src/game/Pet.cpp
@@ -0,0 +1,1750 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Pet.h"
+#include "MapManager.h"
+#include "Formulas.h"
+#include "SpellAuras.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "Util.h"
+
+char const* petTypeSuffix[MAX_PET_TYPE] =
+{
+ "'s Minion", // SUMMON_PET
+ "'s Pet", // HUNTER_PET
+ "'s Guardian", // GUARDIAN_PET
+ "'s Companion" // MINI_PET
+};
+
+//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
+uint32 const LevelUpLoyalty[6] =
+{
+ 5500,
+ 11500,
+ 17000,
+ 23500,
+ 31000,
+ 39500,
+};
+
+uint32 const LevelStartLoyalty[6] =
+{
+ 2000,
+ 4500,
+ 7000,
+ 10000,
+ 13500,
+ 17500,
+};
+
+Pet::Pet(PetType type) : Creature()
+{
+ m_isPet = true;
+ m_name = "Pet";
+ m_petType = type;
+
+ m_removed = false;
+ m_regenTimer = 4000;
+ m_happinessTimer = 7500;
+ m_loyaltyTimer = 12000;
+ m_duration = 0;
+ m_bonusdamage = 0;
+
+ m_loyaltyPoints = 0;
+ m_TrainingPoints = 0;
+ m_resetTalentsCost = 0;
+ m_resetTalentsTime = 0;
+
+ m_auraUpdateMask = 0;
+
+ // pets always have a charminfo, even if they are not actually charmed
+ CharmInfo* charmInfo = InitCharmInfo(this);
+
+ if(type == MINI_PET) // always passive
+ charmInfo->SetReactState(REACT_PASSIVE);
+ else if(type == GUARDIAN_PET) // always aggressive
+ charmInfo->SetReactState(REACT_AGGRESSIVE);
+
+ m_spells.clear();
+ m_Auras.clear();
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+ m_autospells.clear();
+ m_declinedname = NULL;
+}
+
+Pet::~Pet()
+{
+ if(m_uint32Values) // only for fully created Object
+ {
+ for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i)
+ delete i->second;
+ ObjectAccessor::Instance().RemoveObject(this);
+ }
+
+ delete m_declinedname;
+}
+
+void Pet::AddToWorld()
+{
+ ///- Register the pet for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Unit::AddToWorld();
+}
+
+void Pet::RemoveFromWorld()
+{
+ ///- Remove the pet from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ ///- Don't call the function for Creature, normal mobs + totems go in a different storage
+ Unit::RemoveFromWorld();
+}
+
+bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current )
+{
+ uint32 ownerid = owner->GetGUIDLow();
+
+ QueryResult *result;
+
+ if(petnumber)
+ // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, 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 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid );
+ 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 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry );
+ 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 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid);
+
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ // update for case of current pet "slot = 0"
+ petentry = fields[1].GetUInt32();
+ if(!petentry)
+ {
+ delete result;
+ return false;
+ }
+
+ uint32 summon_spell_id = fields[21].GetUInt32();
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
+
+ bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
+
+ // check temporary summoned pets like mage water elemental
+ if(current && is_temporary_summoned)
+ {
+ delete result;
+ return false;
+ }
+
+ Map *map = owner->GetMap();
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+ uint32 pet_number = fields[0].GetUInt32();
+ if(!Create(guid, map, petentry, pet_number))
+ {
+ delete result;
+ return false;
+ }
+
+ float px, py, pz;
+ owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+
+ Relocate(px, py, pz, owner->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ delete result;
+ return false;
+ }
+
+ setPetType(PetType(fields[22].GetUInt8()));
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
+ SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ AIM_Initialize();
+ map->Add((Creature*)this);
+ delete result;
+ return true;
+ }
+ if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)
+ m_charmInfo->SetPetNumber(pet_number, true);
+ else
+ m_charmInfo->SetPetNumber(pet_number, false);
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID());
+ SetDisplayId(fields[3].GetUInt32());
+ SetNativeDisplayId(fields[3].GetUInt32());
+ uint32 petlevel=fields[4].GetUInt32();
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ SetName(fields[11].GetString());
+
+ switch(getPetType())
+ {
+
+ case SUMMON_PET:
+ petlevel=owner->getLevel();
+
+ SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet dismiss, cancel)
+ break;
+ case HUNTER_PET:
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32());
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(fields[12].GetBool())
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+ else
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet abandon, cancel)
+ SetTP(fields[9].GetInt32());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,fields[15].GetUInt32());
+ setPowerType(POWER_FOCUS);
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
+ }
+ InitStatsForLevel( petlevel);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
+ SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID());
+
+ m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() ));
+ m_loyaltyPoints = fields[7].GetInt32();
+
+ uint32 savedhealth = fields[13].GetUInt32();
+ uint32 savedmana = fields[14].GetUInt32();
+
+ // set current pet as current
+ if(fields[10].GetUInt32() != 0)
+ {
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.CommitTransaction();
+ }
+
+ if(!is_temporary_summoned)
+ {
+ // permanent controlled pets store state in DB
+ Tokens tokens = StrSplit(fields[16].GetString(), " ");
+
+ if(tokens.size() != 20)
+ {
+ delete result;
+ return false;
+ }
+
+ int index;
+ Tokens::iterator iter;
+ for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
+ {
+ m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
+ ++iter;
+ m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
+ }
+
+ //init teach spells
+ tokens = StrSplit(fields[17].GetString(), " ");
+ for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index)
+ {
+ uint32 tmp = atol((*iter).c_str());
+
+ ++iter;
+
+ if(tmp)
+ AddTeachSpell(tmp, atol((*iter).c_str()));
+ else
+ break;
+ }
+ }
+
+ // since last save (in seconds)
+ uint32 timediff = (time(NULL) - fields[18].GetUInt32());
+
+ delete result;
+
+ //load spells/cooldowns/auras
+ SetCanModifyStats(true);
+ _LoadAuras(timediff);
+
+ //init AB
+ if(is_temporary_summoned)
+ {
+ // Temporary summoned pets always have initial spell list at load
+ InitPetCreateSpells();
+ }
+ else
+ {
+ LearnPetPassives();
+ CastPetAuras(current);
+ }
+
+ if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
+ {
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ }
+ else
+ {
+ SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
+ SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
+ }
+
+ AIM_Initialize();
+ map->Add((Creature*)this);
+
+ // Spells should be loaded after pet is added to map, because in CanCast is check on it
+ _LoadSpells();
+ _LoadSpellCooldowns();
+
+ owner->SetPet(this); // in DB stored only full controlled creature
+ sLog.outDebug("New Pet has guid %u", GetGUIDLow());
+
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)owner)->PetSpellInitialize();
+ if(((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
+ }
+
+ if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET)
+ {
+ result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
+
+ if(result)
+ {
+ if(m_declinedname)
+ delete m_declinedname;
+
+ m_declinedname = new DeclinedName;
+ Field *fields = result->Fetch();
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ m_declinedname->name[i] = fields[i].GetCppString();
+ }
+ }
+ }
+
+ return true;
+}
+
+void Pet::SavePetToDB(PetSaveMode mode)
+{
+ if(!GetEntry())
+ return;
+
+ // save only fully controlled creature
+ if(!isControlled())
+ return;
+
+ uint32 curhealth = GetHealth();
+ uint32 curmana = GetPower(POWER_MANA);
+
+ switch(mode)
+ {
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ RemoveAllAuras();
+
+ //only alive hunter pets get auras saved, the others don't
+ if(!(getPetType() == HUNTER_PET && isAlive()))
+ m_Auras.clear();
+ }
+ default:
+ break;
+ }
+
+ _SaveSpells();
+ _SaveSpellCooldowns();
+ _SaveAuras();
+
+ switch(mode)
+ {
+ case PET_SAVE_AS_CURRENT:
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ uint32 loyalty =1;
+ if(getPetType()!=HUNTER_PET)
+ loyalty = GetLoyaltyLevel();
+
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ std::string name = m_name;
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.BeginTransaction();
+ // remove current data
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() );
+
+ // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
+ if(mode!=PET_SAVE_NOT_IN_SLOT)
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) );
+
+ // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
+ if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT))
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner );
+ // save pet
+ std::ostringstream ss;
+ ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) "
+ << "VALUES ("
+ << m_charmInfo->GetPetNumber() << ", "
+ << GetEntry() << ", "
+ << owner << ", "
+ << GetNativeDisplayId() << ", "
+ << getLevel() << ", "
+ << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
+ << uint32(m_charmInfo->GetReactState()) << ", "
+ << m_loyaltyPoints << ", "
+ << GetLoyaltyLevel() << ", "
+ << m_TrainingPoints << ", "
+ << uint32(mode) << ", '"
+ << name.c_str() << "', "
+ << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
+ << (curhealth<1?1:curhealth) << ", "
+ << curmana << ", "
+ << GetPower(POWER_HAPPINESS) << ", '";
+
+ for(uint32 i = 0; i < 10; i++)
+ ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
+ ss << "', '";
+
+ //save spells the pet can teach to it's Master
+ {
+ int i = 0;
+ for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr)
+ ss << itr->first << " " << itr->second << " ";
+ for(; i < 4; ++i)
+ ss << uint32(0) << " " << uint32(0) << " ";
+ }
+
+ ss << "', "
+ << time(NULL) << ", "
+ << uint32(m_resetTalentsCost) << ", "
+ << uint64(m_resetTalentsTime) << ", "
+ << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
+ << uint32(getPetType()) << ")";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ CharacterDatabase.CommitTransaction();
+ break;
+ }
+ case PET_SAVE_AS_DELETED:
+ {
+ RemoveAllAuras();
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ DeleteFromDB(m_charmInfo->GetPetNumber());
+ break;
+ }
+ default:
+ sLog.outError("Unknown pet save/remove mode: %d",mode);
+ }
+}
+
+void Pet::DeleteFromDB(uint32 guidlow)
+{
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
+}
+
+void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState
+{
+ Creature::setDeathState(s);
+ if(getDeathState()==CORPSE)
+ {
+ //remove summoned pet (no corpse)
+ if(getPetType()==SUMMON_PET)
+ Remove(PET_SAVE_NOT_IN_SLOT);
+ // other will despawn at corpse desppawning (Pet::Update code)
+ else
+ {
+ // pet corpse non lootable and non skinnable
+ SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 );
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ //lose happiness when died and not in BG/Arena
+ MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
+ if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND))
+ ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE);
+
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+ }
+ else if(getDeathState()==ALIVE)
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ CastPetAuras(true);
+ }
+}
+
+void Pet::Update(uint32 diff)
+{
+ if(m_removed) // pet already removed, just wait in remove queue, no updates
+ return;
+
+ switch( m_deathState )
+ {
+ case CORPSE:
+ {
+ if( m_deathTimer <= diff )
+ {
+ assert(getPetType()!=SUMMON_PET && "Must be already removed.");
+ Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
+ return;
+ }
+ break;
+ }
+ case ALIVE:
+ {
+ // unsummon pet that lost owner
+ Unit* owner = GetOwner();
+ if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID())
+ {
+ Remove(PET_SAVE_NOT_IN_SLOT, true);
+ return;
+ }
+
+ if(isControlled())
+ {
+ if( owner->GetPetGUID() != GetGUID() )
+ {
+ Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(m_duration > 0)
+ {
+ if(m_duration > diff)
+ m_duration -= diff;
+ else
+ {
+ Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(getPetType() != HUNTER_PET)
+ break;
+
+ //regenerate Focus
+ if(m_regenTimer <= diff)
+ {
+ RegenerateFocus();
+ m_regenTimer = 4000;
+ }
+ else
+ m_regenTimer -= diff;
+
+ if(m_happinessTimer <= diff)
+ {
+ LooseHappiness();
+ m_happinessTimer = 7500;
+ }
+ else
+ m_happinessTimer -= diff;
+
+ if(m_loyaltyTimer <= diff)
+ {
+ TickLoyaltyChange();
+ m_loyaltyTimer = 12000;
+ }
+ else
+ m_loyaltyTimer -= diff;
+
+ break;
+ }
+ default:
+ break;
+ }
+ Creature::Update(diff);
+}
+
+void Pet::RegenerateFocus()
+{
+ uint32 curValue = GetPower(POWER_FOCUS);
+ uint32 maxValue = GetMaxPower(POWER_FOCUS);
+
+ if (curValue >= maxValue)
+ return;
+
+ float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS);
+
+ AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
+ for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS)
+ addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
+
+ ModifyPower(POWER_FOCUS, (int32)addvalue);
+}
+
+void Pet::LooseHappiness()
+{
+ uint32 curValue = GetPower(POWER_HAPPINESS);
+ if (curValue <= 0)
+ return;
+ int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
+ if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
+ addvalue = int32(addvalue * 1.5);
+ ModifyPower(POWER_HAPPINESS, -addvalue);
+}
+
+void Pet::ModifyLoyalty(int32 addvalue)
+{
+ uint32 loyaltylevel = GetLoyaltyLevel();
+
+ if(addvalue > 0) //only gain influenced, not loss
+ addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY));
+
+ if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ return;
+
+ m_loyaltyPoints += addvalue;
+
+ if(m_loyaltyPoints < 0)
+ {
+ if(loyaltylevel > REBELLIOUS)
+ {
+ //level down
+ --loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints - int32(getLevel()));
+ }
+ else
+ {
+ m_loyaltyPoints = 0;
+ Unit* owner = GetOwner();
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_BROKEN, 0);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+
+ //run away
+ ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED);
+ }
+ }
+ }
+ //level up
+ else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ {
+ ++loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints + getLevel());
+ }
+}
+
+void Pet::TickLoyaltyChange()
+{
+ int32 addvalue;
+
+ switch(GetHappinessState())
+ {
+ case HAPPY: addvalue = 20; break;
+ case CONTENT: addvalue = 10; break;
+ case UNHAPPY: addvalue = -20; break;
+ default:
+ return;
+ }
+ ModifyLoyalty(addvalue);
+}
+
+void Pet::KillLoyaltyBonus(uint32 level)
+{
+ if(level > 100)
+ return;
+
+ //at lower levels gain is faster | the lower loyalty the more loyalty is gained
+ uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel()));
+ ModifyLoyalty(bonus);
+}
+
+HappinessState Pet::GetHappinessState()
+{
+ if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
+ return UNHAPPY;
+ else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2)
+ return HAPPY;
+ else
+ return CONTENT;
+}
+
+void Pet::SetLoyaltyLevel(LoyaltyLevel level)
+{
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, level);
+}
+
+bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
+{
+ uint8 activecount = 1;
+ uint32 chainstartstore[ACTIVE_SPELLS_MAX];
+
+ if(IsPassiveSpell(spellid))
+ return true;
+
+ chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(IsPassiveSpell(itr->first))
+ continue;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first);
+
+ uint8 x;
+
+ for(x = 0; x < activecount; x++)
+ {
+ if(chainstart == chainstartstore[x])
+ break;
+ }
+
+ if(x == activecount) //spellchain not yet saved -> add active count
+ {
+ ++activecount;
+ if(activecount > ACTIVE_SPELLS_MAX)
+ return false;
+ chainstartstore[x] = chainstart;
+ }
+ }
+ return true;
+}
+
+bool Pet::HasTPForSpell(uint32 spellid)
+{
+ int32 neededtrainp = GetTPForSpell(spellid);
+ if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0)
+ return false;
+ return true;
+}
+
+int32 Pet::GetTPForSpell(uint32 spellid)
+{
+ uint32 basetrainp = 0;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(!_spell_idx->second->reqtrainpoints)
+ return 0;
+
+ basetrainp = _spell_idx->second->reqtrainpoints;
+ break;
+ }
+
+ uint32 spenttrainp = 0;
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
+ SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2)
+ {
+ if(_spell_idx2->second->reqtrainpoints > spenttrainp)
+ {
+ spenttrainp = _spell_idx2->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+ }
+
+ return int32(basetrainp) - int32(spenttrainp);
+}
+
+uint32 Pet::GetMaxLoyaltyPoints(uint32 level)
+{
+ return LevelUpLoyalty[level - 1];
+}
+
+uint32 Pet::GetStartLoyaltyPoints(uint32 level)
+{
+ return LevelStartLoyalty[level - 1];
+}
+
+void Pet::SetTP(int32 TP)
+{
+ m_TrainingPoints = TP;
+ SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP());
+}
+
+int32 Pet::GetDispTP()
+{
+ if(getPetType()!= HUNTER_PET)
+ return(0);
+ if(m_TrainingPoints < 0)
+ return -m_TrainingPoints;
+ else
+ return -(m_TrainingPoints + 1);
+}
+
+void Pet::Remove(PetSaveMode mode, bool returnreagent)
+{
+ Unit* owner = GetOwner();
+
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)owner)->RemovePet(this,mode,returnreagent);
+ return;
+ }
+
+ // only if current pet in slot
+ if(owner->GetPetGUID()==GetGUID())
+ owner->SetPet(0);
+ }
+
+ CleanupsBeforeDelete();
+ AddObjectToRemoveList();
+ m_removed = true;
+}
+
+void Pet::GivePetXP(uint32 xp)
+{
+ if(getPetType() != HUNTER_PET)
+ return;
+
+ if ( xp < 1 )
+ return;
+
+ if(!isAlive())
+ return;
+
+ uint32 level = getLevel();
+
+ // XP to money conversion processed in Player::RewardQuest
+ if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ return;
+
+ uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
+ uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ uint32 newXP = curXP + xp;
+
+ if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
+ {
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
+ return;
+ }
+
+ while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ newXP -= nextLvlXP;
+
+ SetLevel( level + 1 );
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
+
+ level = getLevel();
+ nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ GivePetLevel(level);
+ }
+
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
+
+ if(getPetType() == HUNTER_PET)
+ KillLoyaltyBonus(level);
+}
+
+void Pet::GivePetLevel(uint32 level)
+{
+ if(!level)
+ return;
+
+ InitStatsForLevel( level);
+
+ SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1));
+}
+
+bool Pet::CreateBaseAtCreature(Creature* creature)
+{
+ if(!creature)
+ {
+ sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
+ return false;
+ }
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+
+ sLog.outBasic("SetInstanceID()");
+ SetInstanceId(creature->GetInstanceId());
+
+ sLog.outBasic("Create pet");
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number))
+ return false;
+
+ Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ return false;
+ }
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(!cinfo)
+ {
+ sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
+ return false;
+ }
+
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ setPetType(MINI_PET);
+ return true;
+ }
+ SetDisplayId(creature->GetDisplayId());
+ SetNativeDisplayId(creature->GetNativeDisplayId());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,166500);
+ setPowerType(POWER_FOCUS);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4));
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family);
+ if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] )
+ SetName(familyname);
+ else
+ SetName(creature->GetName());
+
+ m_loyaltyPoints = 1000;
+ if(cinfo->type == CREATURE_TYPE_BEAST)
+ {
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) );
+ SetLoyaltyLevel(REBELLIOUS);
+ }
+ return true;
+}
+
+bool Pet::InitStatsForLevel(uint32 petlevel)
+{
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ assert(cinfo);
+
+ Unit* owner = GetOwner();
+ if(!owner)
+ {
+ sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry);
+ return false;
+ }
+
+ uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry;
+
+ SetLevel( petlevel);
+
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50));
+
+ SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
+
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
+ if(cFamily && cFamily->minScale > 0.0f)
+ {
+ float scale;
+ if (getLevel() >= cFamily->maxScaleLevel)
+ scale = cFamily->maxScale;
+ else if (getLevel() <= cFamily->minScaleLevel)
+ scale = cFamily->minScale;
+ else
+ scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
+ }
+ m_bonusdamage = 0;
+
+ int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
+
+ if(cinfo && getPetType() != HUNTER_PET)
+ {
+ createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1;
+ createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2;
+ createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3;
+ createResistance[SPELL_SCHOOL_FROST] = cinfo->resistance4;
+ createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5;
+ createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6;
+ }
+
+ switch(getPetType())
+ {
+ case SUMMON_PET:
+ {
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ switch(owner->getClass())
+ {
+ case CLASS_WARLOCK:
+ {
+
+ //the damage bonus used for pets is either fire or shadow damage, whatever is higher
+ uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
+ uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
+ uint32 val = (fire > shadow) ? fire : shadow;
+
+ SetBonusDamage(int32 (val * 0.15f));
+ //bonusAP += val * 0.57;
+ break;
+ }
+ case CLASS_MAGE:
+ {
+ //40% damage bonus of mage's frost damage
+ float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4;
+ if(val < 0)
+ val = 0;
+ SetBonusDamage( int32(val));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetCreateMana(pInfo->mana);
+
+ if(pInfo->armor > 0)
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+
+ for(int stat = 0; stat < MAX_STATS; ++stat)
+ {
+ SetCreateStat(Stats(stat),float(pInfo->stats[stat]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry);
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+ SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case HUNTER_PET:
+ {
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4));
+
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ //damage is increased afterwards as strength and pet scaling modify attack power
+
+ //stored standard pet stats are entry 1 in pet_levelinfo
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ for( int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ SetCreateStat(Stats(i), float(pInfo->stats[i]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Hunter pet levelstats missing in DB");
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case GUARDIAN_PET:
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+
+ SetCreateMana( 28 + 10*petlevel );
+ SetCreateHealth( 28 + 30*petlevel );
+
+ // FIXME: this is wrong formula, possible each guardian pet have own damage formula
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break;
+ }
+
+ for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) );
+
+ UpdateAllStats();
+
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+
+ return true;
+}
+
+bool Pet::HaveInDiet(ItemPrototype const* item) const
+{
+ if (!item->FoodType)
+ return false;
+
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return false;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return false;
+
+ uint32 diet = cFamily->petFoodMask;
+ uint32 FoodMask = 1 << (item->FoodType-1);
+ return diet & FoodMask;
+}
+
+uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
+{
+ // -5 or greater food level
+ if(getLevel() <= itemlevel +5) //possible to feed level 60 pet with level 55 level food for full effect
+ return 35000;
+ // -10..-6
+ else if(getLevel() <= itemlevel + 10) //pure guess, but sounds good
+ return 17000;
+ // -14..-11
+ else if(getLevel() <= itemlevel + 14) //level 55 food gets green on 70, makes sense to me
+ return 8000;
+ // -15 or less
+ else
+ return 0; //food too low level
+}
+
+void Pet::_LoadSpellCooldowns()
+{
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ time_t curTime = time(NULL);
+
+ WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8));
+ data << GetGUID();
+ data << uint8(0x0);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+ time_t db_time = (time_t)fields[1].GetUInt64();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id);
+ continue;
+ }
+
+ // skip outdated cooldown
+ if(db_time <= curTime)
+ continue;
+
+ data << uint32(spell_id);
+ data << uint32(uint32(db_time-curTime)*1000); // in m.secs
+
+ _AddCreatureSpellCooldown(spell_id,db_time);
+
+ sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime));
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ if(!m_CreatureSpellCooldowns.empty() && GetOwner())
+ {
+ ((Player*)GetOwner())->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Pet::_SaveSpellCooldowns()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
+
+ time_t curTime = time(NULL);
+
+ // remove oudated and save active
+ for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();)
+ {
+ if(itr->second <= curTime)
+ m_CreatureSpellCooldowns.erase(itr++);
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
+ ++itr;
+ }
+ }
+}
+
+void Pet::_LoadSpells()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveSpells()
+{
+ for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB
+ if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
+ if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active);
+
+ if (itr->second->state == PETSPELL_REMOVED)
+ _removeSpell(itr->first);
+ else
+ itr->second->state = PETSPELL_UNCHANGED;
+ }
+}
+
+void Pet::_LoadAuras(uint32 timediff)
+{
+ m_Auras.clear();
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ m_modAuras[i].clear();
+
+ // all aura related fields
+ for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
+ SetUInt32Value(i, 0);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint64 caster_guid = fields[0].GetUInt64();
+ uint32 spellid = fields[1].GetUInt32();
+ uint32 effindex = fields[2].GetUInt32();
+ int32 damage = (int32)fields[3].GetUInt32();
+ int32 maxduration = (int32)fields[4].GetUInt32();
+ int32 remaintime = (int32)fields[5].GetUInt32();
+ int32 remaincharges = (int32)fields[6].GetUInt32();
+
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
+ if(!spellproto)
+ {
+ sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ if(effindex >= 3)
+ {
+ sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ // negative effects should continue counting down after logout
+ if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
+ {
+ if(remaintime <= int32(timediff))
+ continue;
+
+ remaintime -= timediff;
+ }
+
+ // prevent wrong values of remaincharges
+ if(spellproto->procCharges)
+ {
+ if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
+ remaincharges = spellproto->procCharges;
+ }
+ else
+ remaincharges = -1;
+
+ Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
+
+ if(!damage)
+ damage = aura->GetModifier()->m_amount;
+ aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
+ AddAura(aura);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveAuras()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ AuraMap const& auras = GetAuras();
+ for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
+ SpellEntry const *spellInfo = itr->second->GetSpellProto();
+ uint8 i;
+ for (i = 0; i < 3; i++)
+ if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
+ break;
+
+ if (i == 3 && !itr->second->IsPassive())
+ CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
+ "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
+ m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
+ }
+}
+
+bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do pet spell book cleanup
+ if(state == PETSPELL_UNCHANGED) // spell load case
+ {
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ if (itr->second->state == PETSPELL_REMOVED)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ state = PETSPELL_CHANGED;
+ }
+ else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ itr->second->state = PETSPELL_UNCHANGED;
+ return false;
+ }
+ else
+ return false;
+ }
+
+ uint32 oldspell_id = 0;
+
+ PetSpell *newspell = new PetSpell;
+ newspell->state = state;
+ newspell->type = type;
+
+ if(active == ACT_DECIDE) //active was not used before, so we save it's autocast/passive state here
+ {
+ if(IsPassiveSpell(spell_id))
+ newspell->active = ACT_PASSIVE;
+ else
+ newspell->active = ACT_DISABLED;
+ }
+ else
+ newspell->active = active;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ slot_id = itr->second->slotId;
+ newspell->active = itr->second->active;
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(itr->first, false);
+
+ oldspell_id = itr->first;
+ removeSpell(itr->first);
+ }
+ }
+
+ uint16 tmpslot=slot_id;
+
+ if (tmpslot == 0xffff)
+ {
+ uint16 maxid = 0;
+ PetSpellMap::iterator itr;
+ for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+ if (itr->second->slotId > maxid) maxid = itr->second->slotId;
+ }
+ tmpslot = maxid + 1;
+ }
+
+ newspell->slotId = tmpslot;
+ m_spells[spell_id] = newspell;
+
+ if (IsPassiveSpell(spell_id))
+ CastSpell(this, spell_id, true);
+ else if(state == PETSPELL_NEW)
+ m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(spell_id, true);
+
+ return true;
+}
+
+bool Pet::learnSpell(uint16 spell_id)
+{
+ // prevent duplicated entires in spell book
+ if (!addSpell(spell_id))
+ return false;
+
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)owner)->PetSpellInitialize();
+ return true;
+}
+
+void Pet::removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr == m_spells.end())
+ return;
+
+ if(itr->second->state == PETSPELL_REMOVED)
+ return;
+
+ if(itr->second->state == PETSPELL_NEW)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ }
+ else
+ itr->second->state = PETSPELL_REMOVED;
+
+ RemoveAurasDueToSpell(spell_id);
+}
+
+bool Pet::_removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ return true;
+ }
+ return false;
+}
+
+void Pet::InitPetCreateSpells()
+{
+ m_charmInfo->InitPetActionBar();
+
+ m_spells.clear();
+ int32 usedtrainpoints = 0, petspellid;
+ PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
+ if(CreateSpells)
+ {
+ for(uint8 i = 0; i < 4; i++)
+ {
+ if(!CreateSpells->spellid[i])
+ break;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
+ if(!learn_spellproto)
+ continue;
+
+ if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
+ {
+ petspellid = learn_spellproto->EffectTriggerSpell[0];
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
+ {
+ if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right
+ ((Player*)owner)->learnSpell(learn_spellproto->Id);
+ else
+ AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
+ }
+ }
+ else
+ petspellid = learn_spellproto->Id;
+
+ addSpell(petspellid);
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ usedtrainpoints += _spell_idx->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+
+ LearnPetPassives();
+
+ CastPetAuras(false);
+
+ SetTP(-usedtrainpoints);
+}
+
+void Pet::CheckLearning(uint32 spellid)
+{
+ //charmed case -> prevent crash
+ if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
+ return;
+
+ Unit* owner = GetOwner();
+
+ if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ TeachSpellMap::iterator itr = m_teachspells.find(spellid);
+ if(itr == m_teachspells.end())
+ return;
+
+ if(urand(0, 100) < 10)
+ {
+ ((Player*)owner)->learnSpell(itr->second);
+ m_teachspells.erase(itr);
+ }
+}
+
+uint32 Pet::resetTalentsCost() const
+{
+ uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
+
+ // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
+ if(m_resetTalentsCost < 10*SILVER || days > 0)
+ return 10*SILVER;
+ // then 50 silver
+ else if(m_resetTalentsCost < 50*SILVER)
+ return 50*SILVER;
+ // then 1 gold
+ else if(m_resetTalentsCost < 1*GOLD)
+ return 1*GOLD;
+ // then increasing at a rate of 1 gold; cap 10 gold
+ else
+ return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
+}
+
+void Pet::ToggleAutocast(uint32 spellid, bool apply)
+{
+ if(IsPassiveSpell(spellid))
+ return;
+
+ PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
+
+ int i;
+
+ if(apply)
+ {
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
+ if (i == m_autospells.size())
+ {
+ m_autospells.push_back(spellid);
+ itr->second->active = ACT_ENABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+ else
+ {
+ AutoSpellList::iterator itr2 = m_autospells.begin();
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
+ if (i < m_autospells.size())
+ {
+ m_autospells.erase(itr2);
+ itr->second->active = ACT_DISABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+}
+
+bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
+{
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ Object::_Create(guidlow, pet_number, HIGHGUID_PET);
+
+ m_DBTableGuid = guidlow;
+ m_originalEntry = Entry;
+
+ if(!InitEntry(Entry))
+ return false;
+
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(getPetType() == MINI_PET) // always non-attackable
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
+ return true;
+}
+
+bool Pet::HasSpell(uint32 spell) const
+{
+ return (m_spells.find(spell) != m_spells.end());
+}
+
+// Get all passive spells in our skill line
+void Pet::LearnPetPassives()
+{
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return;
+
+ PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
+ if(petStore != sPetFamilySpellsStore.end())
+ {
+ for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
+ addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
+ }
+}
+
+void Pet::CastPetAuras(bool current)
+{
+ Unit* owner = GetOwner();
+ if(!owner)
+ return;
+
+ if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
+ return;
+
+ for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
+ {
+ PetAura const* pa = *itr;
+ ++itr;
+
+ if(!current && pa->IsRemovedOnChangePet())
+ owner->RemovePetAura(pa);
+ else
+ CastPetAura(pa);
+ }
+}
+
+void Pet::CastPetAura(PetAura const* aura)
+{
+ uint16 auraId = aura->GetAura(GetEntry());
+ if(!auraId)
+ return;
+
+ if(auraId == 35696) // Demonic Knowledge
+ {
+ int32 basePoints = aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100;
+ CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
+ }
+ else
+ CastSpell(this, auraId, true);
+}
diff --git a/src/game/Pet.h b/src/game/Pet.h
new file mode 100644
index 00000000000..f7866cbe0fc
--- /dev/null
+++ b/src/game/Pet.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_PET_H
+#define MANGOSSERVER_PET_H
+
+#include "ObjectDefines.h"
+#include "Creature.h"
+#include "Unit.h"
+
+enum PetType
+{
+ SUMMON_PET = 0,
+ HUNTER_PET = 1,
+ GUARDIAN_PET = 2,
+ MINI_PET = 3,
+ MAX_PET_TYPE = 4
+};
+
+extern char const* petTypeSuffix[MAX_PET_TYPE];
+
+enum PetSaveMode
+{
+ PET_SAVE_AS_DELETED =-1,
+ PET_SAVE_AS_CURRENT = 0,
+ PET_SAVE_IN_STABLE_SLOT_1 = 1,
+ PET_SAVE_IN_STABLE_SLOT_2 = 2,
+ PET_SAVE_NOT_IN_SLOT = 3
+};
+
+enum HappinessState
+{
+ UNHAPPY = 1,
+ CONTENT = 2,
+ HAPPY = 3
+};
+
+enum LoyaltyLevel
+{
+ REBELLIOUS = 1,
+ UNRULY = 2,
+ SUBMISSIVE = 3,
+ DEPENDABLE = 4,
+ FAITHFUL = 5,
+ BEST_FRIEND = 6
+};
+
+enum PetSpellState
+{
+ PETSPELL_UNCHANGED = 0,
+ PETSPELL_CHANGED = 1,
+ PETSPELL_NEW = 2,
+ PETSPELL_REMOVED = 3
+};
+
+enum PetSpellType
+{
+ PETSPELL_NORMAL = 0,
+ PETSPELL_FAMILY = 1,
+};
+
+struct PetSpell
+{
+ uint16 slotId;
+ uint16 active;
+ PetSpellState state : 16;
+ PetSpellType type : 16;
+};
+
+enum ActionFeedback
+{
+ FEEDBACK_NONE = 0,
+ FEEDBACK_PET_DEAD = 1,
+ FEEDBACK_NOTHING_TO_ATT = 2,
+ FEEDBACK_CANT_ATT_TARGET = 3
+};
+
+enum PetTalk
+{
+ PET_TALK_SPECIAL_SPELL = 0,
+ PET_TALK_ATTACK = 1
+};
+
+typedef HM_NAMESPACE::hash_map<uint16, PetSpell*> PetSpellMap;
+typedef std::map<uint32,uint32> TeachSpellMap;
+typedef std::vector<uint32> AutoSpellList;
+
+#define HAPPINESS_LEVEL_SIZE 333000
+
+extern const uint32 LevelUpLoyalty[6];
+extern const uint32 LevelStartLoyalty[6];
+
+#define ACTIVE_SPELLS_MAX 4
+
+#define OWNER_MAX_DISTANCE 100
+
+#define PET_FOLLOW_DIST 1
+#define PET_FOLLOW_ANGLE (M_PI/2)
+
+class Pet : public Creature
+{
+ public:
+ explicit Pet(PetType type = MAX_PET_TYPE);
+ virtual ~Pet();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ PetType getPetType() const { return m_petType; }
+ void setPetType(PetType type) { m_petType = type; }
+ bool isControlled() const { return getPetType()==SUMMON_PET || getPetType()==HUNTER_PET; }
+ bool isTemporarySummoned() const { return m_duration > 0; }
+
+ bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number);
+ bool CreateBaseAtCreature( Creature* creature );
+ bool LoadPetFromDB( Unit* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false );
+ void SavePetToDB(PetSaveMode mode);
+ void Remove(PetSaveMode mode, bool returnreagent = false);
+ static void DeleteFromDB(uint32 guidlow);
+
+ void setDeathState(DeathState s); // overwrite virtual Creature::setDeathState and Unit::setDeathState
+ void Update(uint32 diff); // overwrite virtual Creature::Update and Unit::Update
+
+ uint8 GetPetAutoSpellSize() const { return m_autospells.size(); }
+ uint32 GetPetAutoSpellOnPos(uint8 pos) const
+ {
+ if (pos >= m_autospells.size())
+ return 0;
+ else
+ return m_autospells[pos];
+ }
+
+ void RegenerateFocus();
+ void LooseHappiness();
+ void TickLoyaltyChange();
+ void ModifyLoyalty(int32 addvalue);
+ HappinessState GetHappinessState();
+ uint32 GetMaxLoyaltyPoints(uint32 level);
+ uint32 GetStartLoyaltyPoints(uint32 level);
+ void KillLoyaltyBonus(uint32 level);
+ uint32 GetLoyaltyLevel() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
+ void SetLoyaltyLevel(LoyaltyLevel level);
+ void GivePetXP(uint32 xp);
+ void GivePetLevel(uint32 level);
+ bool InitStatsForLevel(uint32 level);
+ bool HaveInDiet(ItemPrototype const* item) const;
+ uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel);
+ void SetDuration(int32 dur) { m_duration = dur; }
+
+ int32 GetBonusDamage() { return m_bonusdamage; }
+ void SetBonusDamage(int32 damage) { m_bonusdamage = damage; }
+
+ bool UpdateStats(Stats stat);
+ bool UpdateAllStats();
+ void UpdateResistances(uint32 school);
+ void UpdateArmor();
+ void UpdateMaxHealth();
+ void UpdateMaxPower(Powers power);
+ void UpdateAttackPowerAndDamage(bool ranged = false);
+ void UpdateDamagePhysical(WeaponAttackType attType);
+
+ bool CanTakeMoreActiveSpells(uint32 SpellIconID);
+ void ToggleAutocast(uint32 spellid, bool apply);
+ bool HasTPForSpell(uint32 spellid);
+ int32 GetTPForSpell(uint32 spellid);
+
+ bool HasSpell(uint32 spell) const;
+ void AddTeachSpell(uint32 learned_id, uint32 source_id) { m_teachspells[learned_id] = source_id; }
+
+ void LearnPetPassives();
+ void CastPetAuras(bool current);
+ void CastPetAura(PetAura const* aura);
+
+ void _LoadSpellCooldowns();
+ void _SaveSpellCooldowns();
+ void _LoadAuras(uint32 timediff);
+ void _SaveAuras();
+ void _LoadSpells();
+ void _SaveSpells();
+
+ bool addSpell(uint16 spell_id,uint16 active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, uint16 slot_id=0xffff, PetSpellType type = PETSPELL_NORMAL);
+ bool learnSpell(uint16 spell_id);
+ void removeSpell(uint16 spell_id);
+ bool _removeSpell(uint16 spell_id);
+
+ PetSpellMap m_spells;
+ TeachSpellMap m_teachspells;
+ AutoSpellList m_autospells;
+
+ void InitPetCreateSpells();
+ void CheckLearning(uint32 spellid);
+ uint32 resetTalentsCost() const;
+
+ void SetTP(int32 TP);
+ int32 GetDispTP();
+
+ int32 m_TrainingPoints;
+ uint32 m_resetTalentsCost;
+ time_t m_resetTalentsTime;
+
+ uint64 GetAuraUpdateMask() { return m_auraUpdateMask; }
+ void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); }
+ void ResetAuraUpdateMask() { m_auraUpdateMask = 0; }
+
+ DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
+
+ bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved)
+ protected:
+ uint32 m_regenTimer;
+ uint32 m_happinessTimer;
+ uint32 m_loyaltyTimer;
+ PetType m_petType;
+ int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
+ int32 m_loyaltyPoints;
+ int32 m_bonusdamage;
+ uint64 m_auraUpdateMask;
+
+ DeclinedName *m_declinedname;
+
+ private:
+ void SaveToDB(uint32, uint8) // overwrited of Creature::SaveToDB - don't must be called
+ {
+ assert(false);
+ }
+ void DeleteFromDB() // overwrited of Creature::DeleteFromDB - don't must be called
+ {
+ assert(false);
+ }
+};
+#endif
diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp
new file mode 100644
index 00000000000..c9aeaf66ec9
--- /dev/null
+++ b/src/game/PetAI.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "PetAI.h"
+#include "Errors.h"
+#include "Pet.h"
+#include "Player.h"
+#include "Database/DBCStores.h"
+#include "Spell.h"
+#include "ObjectAccessor.h"
+#include "SpellMgr.h"
+#include "Creature.h"
+#include "World.h"
+#include "Util.h"
+
+int PetAI::Permissible(const Creature *creature)
+{
+ if( creature->isPet())
+ return PERMIT_BASE_SPECIAL;
+
+ return PERMIT_BASE_NO;
+}
+
+PetAI::PetAI(Creature &c) : i_pet(c), i_victimGuid(0), i_tracker(TIME_INTERVAL_LOOK)
+{
+ m_AllySet.clear();
+ UpdateAllies();
+}
+
+void PetAI::MoveInLineOfSight(Unit *u)
+{
+ if( !i_pet.getVictim() && i_pet.GetCharmInfo() &&
+ i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) &&
+ u->isTargetableForAttack() && i_pet.IsHostileTo( u ) &&
+ u->isInAccessablePlaceFor(&i_pet))
+ {
+ float attackRadius = i_pet.GetAttackDistance(u);
+ if(i_pet.IsWithinDistInMap(u, attackRadius) && i_pet.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
+ {
+ if(i_pet.IsWithinLOSInMap(u))
+ {
+ AttackStart(u);
+ u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ }
+ }
+ }
+}
+
+void PetAI::AttackStart(Unit *u)
+{
+ if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET )
+ return;
+
+ if(i_pet.Attack(u,true))
+ {
+ i_pet.clearUnitState(UNIT_STAT_FOLLOW);
+ // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
+ // thus with the following clear the original TMG gets invalidated and crash, doh
+ // hope it doesn't start to leak memory without this :-/
+ //i_pet->Clear();
+ i_victimGuid = u->GetGUID();
+ i_pet.GetMotionMaster()->MoveChase(u);
+ }
+}
+
+void PetAI::EnterEvadeMode()
+{
+}
+
+bool PetAI::IsVisible(Unit *pl) const
+{
+ return _isVisible(pl);
+}
+
+bool PetAI::_needToStop() const
+{
+ if(!i_pet.getVictim() || !i_pet.isAlive())
+ return true;
+
+ // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
+ if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer())
+ return true;
+
+ return !i_pet.getVictim()->isTargetableForAttack();
+}
+
+void PetAI::_stopAttack()
+{
+ if( !i_victimGuid )
+ return;
+
+ Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid );
+
+ if ( !victim )
+ return;
+
+ assert(!i_pet.getVictim() || i_pet.getVictim() == victim);
+
+ if( !i_pet.isAlive() )
+ {
+ DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
+ i_pet.StopMoving();
+ i_pet.GetMotionMaster()->Clear();
+ i_pet.GetMotionMaster()->MoveIdle();
+ i_victimGuid = 0;
+ i_pet.CombatStop();
+ i_pet.getHostilRefManager().deleteReferences();
+
+ return;
+ }
+ else if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow());
+ }
+ else if( !victim->isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow());
+ }
+ else if( victim->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow());
+ }
+ else if( victim->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow());
+ }
+
+ Unit* owner = i_pet.GetCharmerOrOwner();
+
+ if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
+ {
+ i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+ }
+ else
+ {
+ i_pet.clearUnitState(UNIT_STAT_FOLLOW);
+ i_pet.GetMotionMaster()->Clear();
+ i_pet.GetMotionMaster()->MoveIdle();
+ }
+ i_victimGuid = 0;
+ i_pet.AttackStop();
+}
+
+void PetAI::UpdateAI(const uint32 diff)
+{
+ // update i_victimGuid if i_pet.getVictim() !=0 and changed
+ if(i_pet.getVictim())
+ i_victimGuid = i_pet.getVictim()->GetGUID();
+
+ Unit* owner = i_pet.GetCharmerOrOwner();
+
+ if(m_updateAlliesTimer <= diff)
+ // UpdateAllies self set update timer
+ UpdateAllies();
+ else
+ m_updateAlliesTimer -= diff;
+
+ // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
+ if( i_victimGuid )
+ {
+ if( _needToStop() )
+ {
+ DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
+ _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now
+ return;
+ }
+ else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
+ {
+ // required to be stopped cases
+ if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) )
+ {
+ if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
+ i_pet.InterruptNonMeleeSpells(false);
+ else
+ return;
+ }
+ // not required to be stopped case
+ else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) )
+ {
+ i_pet.AttackerStateUpdate(i_pet.getVictim());
+
+ i_pet.resetAttackTimer();
+
+ if ( !i_pet.getVictim() )
+ return;
+
+ //if pet misses its target, it will also be the first in threat list
+ i_pet.getVictim()->AddThreat(&i_pet,0.0f);
+
+ if( _needToStop() )
+ _stopAttack();
+ }
+ }
+ }
+ else if(owner && i_pet.GetCharmInfo())
+ {
+ if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY)))
+ {
+ AttackStart(owner->getAttackerForHelper());
+ }
+ else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
+ {
+ if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
+ {
+ i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+ }
+ }
+ }
+
+ //Autocast
+ HM_NAMESPACE::hash_map<uint32, Unit*> targetMap;
+ targetMap.clear();
+ SpellCastTargets NULLtargets;
+
+ for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
+ {
+ uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
+ if (!spellID)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
+ if (!spellInfo)
+ continue;
+
+ Spell *spell = new Spell(&i_pet, spellInfo, false, 0);
+
+ if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
+ targetMap[spellID] = i_pet.getVictim();
+ else
+ {
+ spell->m_targets = NULLtargets;
+ for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
+ {
+ Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);
+
+ //only buff targets that are in combat, unless the spell can only be cast while out of combat
+ if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo)))
+ continue;
+ if(spell->CanAutoCast(Target))
+ targetMap[spellID] = Target;
+ }
+ }
+
+ delete spell;
+ }
+
+ //found units to cast on to
+ if(!targetMap.empty())
+ {
+ uint32 index = urand(1, targetMap.size());
+ HM_NAMESPACE::hash_map<uint32, Unit*>::iterator itr;
+ uint32 i;
+ for(itr = targetMap.begin(), i = 1; i < index; ++itr, ++i);
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+
+ Spell *spell = new Spell(&i_pet, spellInfo, false);
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( itr->second );
+
+ if(!i_pet.HasInArc(M_PI, itr->second))
+ {
+ i_pet.SetInFront(itr->second);
+ if( itr->second->GetTypeId() == TYPEID_PLAYER )
+ i_pet.SendUpdateToPlayer( (Player*)itr->second );
+
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ i_pet.SendUpdateToPlayer( (Player*)owner );
+ }
+
+ i_pet.AddCreatureSpellCooldown(itr->first);
+ if(i_pet.isPet())
+ ((Pet*)&i_pet)->CheckLearning(itr->first);
+
+ spell->prepare(&targets);
+ }
+ targetMap.clear();
+}
+
+bool PetAI::_isVisible(Unit *u) const
+{
+ //return false; //( ((Creature*)&i_pet)->GetDistanceSq(u) * 1.0<= sWorld.getConfig(CONFIG_SIGHT_GUARDER) && !u->m_stealth && u->isAlive());
+ return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
+ && u->isVisibleForOrDetect(&i_pet,true);
+}
+
+void PetAI::UpdateAllies()
+{
+ Unit* owner = i_pet.GetCharmerOrOwner();
+ Group *pGroup = NULL;
+
+ m_updateAlliesTimer = 10000; //update friendly targets every 10 seconds, lesser checks increase performance
+
+ if(!owner)
+ return;
+ else if(owner->GetTypeId() == TYPEID_PLAYER)
+ pGroup = ((Player*)owner)->GetGroup();
+
+ //only pet and owner/not in group->ok
+ if(m_AllySet.size() == 2 && !pGroup)
+ return;
+ //owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
+ if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
+ return;
+
+ m_AllySet.clear();
+ m_AllySet.insert(i_pet.GetGUID());
+ if(pGroup) //add group
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+ if(!Target || !pGroup->SameSubGroup((Player*)owner, Target))
+ continue;
+
+ if(Target->GetGUID() == owner->GetGUID())
+ continue;
+
+ m_AllySet.insert(Target->GetGUID());
+ }
+ }
+ else //remove group
+ m_AllySet.insert(owner->GetGUID());
+}
+
+void PetAI::AttackedBy(Unit *attacker)
+{
+ //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway
+ if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
+ (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker)))
+ AttackStart(attacker);
+}
diff --git a/src/game/PetAI.h b/src/game/PetAI.h
new file mode 100644
index 00000000000..b8124bb3520
--- /dev/null
+++ b/src/game/PetAI.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_PETAI_H
+#define MANGOS_PETAI_H
+
+#include "CreatureAI.h"
+#include "Timer.h"
+
+class Creature;
+
+class MANGOS_DLL_DECL PetAI : public CreatureAI
+{
+ public:
+
+ PetAI(Creature &c);
+
+ void MoveInLineOfSight(Unit *);
+ void AttackStart(Unit *);
+ void EnterEvadeMode();
+ void DamageTaken(Unit *done_by, uint32& /*damage*/) { AttackedBy(done_by); }
+ void AttackedBy(Unit*);
+ bool IsVisible(Unit *) const;
+
+ void UpdateAI(const uint32);
+ void UpdateAllies();
+ static int Permissible(const Creature *);
+
+ private:
+ bool _isVisible(Unit *) const;
+ bool _needToStop(void) const;
+ void _stopAttack(void);
+
+ Creature &i_pet;
+ uint64 i_victimGuid;
+ TimeTracker i_tracker;
+ //uint32 i_RepeatAction;
+ std::set<uint64> m_AllySet;
+ uint32 m_updateAlliesTimer;
+};
+#endif
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp
new file mode 100644
index 00000000000..3251d02d5b9
--- /dev/null
+++ b/src/game/PetHandler.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Spell.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "CreatureAI.h"
+#include "Util.h"
+#include "Pet.h"
+
+void WorldSession::HandlePetAction( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+2+2+8);
+
+ uint64 guid1;
+ uint16 spellid;
+ uint16 flag;
+ uint64 guid2;
+ recv_data >> guid1; //pet guid
+ recv_data >> spellid;
+ recv_data >> flag; //delete = 0x0700 CastSpell = C100
+ recv_data >> guid2; //tag guid
+
+ // used also for charmed creature
+ Unit* pet= ObjectAccessor::GetUnit(*_player,guid1);
+ sLog.outDetail( "HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) );
+ if(!pet)
+ {
+ sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) );
+ return;
+ }
+
+ if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
+ {
+ sLog.outError( "HandlePetAction.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid1)),GetPlayer()->GetName() );
+ return;
+ }
+
+ if(!pet->isAlive())
+ return;
+
+ if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
+ return;
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ switch(flag)
+ {
+ case ACT_COMMAND: //0x0700
+ switch(spellid)
+ {
+ case COMMAND_STAY: //flat=1792 //STAY
+ pet->StopMoving();
+ pet->GetMotionMaster()->Clear();
+ pet->GetMotionMaster()->MoveIdle();
+ charmInfo->SetCommandState( COMMAND_STAY );
+ break;
+ case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
+ pet->AttackStop();
+ pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+ charmInfo->SetCommandState( COMMAND_FOLLOW );
+ break;
+ case COMMAND_ATTACK: //spellid=1792 //ATTACK
+ {
+ // only place where pet can be player
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+ uint64 selguid = _player->GetSelection();
+ Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid);
+ if(!TargetUnit)
+ return;
+
+ // not let attack friendly units.
+ if( GetPlayer()->IsFriendlyTo(TargetUnit))
+ return;
+
+ if(pet->getVictim())
+ pet->AttackStop();
+
+ if(pet->GetTypeId() != TYPEID_PLAYER)
+ {
+ pet->GetMotionMaster()->Clear();
+ if (((Creature*)pet)->AI())
+ ((Creature*)pet)->AI()->AttackStart(TargetUnit);
+
+ //10% chance to play special pet attack talk, else growl
+ if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
+ pet->SendPetTalk((uint32)PET_TALK_ATTACK);
+ else
+ {
+ // 90% chance for pet and 100% chance for charmed creature
+ pet->SendPetAIReaction(guid1);
+ }
+ }
+ else // charmed player
+ {
+ pet->Attack(TargetUnit,true);
+ pet->SendPetAIReaction(guid1);
+ }
+ break;
+ }
+ case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
+ if(((Creature*)pet)->isPet())
+ {
+ Pet* p = (Pet*)pet;
+ if(p->getPetType() == HUNTER_PET)
+ _player->RemovePet(p,PET_SAVE_AS_DELETED);
+ else
+ //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
+ p->setDeathState(CORPSE);
+ }
+ else // charmed
+ _player->Uncharm();
+ break;
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid);
+ }
+ break;
+ case ACT_REACTION: // 0x600
+ switch(spellid)
+ {
+ case REACT_PASSIVE: //passive
+ case REACT_DEFENSIVE: //recovery
+ case REACT_AGGRESSIVE: //activete
+ charmInfo->SetReactState( ReactStates(spellid) );
+ break;
+ }
+ break;
+ case ACT_DISABLED: //0x8100 spell (disabled), ignore
+ case ACT_CAST: //0x0100
+ case ACT_ENABLED: //0xc100 spell
+ {
+ Unit* unit_target;
+ if(guid2)
+ unit_target = ObjectAccessor::GetUnit(*_player,guid2);
+ else
+ unit_target = NULL;
+
+ // do not cast unknown spells
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i\n", spellid);
+ return;
+ }
+
+ for(uint32 i = 0; i < 3;i++)
+ {
+ if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED)
+ return;
+ }
+
+ // do not cast not learned spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell *spell = new Spell(pet, spellInfo, false);
+
+ int16 result = spell->PetCanCast(unit_target);
+
+ //auto turn to target unless possessed
+ if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ pet->SetInFront(unit_target);
+ if( unit_target->GetTypeId() == TYPEID_PLAYER )
+ pet->SendUpdateToPlayer( (Player*)unit_target );
+ if(Unit* powner = pet->GetCharmerOrOwner())
+ if(powner->GetTypeId() == TYPEID_PLAYER)
+ pet->SendUpdateToPlayer((Player*)powner);
+ result = -1;
+ }
+
+ if(result == -1)
+ {
+ ((Creature*)pet)->AddCreatureSpellCooldown(spellid);
+ if (((Creature*)pet)->isPet())
+ ((Pet*)pet)->CheckLearning(spellid);
+
+ unit_target = spell->m_targets.getUnitTarget();
+
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(((Creature*)pet)->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ {
+ pet->SendPetAIReaction(guid1);
+ }
+
+ if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+ if(pet->getVictim())
+ pet->AttackStop();
+ pet->GetMotionMaster()->Clear();
+ if (((Creature*)pet)->AI())
+ ((Creature*)pet)->AI()->AttackStart(unit_target);
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
+ data << uint32(spellid) << uint8(2) << uint8(result);
+ switch (result)
+ {
+ case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
+ data << uint32(spellInfo->RequiresSpellFocus);
+ break;
+ case SPELL_FAILED_REQUIRES_AREA:
+ data << uint32(spellInfo->AreaId);
+ break;
+ }
+ SendPacket(&data);
+ }
+ else
+ pet->SendPetCastFail(spellid, result);
+
+ if(!((Creature*)pet)->HasSpellCooldown(spellid))
+ pet->SendPetClearCooldown(spellid);
+
+ spell->finish(false);
+ delete spell;
+ }
+ break;
+ }
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid);
+ }
+}
+
+void WorldSession::HandlePetNameQuery( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" );
+
+ uint32 petnumber;
+ uint64 petguid;
+
+ recv_data >> petnumber;
+ recv_data >> petguid;
+
+ SendPetNameQuery(petguid,petnumber);
+}
+
+void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber)
+{
+ Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid);
+ if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber)
+ return;
+
+ std::string name = pet->GetName();
+
+ WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
+ data << uint32(petnumber);
+ data << name.c_str();
+ data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
+
+ if( pet->isPet() && ((Pet*)pet)->GetDeclinedNames() )
+ {
+ data << uint8(1);
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ data << ((Pet*)pet)->GetDeclinedNames()->name[i];
+ }
+ else
+ data << uint8(0);
+
+ _player->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+2+2);
+
+ sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" );
+
+ uint64 petguid;
+ uint32 position;
+ uint16 spell_id;
+ uint16 act_state;
+ uint8 count;
+
+ recv_data >> petguid;
+
+ // FIXME: charmed case
+ //Pet* pet = ObjectAccessor::Instance().GetPet(petguid);
+ if(ObjectAccessor::FindPlayer(petguid))
+ return;
+
+ Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid);
+
+ if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm()))
+ {
+ sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" );
+ return;
+ }
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetSetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ count = (recv_data.size() == 24) ? 2 : 1;
+ for(uint8 i = 0; i < count; i++)
+ {
+ recv_data >> position;
+ recv_data >> spell_id;
+ recv_data >> act_state;
+
+ sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state);
+
+ //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
+ if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id)))
+ {
+ //sign for autocast
+ if(act_state == ACT_ENABLED && spell_id)
+ {
+ if(pet->isCharmed())
+ charmInfo->ToggleCreatureAutocast(spell_id, true);
+ else
+ ((Pet*)pet)->ToggleAutocast(spell_id, true);
+ }
+ //sign for no/turn off autocast
+ else if(act_state == ACT_DISABLED && spell_id)
+ {
+ if(pet->isCharmed())
+ charmInfo->ToggleCreatureAutocast(spell_id, false);
+ else
+ ((Pet*)pet)->ToggleAutocast(spell_id, false);
+ }
+
+ charmInfo->GetActionBarEntry(position)->Type = act_state;
+ charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id;
+ }
+ }
+}
+
+void WorldSession::HandlePetRename( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1+1+1+1+1+1+1);
+
+ sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" );
+
+ uint64 petguid;
+ uint8 isdeclined;
+
+ std::string name;
+ DeclinedName declinedname;
+
+ recv_data >> petguid;
+ recv_data >> name;
+ recv_data >> isdeclined;
+
+ Pet* pet = ObjectAccessor::GetPet(petguid);
+ // check it!
+ if( !pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
+ pet->GetByteValue(UNIT_FIELD_BYTES_2, 2) != UNIT_RENAME_ALLOWED ||
+ pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() )
+ return;
+
+ if((!ObjectMgr::IsValidPetName(name)) || (objmgr.IsReservedName(name)))
+ {
+ SendNotification("Invalid name");
+ return;
+ }
+ pet->SetName(name);
+
+ Unit *owner = pet->GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
+
+ pet->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+
+ if(isdeclined)
+ {
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ recv_data >> declinedname.name[i];
+
+ std::wstring wname;
+ Utf8toWStr(name,wname);
+ if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
+ {
+ SendNotification("Invalid name");
+ return;
+ }
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if(isdeclined)
+ {
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.escape_string(declinedname.name[i]);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ CharacterDatabase.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')",
+ pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str());
+ }
+
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(),_player->GetGUIDLow(),pet->GetCharmInfo()->GetPetNumber() );
+ CharacterDatabase.CommitTransaction();
+
+ pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+}
+
+void WorldSession::HandlePetAbandon( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid; //pet guid
+ sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) );
+
+ // pet/charmed
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player, guid);
+ if(pet)
+ {
+ if(pet->isPet())
+ {
+ if(pet->GetGUID() == _player->GetPetGUID())
+ {
+ uint32 feelty = pet->GetPower(POWER_HAPPINESS);
+ pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0);
+ }
+
+ _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED);
+ }
+ else if(pet->GetGUID() == _player->GetCharmGUID())
+ {
+ _player->Uncharm();
+ }
+ }
+}
+
+void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ sLog.outDetail("CMSG_PET_UNLEARN");
+ uint64 guid;
+ recvPacket >> guid;
+
+ Pet* pet = _player->GetPet();
+
+ if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1)
+ return;
+
+ if(guid != pet->GetGUID())
+ {
+ sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ uint32 cost = pet->resetTalentsCost();
+
+ if (GetPlayer()->GetMoney() < cost)
+ {
+ GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
+ return;
+ }
+
+ for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();)
+ {
+ uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell
+ ++itr;
+ pet->removeSpell(spell_id);
+ }
+
+ pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1));
+
+ for(uint8 i = 0; i < 10; i++)
+ {
+ if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED)
+ charmInfo->GetActionBarEntry(i)->SpellOrAction = 0;
+ }
+
+ // relearn pet passives
+ pet->LearnPetPassives();
+
+ pet->m_resetTalentsTime = time(NULL);
+ pet->m_resetTalentsCost = cost;
+ GetPlayer()->ModifyMoney(-(int32)cost);
+
+ GetPlayer()->PetSpellInitialize();
+}
+
+void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
+{
+ CHECK_PACKET_SIZE(recvPacket,8+2+2+1);
+
+ sLog.outDetail("CMSG_PET_SPELL_AUTOCAST");
+ uint64 guid;
+ uint16 spellid;
+ uint16 spellid2; //maybe second spell, automatically toggled off when first toggled on?
+ uint8 state; //1 for on, 0 for off
+ recvPacket >> guid >> spellid >> spellid2 >> state;
+
+ if(!_player->GetPet() && !_player->GetCharm())
+ return;
+
+ if(ObjectAccessor::FindPlayer(guid))
+ return;
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
+
+ if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm()))
+ {
+ sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ // do not add not learned spells/ passive spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID());
+ return;
+ }
+
+ if(pet->isCharmed())
+ //state can be used as boolean
+ pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
+ else
+ ((Pet*)pet)->ToggleAutocast(spellid, state);
+
+ for(uint8 i = 0; i < 10; ++i)
+ {
+ if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction)
+ charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED;
+ }
+}
+
+void WorldSession::HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket )
+{
+ sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
+
+ CHECK_PACKET_SIZE(recvPacket,8+4);
+ uint64 guid;
+ uint32 spellid;
+
+ recvPacket >> guid >> spellid;
+
+ if(!_player->GetPet() && !_player->GetCharm())
+ return;
+
+ if(ObjectAccessor::FindPlayer(guid))
+ return;
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
+
+ if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm()))
+ {
+ sLog.outError( "HandleAddDynamicTargetObsoleteOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i\n", spellid);
+ return;
+ }
+
+ // do not cast not learned spells
+ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ SpellCastTargets targets;
+ if(!targets.read(&recvPacket,pet))
+ return;
+
+ pet->clearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell *spell = new Spell(pet, spellInfo, false);
+ spell->m_targets = targets;
+
+ int16 result = spell->PetCanCast(NULL);
+ if(result == -1)
+ {
+ pet->AddCreatureSpellCooldown(spellid);
+ if(pet->isPet())
+ {
+ Pet* p = (Pet*)pet;
+ p->CheckLearning(spellid);
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ pet->SendPetAIReaction(guid);
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ pet->SendPetCastFail(spellid, result);
+ if(!pet->HasSpellCooldown(spellid))
+ pet->SendPetClearCooldown(spellid);
+
+ spell->finish(false);
+ delete spell;
+ }
+}
diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp
new file mode 100644
index 00000000000..6957634dc9e
--- /dev/null
+++ b/src/game/PetitionsHandler.cpp
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Guild.h"
+#include "ArenaTeam.h"
+#include "MapManager.h"
+#include "GossipDef.h"
+#include "SocialMgr.h"
+
+/*enum PetitionType // dbc data
+{
+ PETITION_TYPE_GUILD = 1,
+ PETITION_TYPE_ARENA_TEAM = 3
+};*/
+
+// Charters ID in item_template
+#define GUILD_CHARTER 5863
+#define GUILD_CHARTER_COST 1000 // 10 S
+#define ARENA_TEAM_CHARTER_2v2 23560
+#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G
+#define ARENA_TEAM_CHARTER_3v3 23561
+#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G
+#define ARENA_TEAM_CHARTER_5v5 23562
+#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G
+
+void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_BUY");
+ //recv_data.hexlike();
+
+ uint64 guidNPC;
+ uint64 unk1, unk3, unk4, unk5, unk6, unk7;
+ uint32 unk2;
+ std::string name;
+ uint16 unk8;
+ uint8 unk9;
+ uint32 unk10; // selected index
+ uint32 unk11;
+ recv_data >> guidNPC; // NPC GUID
+ recv_data >> unk1; // 0
+ recv_data >> unk2; // 0
+ recv_data >> name; // name
+
+ // recheck
+ CHECK_PACKET_SIZE(recv_data, 8+8+4+(name.size()+1)+5*8+2+1+4+4);
+
+ recv_data >> unk3; // 0
+ recv_data >> unk4; // 0
+ recv_data >> unk5; // 0
+ recv_data >> unk6; // 0
+ recv_data >> unk7; // 0
+ recv_data >> unk8; // 0
+ recv_data >> unk9; // 0
+ recv_data >> unk10; // index
+ recv_data >> unk11; // 0
+ sLog.outDebug("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
+
+ // prevent cheating
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guidNPC,UNIT_NPC_FLAG_PETITIONER);
+ if (!pCreature)
+ {
+ sLog.outDebug("WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC));
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint32 charterid = 0;
+ uint32 cost = 0;
+ uint32 type = 0;
+ if(pCreature->isTabardDesigner())
+ {
+ // if tabard designer, then trying to buy a guild charter.
+ // do not let if already in guild.
+ if(_player->GetGuildId())
+ return;
+
+ charterid = GUILD_CHARTER;
+ cost = GUILD_CHARTER_COST;
+ type = 9;
+ }
+ else
+ {
+ // TODO: find correct opcode
+ if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ SendNotification(GetMangosString(LANG_ARENA_ONE_TOOLOW), 70);
+ return;
+ }
+
+ for(uint8 i = 0; i < MAX_ARENA_SLOT; i++)
+ {
+ if(_player->GetArenaTeamId(i) && (i == (unk10-1)))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
+ return;
+ }
+ }
+ switch(unk10)
+ {
+ case 1:
+ charterid = ARENA_TEAM_CHARTER_2v2;
+ cost = ARENA_TEAM_CHARTER_2v2_COST;
+ type = 2; // 2v2
+ break;
+ case 2:
+ charterid = ARENA_TEAM_CHARTER_3v3;
+ cost = ARENA_TEAM_CHARTER_3v3_COST;
+ type = 3; // 3v3
+ break;
+ case 3:
+ charterid = ARENA_TEAM_CHARTER_5v5;
+ cost = ARENA_TEAM_CHARTER_5v5_COST;
+ type = 5; // 5v5
+ break;
+ default:
+ sLog.outDebug("unknown selection at buy petition: %u", unk10);
+ return;
+ }
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS);
+ return;
+ }
+ if(objmgr.IsReservedName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID);
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ return;
+ }
+ if(objmgr.IsReservedName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ }
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid);
+ if(!pProto)
+ {
+ _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
+ return;
+ }
+
+ if(_player->GetMoney() < cost)
+ { //player hasn't got enough money
+ _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount );
+ if(msg != EQUIP_ERR_OK)
+ {
+ _player->SendBuyError(msg, pCreature, charterid, 0);
+ return;
+ }
+
+ _player->ModifyMoney(-(int32)cost);
+ Item *charter = _player->StoreNewItem(dest, charterid, true);
+ if(!charter)
+ return;
+
+ charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT, charter->GetGUIDLow());
+ // ITEM_FIELD_ENCHANTMENT is guild/arenateam id
+ // ITEM_FIELD_ENCHANTMENT+1 is current signatures count (showed on item)
+ charter->SetState(ITEM_CHANGED, _player);
+ _player->SendNewItem(charter, 1, true, false);
+
+ // a petition is invalid, if both the owner and the type matches
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type);
+
+ std::ostringstream ssInvalidPetitionGUIDs;
+
+ if (result)
+ {
+
+ do
+ {
+ Field *fields = result->Fetch();
+ ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , ";
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ // delete petitions with the same guid as this one
+ ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'";
+
+ sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str());
+ CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
+ _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
+ CharacterDatabase.CommitTransaction();
+}
+
+void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ // ok
+ sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES");
+ //recv_data.hexlike();
+
+ uint8 signs = 0;
+ uint64 petitionguid;
+ recv_data >> petitionguid; // petition guid
+
+ // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?)
+ uint32 petitionguid_low = GUID_LOPART(petitionguid);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid, type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+ Field *fields = result->Fetch();
+ uint32 type = fields[1].GetUInt32();
+ delete result;
+ // if guild petition and has guild => error, return;
+ if(type==9 && _player->GetGuildId())
+ return;
+
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low);
+
+ // result==NULL also correct in case no sign yet
+ if(result)
+ signs = result->GetRowCount();
+
+ sLog.outDebug("CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low);
+
+ WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12));
+ data << petitionguid; // petition guid
+ data << _player->GetGUID(); // owner guid
+ data << petitionguid_low; // guild guid (in mangos always same as GUID_LOPART(petitionguid)
+ data << signs; // sign's count
+
+ for(uint8 i = 1; i <= signs; i++)
+ {
+ Field *fields = result->Fetch();
+ uint64 plguid = fields[0].GetUInt64();
+
+ data << plguid; // Player GUID
+ data << (uint32)0; // there 0 ...
+
+ result->NextRow();
+ }
+ delete result;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 4+8);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok
+ //recv_data.hexlike();
+
+ uint32 guildguid;
+ uint64 petitionguid;
+ recv_data >> guildguid; // in mangos always same as GUID_LOPART(petitionguid)
+ recv_data >> petitionguid; // petition guid
+ sLog.outDebug("CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid);
+
+ SendPetitionQueryOpcode(petitionguid);
+}
+
+void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
+{
+ uint64 ownerguid = 0;
+ uint32 type;
+ std::string name = "NO_NAME_FOR_GUID";
+ uint8 signs = 0;
+
+ QueryResult *result = CharacterDatabase.PQuery(
+ "SELECT ownerguid, name, "
+ " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs "
+ "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ name = fields[1].GetCppString();
+ signs = fields[2].GetUInt8();
+ delete result;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*13));
+ data << GUID_LOPART(petitionguid); // guild/team guid (in mangos always same as GUID_LOPART(petition guid)
+ data << ownerguid; // charter owner guid
+ data << name; // name (guild/arena team)
+ data << uint8(0); // 1
+ if(type == 9)
+ {
+ data << uint32(9);
+ data << uint32(9);
+ data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition
+ }
+ else
+ {
+ data << type-1;
+ data << type-1;
+ data << type; // bypass client - side limitation, a different value is needed here for each petition
+ }
+ data << uint32(0); // 5
+ data << uint32(0); // 6
+ data << uint32(0); // 7
+ data << uint32(0); // 8
+ data << uint16(0); // 9 2 bytes field
+ data << uint32(0); // 10
+ data << uint32(0); // 11
+ data << uint32(0); // 13 count of next strings?
+ data << uint32(0); // 14
+ if(type == 9)
+ data << uint32(0); // 15 0 - guild, 1 - arena team
+ else
+ data << uint32(1);
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok
+ //recv_data.hexlike();
+
+ uint64 petitionguid;
+ uint32 type;
+ std::string newname;
+
+ recv_data >> petitionguid; // guid
+ recv_data >> newname; // new name
+
+ Item *item = _player->GetItemByGuid(petitionguid);
+ if(!item)
+ return;
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_EXISTS);
+ return;
+ }
+ if(objmgr.IsReservedName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(newname))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID);
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ return;
+ }
+ if(objmgr.IsReservedName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ if(!ObjectMgr::IsValidCharterName(newname))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
+ return;
+ }
+ }
+
+ std::string db_newname = newname;
+ CharacterDatabase.escape_string(db_newname);
+ CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'",
+ db_newname.c_str(), GUID_LOPART(petitionguid));
+
+ sLog.outDebug("Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionguid), newname.c_str());
+ WorldPacket data(MSG_PETITION_RENAME, (8+newname.size()+1));
+ data << petitionguid;
+ data << newname;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8+1);
+
+ sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok
+ //recv_data.hexlike();
+
+ Field *fields;
+ uint64 petitionguid;
+ uint32 type;
+ uint8 unk;
+ uint64 ownerguid;
+ recv_data >> petitionguid; // petition guid
+ recv_data >> unk;
+
+ uint8 signs = 0;
+
+ QueryResult *result = CharacterDatabase.PQuery(
+ "SELECT ownerguid, "
+ " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs "
+ "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+
+ fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ signs = fields[1].GetUInt8();
+
+ delete result;
+
+ uint32 plguidlo = _player->GetGUIDLow();
+ if(GUID_LOPART(ownerguid) == plguidlo)
+ return;
+
+ // not let enemies sign guild charter
+ if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid))
+ return;
+
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
+ if(result2)
+ {
+ Field* fields = result2->Fetch();
+ type = fields[0].GetUInt32();
+ delete result2;
+ }
+ else
+ {
+ sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
+ return;
+ }
+
+ if(type != 9 && _player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ // player is too low level to join an arena team
+ SendNotification("You must be level %u to join an arena team!",sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+ return;
+ }
+
+ signs += 1;
+ if(signs > type) // client signs maximum
+ return;
+
+ //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account
+ //not allow sign another player from already sign player account
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid));
+
+ if(result)
+ {
+ delete result;
+ WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
+ data << petitionguid;
+ data << _player->GetGUID();
+ data << (uint32)PETITION_SIGN_ALREADY_SIGNED;
+
+ // close at signer side
+ SendPacket(&data);
+
+ // update for owner if online
+ if(Player *owner = objmgr.GetPlayer(ownerguid))
+ owner->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId());
+
+ sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,GetAccountId());
+
+ WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
+ data << petitionguid;
+ data << _player->GetGUID();
+ data << (uint32)PETITION_SIGN_OK;
+
+ // close at signer side
+ SendPacket(&data);
+
+ // update signs count on charter, required testing...
+ //Item *item = _player->GetItemByGuid(petitionguid));
+ //if(item)
+ // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT+1, signs);
+
+ // update for owner if online
+ if(Player *owner = objmgr.GetPlayer(ownerguid))
+ owner->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok
+ //recv_data.hexlike();
+
+ uint64 petitionguid;
+ uint64 ownerguid;
+ recv_data >> petitionguid; // petition guid
+ sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete result;
+
+ Player *owner = objmgr.GetPlayer(ownerguid);
+ if(owner) // petition owner online
+ {
+ WorldPacket data(MSG_PETITION_DECLINE, 8);
+ data << _player->GetGUID();
+ owner->GetSession()->SendPacket(&data);
+ }
+}
+
+void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 4+8+8);
+
+ sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok
+ //recv_data.hexlike();
+
+ uint8 signs = 0;
+ uint64 petitionguid, plguid;
+ uint32 petitiontype;
+ Player *player;
+ recv_data >> petitiontype; // 2.0.8 - petition type?
+ recv_data >> petitionguid; // petition guid
+ recv_data >> plguid; // player guid
+ sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
+
+ player = ObjectAccessor::FindPlayer(plguid);
+ if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
+ return;
+
+ // not let offer to enemies
+ if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() )
+ return;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(!result)
+ {
+ sLog.outError("any petition on server...");
+ return;
+ }
+
+ delete result;
+
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ // result==NULL also correct charter without signs
+ if(result)
+ signs = result->GetRowCount();
+
+ WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12));
+ data << petitionguid; // petition guid
+ data << _player->GetGUID(); // owner guid
+ data << GUID_LOPART(petitionguid); // guild guid (in mangos always same as GUID_LOPART(petition guid)
+ data << signs; // sign's count
+
+ for(uint8 i = 1; i <= signs; i++)
+ {
+ Field *fields = result->Fetch();
+ uint64 plguid = fields[0].GetUInt64();
+
+ data << plguid; // Player GUID
+ data << (uint32)0; // there 0 ...
+
+ result->NextRow();
+ }
+
+ delete result;
+ player->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received opcode CMSG_TURN_IN_PETITION"); // ok
+ //recv_data.hexlike();
+
+ WorldPacket data;
+ uint64 petitionguid;
+
+ uint32 ownerguidlo;
+ uint32 type;
+ std::string name;
+
+ recv_data >> petitionguid;
+
+ sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
+
+ // data
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ ownerguidlo = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ type = fields[2].GetUInt32();
+ delete result;
+ }
+ else
+ {
+ sLog.outError("petition table has broken data!");
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(_player->GetGuildId())
+ {
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
+ _player->GetSession()->SendPacket(&data);
+ return;
+ }
+ }
+ else
+ {
+ uint8 slot = ArenaTeam::GetSlotByType(type);
+ if(slot >= MAX_ARENA_SLOT)
+ return;
+
+ if(_player->GetArenaTeamId(slot))
+ {
+ //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
+ //_player->GetSession()->SendPacket(&data);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
+ return;
+ }
+ }
+
+ if(_player->GetGUIDLow() != ownerguidlo)
+ return;
+
+ // signs
+ uint8 signs;
+ result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ if(result)
+ signs = result->GetRowCount();
+ else
+ signs = 0;
+
+ uint32 count;
+ //if(signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS))
+ if(type == 9)
+ count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS);
+ else
+ count = type-1;
+ if(signs < count)
+ {
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures...
+ SendPacket(&data);
+ delete result;
+ return;
+ }
+
+ if(type == 9)
+ {
+ if(objmgr.GetGuildByName(name))
+ {
+ SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS);
+ delete result;
+ return;
+ }
+ }
+ else
+ {
+ if(objmgr.GetArenaTeamByName(name))
+ {
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
+ delete result;
+ return;
+ }
+ }
+
+ // and at last charter item check
+ Item *item = _player->GetItemByGuid(petitionguid);
+ if(!item)
+ {
+ delete result;
+ return;
+ }
+
+ // OK!
+
+ // delete charter item
+ _player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
+
+ if(type == 9) // create guild
+ {
+ Guild* guild = new Guild;
+ if(!guild->create(_player->GetGUID(), name))
+ {
+ delete guild;
+ delete result;
+ return;
+ }
+
+ // register guild and add guildmaster
+ objmgr.AddGuild(guild);
+
+ // add members
+ for(uint8 i = 0; i < signs; ++i)
+ {
+ Field* fields = result->Fetch();
+ guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank());
+ result->NextRow();
+ }
+ }
+ else // or arena team
+ {
+ ArenaTeam* at = new ArenaTeam;
+ if(!at->create(_player->GetGUID(), type, name))
+ {
+ sLog.outError("PetitionsHandler: arena team create failed.");
+ delete at;
+ delete result;
+ return;
+ }
+
+ CHECK_PACKET_SIZE(recv_data, 8+5*4);
+ uint32 icon, iconcolor, border, bordercolor, backgroud;
+ recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor;
+
+ at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor);
+
+ // register team and add captain
+ objmgr.AddArenaTeam(at);
+ sLog.outDebug("PetitonsHandler: arena team added to objmrg");
+
+ // add members
+ for(uint8 i = 0; i < signs; ++i)
+ {
+ Field* fields = result->Fetch();
+ sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64());
+ at->AddMember(fields[0].GetUInt64());
+ result->NextRow();
+ }
+ }
+
+ delete result;
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+ CharacterDatabase.CommitTransaction();
+
+ // created
+ sLog.outDebug("TURN IN PETITION GUID %u", GUID_LOPART(petitionguid));
+
+ data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
+ data << (uint32)PETITION_TURN_OK;
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data, 8);
+
+ sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok
+ //recv_data.hexlike();
+
+ uint64 guid;
+ recv_data >> guid;
+
+ SendPetitionShowList(guid);
+}
+
+void WorldSession::SendPetitionShowList(uint64 guid)
+{
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_PETITIONER);
+ if (!pCreature)
+ {
+ sLog.outDebug("WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ uint8 count = 0;
+ if(pCreature->isTabardDesigner())
+ count = 1;
+ else
+ count = 3;
+
+ WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
+ data << guid; // npc guid
+ data << count; // count
+ if(count == 1)
+ {
+ data << uint32(1); // index
+ data << uint32(GUILD_CHARTER); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(GUILD_CHARTER_COST); // charter cost
+ data << uint32(0); // unknown
+ data << uint32(9); // required signs?
+ }
+ else
+ {
+ // 2v2
+ data << uint32(1); // index
+ data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost
+ data << uint32(2); // unknown
+ data << uint32(2); // required signs?
+ // 3v3
+ data << uint32(2); // index
+ data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost
+ data << uint32(3); // unknown
+ data << uint32(3); // required signs?
+ // 5v5
+ data << uint32(3); // index
+ data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry
+ data << uint32(16161); // charter display id
+ data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
+ data << uint32(5); // unknown
+ data << uint32(5); // required signs?
+ }
+ //for(uint8 i = 0; i < count; i++)
+ //{
+ // data << uint32(i); // index
+ // data << uint32(GUILD_CHARTER); // charter entry
+ // data << uint32(16161); // charter display id
+ // data << uint32(GUILD_CHARTER_COST+i); // charter cost
+ // data << uint32(0); // unknown
+ // data << uint32(9); // required signs?
+ //}
+ SendPacket(&data);
+ sLog.outDebug("Sent SMSG_PETITION_SHOWLIST");
+}
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
new file mode 100644
index 00000000000..f93b44ba732
--- /dev/null
+++ b/src/game/Player.cpp
@@ -0,0 +1,18130 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "World.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "UpdateMask.h"
+#include "Player.h"
+#include "SkillDiscovery.h"
+#include "QuestDef.h"
+#include "GossipDef.h"
+#include "UpdateData.h"
+#include "Channel.h"
+#include "ChannelMgr.h"
+#include "MapManager.h"
+#include "MapInstanced.h"
+#include "InstanceSaveMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "ObjectMgr.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "Formulas.h"
+#include "Group.h"
+#include "Guild.h"
+#include "Pet.h"
+#include "SpellAuras.h"
+#include "Util.h"
+#include "Transports.h"
+#include "Weather.h"
+#include "BattleGround.h"
+#include "BattleGroundMgr.h"
+#include "ArenaTeam.h"
+#include "Chat.h"
+#include "Database/DatabaseImpl.h"
+#include "Spell.h"
+#include "SocialMgr.h"
+
+#include <cmath>
+
+#define ZONE_UPDATE_INTERVAL 1000
+
+#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
+#define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
+#define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)
+
+#define SKILL_VALUE(x) PAIR32_LOPART(x)
+#define SKILL_MAX(x) PAIR32_HIPART(x)
+#define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v,m)
+
+#define SKILL_TEMP_BONUS(x) int16(PAIR32_LOPART(x))
+#define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
+#define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t,p)
+
+enum CharacterFlags
+{
+ CHARACTER_FLAG_NONE = 0x00000000,
+ CHARACTER_FLAG_UNK1 = 0x00000001,
+ CHARACTER_FLAG_UNK2 = 0x00000002,
+ CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
+ CHARACTER_FLAG_UNK4 = 0x00000008,
+ CHARACTER_FLAG_UNK5 = 0x00000010,
+ CHARACTER_FLAG_UNK6 = 0x00000020,
+ CHARACTER_FLAG_UNK7 = 0x00000040,
+ CHARACTER_FLAG_UNK8 = 0x00000080,
+ CHARACTER_FLAG_UNK9 = 0x00000100,
+ CHARACTER_FLAG_UNK10 = 0x00000200,
+ CHARACTER_FLAG_HIDE_HELM = 0x00000400,
+ CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
+ CHARACTER_FLAG_UNK13 = 0x00001000,
+ CHARACTER_FLAG_GHOST = 0x00002000,
+ CHARACTER_FLAG_RENAME = 0x00004000,
+ CHARACTER_FLAG_UNK16 = 0x00008000,
+ CHARACTER_FLAG_UNK17 = 0x00010000,
+ CHARACTER_FLAG_UNK18 = 0x00020000,
+ CHARACTER_FLAG_UNK19 = 0x00040000,
+ CHARACTER_FLAG_UNK20 = 0x00080000,
+ CHARACTER_FLAG_UNK21 = 0x00100000,
+ CHARACTER_FLAG_UNK22 = 0x00200000,
+ CHARACTER_FLAG_UNK23 = 0x00400000,
+ CHARACTER_FLAG_UNK24 = 0x00800000,
+ CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
+ CHARACTER_FLAG_DECLINED = 0x02000000,
+ CHARACTER_FLAG_UNK27 = 0x04000000,
+ CHARACTER_FLAG_UNK28 = 0x08000000,
+ CHARACTER_FLAG_UNK29 = 0x10000000,
+ CHARACTER_FLAG_UNK30 = 0x20000000,
+ CHARACTER_FLAG_UNK31 = 0x40000000,
+ CHARACTER_FLAG_UNK32 = 0x80000000
+};
+
+// corpse reclaim times
+#define DEATH_EXPIRE_STEP (5*MINUTE)
+#define MAX_DEATH_COUNT 3
+
+static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
+
+//== PlayerTaxi ================================================
+
+PlayerTaxi::PlayerTaxi()
+{
+ // Taxi nodes
+ memset(m_taximask, 0, sizeof(m_taximask));
+}
+
+void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level)
+{
+ // capital and taxi hub masks
+ switch(race)
+ {
+ case RACE_HUMAN: SetTaximaskNode(2); break; // Human
+ case RACE_ORC: SetTaximaskNode(23); break; // Orc
+ case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
+ case RACE_NIGHTELF: SetTaximaskNode(26);
+ SetTaximaskNode(27); break; // Night Elf
+ case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
+ case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
+ case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
+ case RACE_TROLL: SetTaximaskNode(23); break; // Troll
+ case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
+ case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
+ }
+ // new continent starting masks (It will be accessible only at new map)
+ switch(Player::TeamForRace(race))
+ {
+ case ALLIANCE: SetTaximaskNode(100); break;
+ case HORDE: SetTaximaskNode(99); break;
+ }
+ // level dependent taxi hubs
+ if(level>=68)
+ SetTaximaskNode(213); //Shattered Sun Staging Area
+}
+
+void PlayerTaxi::LoadTaxiMask(const char* data)
+{
+ Tokens tokens = StrSplit(data, " ");
+
+ int index;
+ Tokens::iterator iter;
+ for (iter = tokens.begin(), index = 0;
+ (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
+ {
+ // load and set bits only for existed taxi nodes
+ m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
+ }
+}
+
+void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
+{
+ if(all)
+ {
+ for (uint8 i=0; i<TaxiMaskSize; i++)
+ data << sTaxiNodesMask[i]; // all existed nodes
+ }
+ else
+ {
+ for (uint8 i=0; i<TaxiMaskSize; i++)
+ data << uint32(m_taximask[i]); // known nodes
+ }
+}
+
+bool PlayerTaxi::LoadTaxiDestinationsFromString( std::string values )
+{
+ ClearTaxiDestinations();
+
+ Tokens tokens = StrSplit(values," ");
+
+ for(Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
+ {
+ uint32 node = uint32(atol(iter->c_str()));
+ AddTaxiDestination(node);
+ }
+
+ if(m_TaxiDestinations.empty())
+ return true;
+
+ // Check integrity
+ if(m_TaxiDestinations.size() < 2)
+ return false;
+
+ for(size_t i = 1; i < m_TaxiDestinations.size(); ++i)
+ {
+ uint32 cost;
+ uint32 path;
+ objmgr.GetTaxiPath(m_TaxiDestinations[i-1],m_TaxiDestinations[i],path,cost);
+ if(!path)
+ return false;
+ }
+
+ return true;
+}
+
+std::string PlayerTaxi::SaveTaxiDestinationsToString()
+{
+ if(m_TaxiDestinations.empty())
+ return "";
+
+ std::ostringstream ss;
+
+ for(size_t i=0; i < m_TaxiDestinations.size(); ++i)
+ ss << m_TaxiDestinations[i] << " ";
+
+ return ss.str();
+}
+
+uint32 PlayerTaxi::GetCurrentTaxiPath() const
+{
+ if(m_TaxiDestinations.size() < 2)
+ return 0;
+
+ uint32 path;
+ uint32 cost;
+
+ objmgr.GetTaxiPath(m_TaxiDestinations[0],m_TaxiDestinations[1],path,cost);
+
+ return path;
+}
+
+//== Player ====================================================
+
+const int32 Player::ReputationRank_Length[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
+
+UpdateMask Player::updateVisualBits;
+
+Player::Player (WorldSession *session): Unit()
+{
+ m_transport = 0;
+
+ m_speakTime = 0;
+ m_speakCount = 0;
+
+ m_objectType |= TYPEMASK_PLAYER;
+ m_objectTypeId = TYPEID_PLAYER;
+
+ m_valuesCount = PLAYER_END;
+
+ m_session = session;
+
+ m_divider = 0;
+
+ m_ExtraFlags = 0;
+ if(GetSession()->GetSecurity() >= SEC_GAMEMASTER)
+ SetAcceptTicket(true);
+
+ // players always and GM if set in config accept whispers by default
+ if(GetSession()->GetSecurity() == SEC_PLAYER || sWorld.getConfig(CONFIG_GM_WISPERING_TO))
+ SetAcceptWhispers(true);
+
+ m_curSelection = 0;
+ m_lootGuid = 0;
+
+ m_comboTarget = 0;
+ m_comboPoints = 0;
+
+ m_usedTalentCount = 0;
+
+ m_regenTimer = 0;
+ m_weaponChangeTimer = 0;
+
+ m_zoneUpdateId = 0;
+ m_zoneUpdateTimer = 0;
+
+ m_areaUpdateId = 0;
+
+ 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();
+
+ m_SpellModRemoveCount = 0;
+
+ memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
+
+ m_social = NULL;
+
+ // group is initialized in the reference constructor
+ SetGroupInvite(NULL);
+ m_groupUpdateMask = 0;
+ m_auraUpdateMask = 0;
+
+ duel = NULL;
+
+ m_GuildIdInvited = 0;
+ m_ArenaTeamIdInvited = 0;
+
+ m_atLoginFlags = AT_LOGIN_NONE;
+
+ m_dontMove = false;
+
+ pTrader = 0;
+ ClearTrade();
+
+ m_cinematic = 0;
+
+ PlayerTalkClass = new PlayerMenu( GetSession() );
+ m_currentBuybackSlot = BUYBACK_SLOT_START;
+
+ for ( int aX = 0 ; aX < 8 ; aX++ )
+ m_Tutorials[ aX ] = 0x00;
+ m_TutorialsChanged = false;
+
+ m_DailyQuestChanged = false;
+ m_lastDailyQuestTime = 0;
+
+ m_regenTimer = 0;
+ m_weaponChangeTimer = 0;
+ m_breathTimer = 0;
+ m_isunderwater = 0;
+ m_isInWater = false;
+ m_drunkTimer = 0;
+ m_drunk = 0;
+ m_restTime = 0;
+ m_deathTimer = 0;
+ m_deathExpireTime = 0;
+
+ m_swingErrorMsg = 0;
+
+ m_DetectInvTimer = 1000;
+
+ m_bgBattleGroundID = 0;
+ for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++)
+ {
+ m_bgBattleGroundQueueID[j].bgType = 0;
+ m_bgBattleGroundQueueID[j].invited = false;
+ }
+ m_bgTeam = 0;
+
+ m_logintime = time(NULL);
+ m_Last_tick = m_logintime;
+ m_WeaponProficiency = 0;
+ m_ArmorProficiency = 0;
+ m_canParry = false;
+ m_canDualWield = false;
+ m_ammoDPS = 0.0f;
+
+ m_temporaryUnsummonedPetNumber = 0;
+ //cache for UNIT_CREATED_BY_SPELL to allow
+ //returning reagests for temporarily removed pets
+ //when dying/logging out
+ m_oldpetspell = 0;
+
+ ////////////////////Rest System/////////////////////
+ time_inn_enter=0;
+ inn_pos_mapid=0;
+ inn_pos_x=0;
+ inn_pos_y=0;
+ inn_pos_z=0;
+ m_rest_bonus=0;
+ rest_type=REST_TYPE_NO;
+ ////////////////////Rest System/////////////////////
+
+ m_mailsLoaded = false;
+ m_mailsUpdated = false;
+ unReadMails = 0;
+ m_nextMailDelivereTime = 0;
+
+ m_resetTalentsCost = 0;
+ m_resetTalentsTime = 0;
+ m_itemUpdateQueueBlocked = false;
+
+ for (int i = 0; i < MAX_MOVE_TYPE; ++i)
+ m_forced_speed_changes[i] = 0;
+
+ m_stableSlots = 0;
+
+ /////////////////// Instance System /////////////////////
+
+ m_HomebindTimer = 0;
+ m_InstanceValid = true;
+ m_dungeonDifficulty = DIFFICULTY_NORMAL;
+
+ for (int i = 0; i < BASEMOD_END; i++)
+ {
+ m_auraBaseMod[i][FLAT_MOD] = 0.0f;
+ m_auraBaseMod[i][PCT_MOD] = 1.0f;
+ }
+
+ // Honor System
+ m_lastHonorUpdateTime = time(NULL);
+
+ // Player summoning
+ m_summon_expire = 0;
+ m_summon_mapid = 0;
+ m_summon_x = 0.0f;
+ m_summon_y = 0.0f;
+ m_summon_z = 0.0f;
+
+ //Default movement to run mode
+ m_unit_movement_flags = 0;
+
+ m_miniPet = 0;
+ m_bgAfkReportedTimer = 0;
+ m_contestedPvPTimer = 0;
+
+ m_declinedname = NULL;
+}
+
+Player::~Player ()
+{
+ CleanupsBeforeDelete();
+
+ if(m_uint32Values) // only for fully created Object
+ {
+ sSocialMgr.RemovePlayerSocial(GetGUIDLow());
+ }
+
+ // Note: buy back item already deleted from DB when player was saved
+ for(int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
+ {
+ if(m_items[i])
+ delete m_items[i];
+ }
+ CleanupChannels();
+
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ delete itr->second;
+
+ //all mailed items should be deleted, also all mail should be deallocated
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
+ delete *itr;
+
+ for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
+ delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
+
+ delete PlayerTalkClass;
+
+ if (m_transport)
+ {
+ m_transport->RemovePassenger(this);
+ }
+
+ for(size_t x = 0; x < ItemSetEff.size(); x++)
+ if(ItemSetEff[x])
+ delete ItemSetEff[x];
+
+ // clean up player-instance binds, may unload some instance saves
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ itr->second.save->RemovePlayer(this);
+
+ delete m_declinedname;
+}
+
+void Player::CleanupsBeforeDelete()
+{
+ if(m_uint32Values) // only for fully created Object
+ {
+ TradeCancel(false);
+ DuelComplete(DUEL_INTERUPTED);
+ }
+ Unit::CleanupsBeforeDelete();
+}
+
+bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId )
+{
+ Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
+
+ m_name = name;
+
+ PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_);
+ if(!info)
+ {
+ sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
+ return false;
+ }
+
+ for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
+ m_items[i] = NULL;
+
+ //for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
+ //{
+ // SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+j*2,0);
+ // SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+j,0);
+ // SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+j,0);
+ //}
+
+ m_race = race;
+ m_class = class_;
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ);
+
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
+ if(!cEntry)
+ {
+ sLog.outError("Class %u not found in DBC (Wrong DBC files?)",class_);
+ return false;
+ }
+
+ uint8 powertype = cEntry->powerType;
+
+ uint32 unitfield;
+
+ switch(powertype)
+ {
+ case POWER_ENERGY:
+ case POWER_MANA:
+ unitfield = 0x00000000;
+ break;
+ case POWER_RAGE:
+ unitfield = 0x00110000;
+ break;
+ default:
+ sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_);
+ return false;
+ }
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
+ SetFloatValue(UNIT_FIELD_COMBATREACH, 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);
+
+ SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( race ) | ( class_ << 8 ) | ( gender << 16 ) | ( powertype << 24 ) ) );
+ SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE );
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
+
+ //-1 is default value
+ SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
+
+ SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
+ SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
+ SetByteValue(PLAYER_BYTES_3, 0, gender);
+
+ SetUInt32Value( PLAYER_GUILDID, 0 );
+ SetUInt32Value( PLAYER_GUILDRANK, 0 );
+ SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 );
+
+ SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled
+ SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
+ SetUInt32Value( PLAYER_FIELD_KILLS, 0 );
+ SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 );
+ SetUInt32Value( PLAYER_FIELD_TODAY_CONTRIBUTION, 0 );
+ SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 );
+
+ // set starting level
+ SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) );
+
+ // Played time
+ m_Last_tick = time(NULL);
+ m_Played_time[0] = 0;
+ m_Played_time[1] = 0;
+
+ // base stats and related field values
+ InitStatsForLevel();
+ InitTaxiNodesForLevel();
+ InitTalentForLevel();
+ InitPrimaryProffesions(); // to max set before any spell added
+
+ // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
+ UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
+ SetHealth(GetMaxHealth());
+ if (getPowerType()==POWER_MANA)
+ {
+ UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intelect)
+ SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
+ }
+
+ learnDefaultSpells(true);
+
+ std::list<uint16>::const_iterator action_itr[4];
+ for(int 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(int i=0; i<4 ;i++)
+ taction[i] = (*action_itr[i]);
+
+ addActionButton((uint8)taction[0], taction[1], (uint8)taction[2], (uint8)taction[3]);
+
+ for(int i=0; i<4 ;i++)
+ ++action_itr[i];
+ }
+
+ UpdateBlockPercentage();
+
+ for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++)
+ {
+ uint32 titem_id = item_id_itr->item_id;
+ uint32 titem_amount = item_id_itr->item_amount;
+
+ sLog.outDebug("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount);
+
+ // attempt equip
+ uint16 eDest;
+ uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, titem_amount, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ EquipNewItem( eDest, titem_id, titem_amount, true);
+ AutoUnequipOffhandIfNeed();
+ continue; // equipped, to next
+ }
+
+ // attempt store
+ ItemPosCountVec sDest;
+ // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
+ msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount );
+ if( msg == EQUIP_ERR_OK )
+ {
+ StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) );
+ continue; // stored, to next
+ }
+
+ // item can't be added
+ sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u",titem_id,race,class_,msg);
+ }
+
+ // bags and main-hand weapon must equipped at this moment
+ // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
+ // or ammo not equipped in special bag
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ if(Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ uint16 eDest;
+ // equip offhand weapon/shield if it attempt equipped before main-hand weapon
+ uint8 msg = CanEquipItem( NULL_SLOT, eDest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
+ EquipItem( eDest, pItem, true);
+ }
+ // move other items to more appropriate slots (ammo not equipped in special bag)
+ else
+ {
+ ItemPosCountVec sDest;
+ msg = CanStoreItem( NULL_BAG, NULL_SLOT, sDest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
+ pItem = StoreItem( sDest, pItem, true);
+ }
+
+ // if this is ammo then use it
+ uint8 msg = CanUseAmmo( pItem->GetProto()->ItemId );
+ if( msg == EQUIP_ERR_OK )
+ SetAmmo( pItem->GetProto()->ItemId );
+ }
+ }
+ }
+ // all item positions resolved
+
+ return true;
+}
+
+void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue)
+{
+ uint32 BreathRegen = (uint32)-1;
+
+ WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
+ data << (uint32)Type;
+ data << MaxValue;
+ data << MaxValue;
+ data << BreathRegen;
+ data << (uint8)0;
+ data << (uint32)0; // spell id
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen)
+{
+ if(Type==BREATH_TIMER)
+ m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen;
+
+ WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
+ data << (uint32)Type;
+ data << CurrentValue;
+ data << MaxValue;
+ data << Regen;
+ data << (uint8)0;
+ data << (uint32)0; // spell id
+ GetSession()->SendPacket( &data );
+}
+
+void Player::StopMirrorTimer(MirrorTimerType Type)
+{
+ if(Type==BREATH_TIMER)
+ m_breathTimer = 0;
+
+ WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
+ data << (uint32)Type;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage)
+{
+ WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
+ data << (uint64)guid;
+ data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
+ data << (uint32)damage;
+ data << (uint32)0;
+ data << (uint32)0;
+ //m_session->SendPacket(&data);
+ //Let other players see that you get damage
+ SendMessageToSet(&data, true);
+ DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+
+ if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage
+ {
+ DEBUG_LOG("We are fall to death, loosing 10 percents durability");
+ DurabilityLossAll(0.10f,false);
+ // durability lost message
+ WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::HandleDrowning()
+{
+ if(!m_isunderwater)
+ return;
+
+ //if have water breath , then remove bar
+ if(waterbreath || isGameMaster() || !isAlive())
+ {
+ StopMirrorTimer(BREATH_TIMER);
+ m_isunderwater = 0;
+ return;
+ }
+
+ uint32 UnderWaterTime = 1*MINUTE*1000; // default leangthL 1 min
+
+ AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
+ for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
+ UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifier()->m_amount) / 100.0f);
+
+ if ((m_isunderwater & 0x01) && !(m_isunderwater & 0x80) && isAlive())
+ {
+ //single trigger timer
+ if (!(m_isunderwater & 0x02))
+ {
+ m_isunderwater|= 0x02;
+ m_breathTimer = UnderWaterTime + 1000;
+ }
+ //single trigger "Breathbar"
+ if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & 0x04))
+ {
+ m_isunderwater|= 0x04;
+ StartMirrorTimer(BREATH_TIMER, UnderWaterTime);
+ }
+ //continius trigger drowning "Damage"
+ if ((m_breathTimer == 0) && (m_isunderwater & 0x01))
+ {
+ //TODO: Check this formula
+ uint64 guid = GetGUID();
+ uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
+
+ EnvironmentalDamage(guid, DAMAGE_DROWNING,damage);
+ m_breathTimer = 2000;
+ }
+ }
+ //single trigger retract bar
+ else if (!(m_isunderwater & 0x01) && !(m_isunderwater & 0x08) && (m_isunderwater & 0x02) && (m_breathTimer > 0) && isAlive())
+ {
+ m_isunderwater = 0x08;
+
+ uint32 BreathRegen = 10;
+ ModifyMirrorTimer(BREATH_TIMER, UnderWaterTime, m_breathTimer,BreathRegen);
+ m_isunderwater = 0x10;
+ }
+ //remove bar
+ else if ((m_breathTimer < 50) && !(m_isunderwater & 0x01) && (m_isunderwater == 0x10))
+ {
+ StopMirrorTimer(BREATH_TIMER);
+ m_isunderwater = 0;
+ }
+}
+
+void Player::HandleLava()
+{
+ bool ValidArea = false;
+
+ if ((m_isunderwater & 0x80) && isAlive())
+ {
+ //Single trigger Set BreathTimer
+ if (!(m_isunderwater & 0x80))
+ {
+ m_isunderwater|= 0x04;
+ m_breathTimer = 1000;
+ }
+ //Reset BreathTimer and still in the lava
+ if (!m_breathTimer)
+ {
+ uint64 guid = GetGUID();
+ uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage
+ uint32 dmgZone = GetZoneId(); // TODO: Find correct "lava dealing zone" flag in Area Table
+
+ // Deal lava damage only in lava zones.
+ switch(dmgZone)
+ {
+ case 0x8D:
+ ValidArea = false;
+ break;
+ case 0x94:
+ ValidArea = false;
+ break;
+ case 0x2CE:
+ ValidArea = false;
+ break;
+ case 0x2CF:
+ ValidArea = false;
+ break;
+ default:
+ if (dmgZone / 5 & 0x408)
+ ValidArea = true;
+ }
+
+ // if is valid area and is not gamemaster then deal damage
+ if ( ValidArea && !isGameMaster() )
+ EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
+
+ m_breathTimer = 1000;
+ }
+
+ }
+ //Death timer disabled and WaterFlags reset
+ else if (m_deathState == DEAD)
+ {
+ m_breathTimer = 0;
+ m_isunderwater = 0;
+ }
+}
+
+///The player sobers by 256 every 10 seconds
+void Player::HandleSobering()
+{
+ m_drunkTimer = 0;
+
+ uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
+ SetDrunkValue(drunk);
+}
+
+DrunkenState Player::GetDrunkenstateByValue(uint16 value)
+{
+ if(value >= 23000)
+ return DRUNKEN_SMASHED;
+ if(value >= 12800)
+ return DRUNKEN_DRUNK;
+ if(value & 0xFFFE)
+ return DRUNKEN_TIPSY;
+ return DRUNKEN_SOBER;
+}
+
+void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId)
+{
+ uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
+
+ m_drunk = newDrunkenValue;
+ SetUInt32Value(PLAYER_BYTES_3,(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));
+
+ uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
+
+ // special drunk invisibility detection
+ if(newDrunkenState >= DRUNKEN_DRUNK)
+ m_detectInvisibilityMask |= (1<<6);
+ else
+ m_detectInvisibilityMask &= ~(1<<6);
+
+ if(newDrunkenState == oldDrunkenState)
+ return;
+
+ WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8+4+4));
+ data << GetGUID();
+ data << uint32(newDrunkenState);
+ data << uint32(itemId);
+
+ SendMessageToSet(&data, true);
+}
+
+void Player::Update( uint32 p_time )
+{
+ if(!IsInWorld())
+ return;
+
+ // undelivered mail
+ if(m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
+ {
+ SendNewMail();
+ ++unReadMails;
+
+ // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
+ m_nextMailDelivereTime = 0;
+ }
+
+ Unit::Update( p_time );
+
+ // update player only attacks
+ if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
+ {
+ setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time) );
+ }
+
+ if(uint32 off_att = getAttackTimer(OFF_ATTACK))
+ {
+ setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time) );
+ }
+
+ time_t now = time (NULL);
+
+ UpdatePvPFlag(now);
+
+ UpdateContestedPvP(p_time);
+
+ UpdateDuelFlag(now);
+
+ CheckDuelDistance(now);
+
+ UpdateAfkReport(now);
+
+ CheckExploreSystem();
+
+ // Update items that have just a limited lifetime
+ if (now>m_Last_tick)
+ UpdateItemDuration(uint32(now- m_Last_tick));
+
+ if (!m_timedquests.empty())
+ {
+ std::set<uint32>::iterator iter = m_timedquests.begin();
+ while (iter != m_timedquests.end())
+ {
+ QuestStatusData& q_status = mQuestStatus[*iter];
+ if( q_status.m_timer <= p_time )
+ {
+ uint32 quest_id = *iter;
+ ++iter; // current iter will be removed in FailTimedQuest
+ FailTimedQuest( quest_id );
+ }
+ else
+ {
+ q_status.m_timer -= p_time;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ ++iter;
+ }
+ }
+ }
+
+ if (hasUnitState(UNIT_STAT_MELEE_ATTACKING))
+ {
+ Unit *pVictim = getVictim();
+ if( !IsNonMeleeSpellCasted(false) && pVictim)
+ {
+ // default combat reach 10
+ // TODO add weapon,skill check
+
+ float pldistance = ATTACK_DISTANCE;
+
+ if (isAttackReady(BASE_ATTACK))
+ {
+ if(!IsWithinDistInMap(pVictim, pldistance))
+ {
+ setAttackTimer(BASE_ATTACK,100);
+ if(m_swingErrorMsg != 1) // send single time (client auto repeat)
+ {
+ SendAttackSwingNotInRange();
+ m_swingErrorMsg = 1;
+ }
+ }
+ //120 degrees of radiant range
+ else if( !HasInArc( 2*M_PI/3, pVictim ))
+ {
+ setAttackTimer(BASE_ATTACK,100);
+ if(m_swingErrorMsg != 2) // send single time (client auto repeat)
+ {
+ SendAttackSwingBadFacingAttack();
+ m_swingErrorMsg = 2;
+ }
+ }
+ else
+ {
+ m_swingErrorMsg = 0; // reset swing error state
+
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ if(haveOffhandWeapon())
+ {
+ uint32 off_att = getAttackTimer(OFF_ATTACK);
+ if(off_att < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(OFF_ATTACK,ATTACK_DISPLAY_DELAY);
+ }
+ AttackerStateUpdate(pVictim, BASE_ATTACK);
+ resetAttackTimer(BASE_ATTACK);
+ }
+ }
+
+ if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
+ {
+ if(!IsWithinDistInMap(pVictim, pldistance))
+ {
+ setAttackTimer(OFF_ATTACK,100);
+ }
+ else if( !HasInArc( 2*M_PI/3, pVictim ))
+ {
+ setAttackTimer(OFF_ATTACK,100);
+ }
+ else
+ {
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ uint32 base_att = getAttackTimer(BASE_ATTACK);
+ if(base_att < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(BASE_ATTACK,ATTACK_DISPLAY_DELAY);
+ // do attack
+ AttackerStateUpdate(pVictim, OFF_ATTACK);
+ resetAttackTimer(OFF_ATTACK);
+ }
+ }
+
+ Unit *owner = pVictim->GetOwner();
+ Unit *u = owner ? owner : pVictim;
+ if(u->IsPvP() && (!duel || duel->opponent != u))
+ {
+ UpdatePvP(true);
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ }
+ }
+ }
+
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
+ {
+ if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update
+ {
+ int time_inn = time(NULL)-GetTimeInnEnter();
+ if (time_inn >= 10) //freeze update
+ {
+ float bubble = 0.125*sWorld.getRate(RATE_REST_INGAME);
+ //speed collect rest bonus (section/in hour)
+ SetRestBonus( GetRestBonus()+ time_inn*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble );
+ UpdateInnerTime(time(NULL));
+ }
+ }
+ }
+
+ if(m_regenTimer > 0)
+ {
+ if(p_time >= m_regenTimer)
+ m_regenTimer = 0;
+ else
+ m_regenTimer -= p_time;
+ }
+
+ if (m_weaponChangeTimer > 0)
+ {
+ if(p_time >= m_weaponChangeTimer)
+ m_weaponChangeTimer = 0;
+ else
+ m_weaponChangeTimer -= p_time;
+ }
+
+ if (m_zoneUpdateTimer > 0)
+ {
+ if(p_time >= m_zoneUpdateTimer)
+ {
+ uint32 newzone = GetZoneId();
+ if( m_zoneUpdateId != newzone )
+ UpdateZone(newzone); // also update area
+ else
+ {
+ // use area updates as well
+ // needed for free far all arenas for example
+ uint32 newarea = GetAreaId();
+ if( m_areaUpdateId != newarea )
+ UpdateArea(newarea);
+
+ m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+ }
+ }
+ else
+ m_zoneUpdateTimer -= p_time;
+ }
+
+ if (isAlive())
+ {
+ RegenerateAll();
+ }
+
+ if (m_deathState == JUST_DIED)
+ {
+ KillPlayer();
+ }
+
+ if(m_nextSave > 0)
+ {
+ if(p_time >= m_nextSave)
+ {
+ // m_nextSave reseted in SaveToDB call
+ SaveToDB();
+ sLog.outDetail("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
+ }
+ else
+ {
+ m_nextSave -= p_time;
+ }
+ }
+
+ //Breathtimer
+ if(m_breathTimer > 0)
+ {
+ if(p_time >= m_breathTimer)
+ m_breathTimer = 0;
+ else
+ m_breathTimer -= p_time;
+
+ }
+
+ //Handle Water/drowning
+ HandleDrowning();
+
+ //Handle lava
+ HandleLava();
+
+ //Handle detect stealth players
+ if (m_DetectInvTimer > 0)
+ {
+ if (p_time >= m_DetectInvTimer)
+ {
+ m_DetectInvTimer = 3000;
+ HandleStealthedUnitsDetection();
+ }
+ else
+ m_DetectInvTimer -= p_time;
+ }
+
+ // Played 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_Last_tick = now;
+ }
+
+ if (m_drunk)
+ {
+ m_drunkTimer += p_time;
+
+ if (m_drunkTimer > 10000)
+ HandleSobering();
+ }
+
+ // not auto-free ghost from body in instances
+ if(m_deathTimer > 0 && !GetBaseMap()->Instanceable())
+ {
+ if(p_time >= m_deathTimer)
+ {
+ m_deathTimer = 0;
+ BuildPlayerRepop();
+ RepopAtGraveyard();
+ }
+ else
+ m_deathTimer -= p_time;
+ }
+
+ UpdateEnchantTime(p_time);
+ UpdateHomebindTime(p_time);
+
+ // group update
+ SendUpdateToOutOfRangeGroupMembers();
+
+ Pet* pet = GetPet();
+ if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
+ {
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
+ return;
+ }
+}
+
+void Player::setDeathState(DeathState s)
+{
+ uint32 ressSpellId = 0;
+
+ bool cur = isAlive();
+
+ if(s == JUST_DIED && cur)
+ {
+ // drunken state is cleared on death
+ SetDrunkValue(0);
+ // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
+ ClearComboPoints();
+
+ clearResurrectRequestData();
+
+ // remove form before other mods to prevent incorrect stats calculation
+ RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
+
+ //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
+ RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+
+ // remove uncontrolled pets
+ RemoveMiniPet();
+ RemoveGuardians();
+
+ // save value before aura remove in Unit::setDeathState
+ ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
+
+ // passive spell
+ if(!ressSpellId)
+ ressSpellId = GetResurrectionSpellId();
+ }
+ Unit::setDeathState(s);
+
+ // restore resurrection spell id for player after aura remove
+ if(s == JUST_DIED && cur && ressSpellId)
+ SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
+
+ if(isAlive() && !cur)
+ {
+ //clear aura case after resurrection by another way (spells will be applied before next death)
+ SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
+
+ // restore default warrior stance
+ if(getClass()== CLASS_WARRIOR)
+ CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
+ }
+}
+
+void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
+{
+ *p_data << GetGUID();
+ *p_data << m_name;
+
+ *p_data << getRace();
+ uint8 pClass = getClass();
+ *p_data << pClass;
+ *p_data << getGender();
+
+ uint32 bytes = GetUInt32Value(PLAYER_BYTES);
+ *p_data << uint8(bytes);
+ *p_data << uint8(bytes >> 8);
+ *p_data << uint8(bytes >> 16);
+ *p_data << uint8(bytes >> 24);
+
+ bytes = GetUInt32Value(PLAYER_BYTES_2);
+ *p_data << uint8(bytes);
+
+ *p_data << uint8(getLevel()); // player level
+ // do not use GetMap! it will spawn a new instance since the bound instances are not loaded
+ uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY());
+
+ *p_data << zoneId;
+ *p_data << GetMapId();
+
+ *p_data << GetPositionX();
+ *p_data << GetPositionY();
+ *p_data << GetPositionZ();
+
+ *p_data << GetUInt32Value(PLAYER_GUILDID); // guild id
+
+ uint32 char_flags = 0;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
+ char_flags |= CHARACTER_FLAG_HIDE_HELM;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
+ char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ char_flags |= CHARACTER_FLAG_GHOST;
+ if(HasAtLoginFlag(AT_LOGIN_RENAME))
+ char_flags |= CHARACTER_FLAG_RENAME;
+ // always send the flag if declined names aren't used
+ // to let the client select a default method of declining the name
+ if(!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) || (result && result->Fetch()[12].GetCppString() != ""))
+ char_flags |= CHARACTER_FLAG_DECLINED;
+
+ *p_data << (uint32)char_flags; // character flags
+
+ *p_data << (uint8)1; // unknown
+
+ // Pets info
+ {
+ uint32 petDisplayId = 0;
+ 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))
+ {
+ Field* fields = result->Fetch();
+
+ uint32 entry = fields[9].GetUInt32();
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
+ if(cInfo)
+ {
+ petDisplayId = fields[10].GetUInt32();
+ petLevel = fields[11].GetUInt32();
+ petFamily = cInfo->family;
+ }
+ }
+
+ *p_data << (uint32)petDisplayId;
+ *p_data << (uint32)petLevel;
+ *p_data << (uint32)petFamily;
+ }
+
+ /*ItemPrototype const *items[EQUIPMENT_SLOT_END];
+ for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ items[i] = NULL;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT slot,item_template FROM character_inventory WHERE guid = '%u' AND bag = 0",GetGUIDLow());
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 slot = fields[0].GetUInt8() & 255;
+ uint32 item_id = fields[1].GetUInt32();
+ if( slot >= EQUIPMENT_SLOT_END )
+ continue;
+
+ items[slot] = objmgr.GetItemPrototype(item_id);
+ if(!items[slot])
+ {
+ sLog.outError( "Player::BuildEnumData: Player %s have unknown item (id: #%u) in inventory, skipped.", GetName(),item_id );
+ continue;
+ }
+ } while (result->NextRow());
+ delete result;
+ }*/
+
+ for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
+ {
+ uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ uint32 item_id = GetUInt32Value(visualbase);
+ const ItemPrototype * proto = objmgr.GetItemPrototype(item_id);
+ SpellItemEnchantmentEntry const *enchant = NULL;
+
+ for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot<=TEMP_ENCHANTMENT_SLOT; enchantSlot++)
+ {
+ uint32 enchantId = GetUInt32Value(visualbase+1+enchantSlot);
+ 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)0; // first bag display id
+ *p_data << (uint8)0; // first bag inventory type
+ *p_data << (uint32)0; // enchant?
+}
+
+bool Player::ToggleAFK()
+{
+ ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
+
+ bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
+
+ // afk player not allowed in battleground
+ if(state && InBattleGround())
+ LeaveBattleground();
+
+ return state;
+}
+
+bool Player::ToggleDND()
+{
+ ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
+
+ return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
+}
+
+uint8 Player::chatTag() const
+{
+ // it's bitmask
+ // 0x8 - ??
+ // 0x4 - gm
+ // 0x2 - dnd
+ // 0x1 - afk
+ if(isGameMaster())
+ return 4;
+ else if(isDND())
+ return 3;
+ if(isAFK())
+ return 1;
+ else
+ return 0;
+}
+
+bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
+{
+ if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
+ {
+ sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
+ return false;
+ }
+
+ // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
+ Pet* pet = GetPet();
+
+ MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
+
+ // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
+ if(!InBattleGround() && mEntry->IsBattleGround() && !GetSession()->GetSecurity())
+ return false;
+
+ bool tbc = GetSession()->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+
+ // normal client and TBC map
+ if(!tbc && mEntry->IsExpansionMap())
+ {
+ sLog.outDebug("Player %s using Normal client and tried teleport to non existing map %u", GetName(), mapid);
+
+ if(GetTransport())
+ RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
+
+ SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL1);
+
+ return false; // normal client can't teleport to this map...
+ }
+ else if(tbc) // can teleport to any existing map
+ {
+ sLog.outDebug("Player %s have TBC client and will teleported to map %u", GetName(), mapid);
+ }
+ else
+ {
+ sLog.outDebug("Player %s have normal client and will teleported to standard map %u", GetName(), mapid);
+ }
+ /*
+ only TBC (no 0x80000 and 0x10 flags...)
+ 3604590=0x37006E=0x200000 + 0x100000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
+
+ Kharazan (normal/TBC??), but not have 0x10 flag (accessible by normal client?)
+ 4128878=0x3F006E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
+
+ normal+TBC maps
+ 4128894=0x3F007E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
+
+ normal+TBC maps
+ 8323198=0x7F007E=0x400000 + 0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
+ */
+
+ // if we were on a transport, leave
+ if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT) && m_transport)
+ {
+ m_transport->RemovePassenger(this);
+ m_transport = NULL;
+ 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_time = 0;
+ }
+
+ SetSemaphoreTeleport(true);
+
+ // The player was ported to another map and looses the duel immediatly.
+ // We have to perform this check before the teleport, otherwise the
+ // ObjectAccessor won't find the flag.
+ if (duel && this->GetMapId()!=mapid)
+ {
+ GameObject* obj = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
+ if (obj)
+ DuelComplete(DUEL_FLED);
+ }
+
+ // reset movement flags at teleport, because player will continue move with these flags after teleport
+ SetUnitMovementFlags(0);
+
+ if ((this->GetMapId() == mapid) && (!m_transport))
+ {
+ // prepare zone change detect
+ uint32 old_zone = GetZoneId();
+
+ // near teleport
+ if(!GetSession()->PlayerLogout())
+ {
+ WorldPacket data;
+ BuildTeleportAckMsg(&data, x, y, z, orientation);
+ GetSession()->SendPacket(&data);
+ SetPosition( x, y, z, orientation, true);
+ }
+ else
+ // this will be used instead of the current location in SaveToDB
+ m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
+
+ //BuildHeartBeatMsg(&data);
+ //SendMessageToSet(&data, true);
+ if (!(options & TELE_TO_NOT_UNSUMMON_PET))
+ {
+ //same map, only remove pet if out of range
+ if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
+ {
+ if(pet->isControlled() && !pet->isTemporarySummoned() )
+ m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
+ else
+ m_temporaryUnsummonedPetNumber = 0;
+
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
+ }
+ }
+
+ if(!(options & TELE_TO_NOT_LEAVE_COMBAT))
+ CombatStop();
+
+ if (!(options & TELE_TO_NOT_UNSUMMON_PET))
+ {
+ // resummon pet
+ if(pet && m_temporaryUnsummonedPetNumber)
+ {
+ Pet* NewPet = new Pet;
+ if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
+ delete NewPet;
+
+ m_temporaryUnsummonedPetNumber = 0;
+ }
+ }
+
+ SetSemaphoreTeleport(false);
+
+ if(!GetSession()->PlayerLogout())
+ UpdateZone(GetZoneId());
+
+ // new zone
+ if(old_zone != GetZoneId())
+ {
+ // honorless target
+ if(pvpInfo.inHostileArea)
+ CastSpell(this, 2479, true);
+ }
+ }
+ else
+ {
+ // far teleport to another map
+ Map* oldmap = IsInWorld() ? MapManager::Instance().GetMap(GetMapId(), this) : NULL;
+ // check if we can enter before stopping combat / removing pet / totems / interrupting spells
+
+ // Check enter rights before map getting to avoid creating instance copy for player
+ // this check not dependent from map instance copy and same for all instance copies of selected map
+ if (!MapManager::Instance().CanPlayerEnter(mapid, this))
+ {
+ SetSemaphoreTeleport(false);
+ return false;
+ }
+
+ // If the map is not created, assume it is possible to enter it.
+ // It will be created in the WorldPortAck.
+ Map *map = MapManager::Instance().FindMap(mapid);
+ if (!map || map->CanEnter(this))
+ {
+ SetSelection(0);
+
+ CombatStop();
+
+ ResetContestedPvP();
+
+ // remove player from battleground on far teleport (when changing maps)
+ if(BattleGround const* bg = GetBattleGround())
+ {
+ // Note: at battleground join battleground id set before teleport
+ // and we already will found "current" battleground
+ // just need check that this is targeted map or leave
+ if(bg->GetMapId() != mapid)
+ LeaveBattleground(false); // don't teleport to entry point
+ }
+
+ // remove pet on map change
+ if (pet)
+ {
+ //leaving map -> delete pet right away (doing this later will cause problems)
+ if(pet->isControlled() && !pet->isTemporarySummoned())
+ m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
+ else
+ m_temporaryUnsummonedPetNumber = 0;
+
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
+ }
+
+ // remove all dyn objects
+ RemoveAllDynObjects();
+
+ // stop spellcasting
+ // not attempt interrupt teleportation spell at caster teleport
+ if(!(options & TELE_TO_SPELL))
+ if(IsNonMeleeSpellCasted(true))
+ InterruptNonMeleeSpells(true);
+
+ if(!GetSession()->PlayerLogout())
+ {
+ // send transfer packets
+ WorldPacket data(SMSG_TRANSFER_PENDING, (4+4+4));
+ data << uint32(mapid);
+ if (m_transport)
+ {
+ data << m_transport->GetEntry() << GetMapId();
+ }
+ GetSession()->SendPacket(&data);
+
+ data.Initialize(SMSG_NEW_WORLD, (20));
+ if (m_transport)
+ {
+ data << (uint32)mapid << m_movementInfo.t_x << m_movementInfo.t_y << m_movementInfo.t_z << m_movementInfo.t_o;
+ }
+ else
+ {
+ data << (uint32)mapid << (float)x << (float)y << (float)z << (float)orientation;
+ }
+ GetSession()->SendPacket( &data );
+ SendSavedInstances();
+
+ // remove from old map now
+ if(oldmap) oldmap->Remove(this, false);
+ }
+
+ // new final coordinates
+ float final_x = x;
+ float final_y = y;
+ float final_z = z;
+ float final_o = orientation;
+
+ if(m_transport)
+ {
+ final_x += m_movementInfo.t_x;
+ final_y += m_movementInfo.t_y;
+ final_z += m_movementInfo.t_z;
+ final_o += m_movementInfo.t_o;
+ }
+
+ m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o);
+ // 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);
+
+ // move packet sent by client always after far teleport
+ // SetPosition(final_x, final_y, final_z, final_o, true);
+ SetDontMove(true);
+
+ // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
+ }
+ else
+ return false;
+ }
+ return true;
+}
+
+void Player::AddToWorld()
+{
+ ///- Do not add/remove the player from the object storage
+ ///- It will crash when updating the ObjectAccessor
+ ///- The player should only be added when logging in
+ Unit::AddToWorld();
+
+ for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(m_items[i])
+ m_items[i]->AddToWorld();
+ }
+}
+
+void Player::RemoveFromWorld()
+{
+ // cleanup
+ if(IsInWorld())
+ {
+ ///- Release charmed creatures, unsummon totems and remove pets/guardians
+ Uncharm();
+ UnsummonAllTotems();
+ RemoveMiniPet();
+ RemoveGuardians();
+ }
+
+ for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(m_items[i])
+ m_items[i]->RemoveFromWorld();
+ }
+
+ ///- Do not add/remove the player from the object storage
+ ///- It will crash when updating the ObjectAccessor
+ ///- The player should only be removed when logging out
+ Unit::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,0))
+ addRage *= 1.3;
+ }
+
+ addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME);
+
+ ModifyPower(POWER_RAGE, uint32(addRage*10));
+}
+
+void Player::RegenerateAll()
+{
+ if (m_regenTimer != 0)
+ return;
+ uint32 regenDelay = 2000;
+
+ // Not in combat or they have regeneration
+ if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) ||
+ HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) || IsPolymorphed() )
+ {
+ RegenerateHealth();
+ if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
+ Regenerate(POWER_RAGE);
+ }
+
+ Regenerate( POWER_ENERGY );
+
+ Regenerate( POWER_MANA );
+
+ m_regenTimer = regenDelay;
+}
+
+void Player::Regenerate(Powers power)
+{
+ uint32 curValue = GetPower(power);
+ uint32 maxValue = GetMaxPower(power);
+
+ float addvalue = 0.0f;
+
+ switch (power)
+ {
+ case POWER_MANA:
+ {
+ bool recentCast = IsUnderLastManaUseEffect();
+ float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
+ if (recentCast)
+ {
+ // Mangos Updates Mana in intervals of 2s, which is correct
+ addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f;
+ }
+ else
+ {
+ addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f;
+ }
+ } break;
+ case POWER_RAGE: // Regenerate rage
+ {
+ float RageDecreaseRate = sWorld.getRate(RATE_POWER_RAGE_LOSS);
+ addvalue = 30 * RageDecreaseRate; // 3 rage by tick
+ } break;
+ case POWER_ENERGY: // Regenerate energy (rogue)
+ addvalue = 20;
+ break;
+ case POWER_FOCUS:
+ case POWER_HAPPINESS:
+ break;
+ }
+
+ // Mana regen calculated in Player::UpdateManaRegen()
+ // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
+ if(power != POWER_MANA)
+ {
+ AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
+ for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == power)
+ addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
+ }
+
+ if (power != POWER_RAGE)
+ {
+ curValue += uint32(addvalue);
+ if (curValue > maxValue)
+ curValue = maxValue;
+ }
+ else
+ {
+ if(curValue <= uint32(addvalue))
+ curValue = 0;
+ else
+ curValue -= uint32(addvalue);
+ }
+ SetPower(power, curValue);
+}
+
+void Player::RegenerateHealth()
+{
+ uint32 curValue = GetHealth();
+ uint32 maxValue = GetMaxHealth();
+
+ if (curValue >= maxValue) return;
+
+ float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
+
+ float addvalue = 0.0f;
+
+ // polymorphed case
+ if ( IsPolymorphed() )
+ addvalue = GetMaxHealth()/3;
+ // normal regen case (maybe partly in combat case)
+ else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) )
+ {
+ addvalue = OCTRegenHPPerSpirit()* HealthIncreaseRate;
+ if (!isInCombat())
+ {
+ AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
+ for(AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
+ addvalue *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
+ }
+ else if(HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
+ addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f;
+
+ if(!IsStandState())
+ addvalue *= 1.5;
+ }
+
+ // always regeneration bonus (including combat)
+ addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
+
+ if(addvalue < 0)
+ addvalue = 0;
+
+ ModifyHealth(int32(addvalue));
+}
+
+bool Player::CanInteractWithNPCs(bool alive) const
+{
+ if(alive && !isAlive())
+ return false;
+ if(isInFlight())
+ return false;
+
+ return true;
+}
+
+bool Player::IsUnderWater() const
+{
+ return IsInWater() &&
+ GetPositionZ() < (MapManager::Instance().GetBaseMap(GetMapId())->GetWaterLevel(GetPositionX(),GetPositionY())-2);
+}
+
+void Player::SetInWater(bool apply)
+{
+ if(m_isInWater==apply)
+ return;
+
+ //define player in water by opcodes
+ //move player's guid into HateOfflineList of those mobs
+ //which can't swim and move guid back into ThreatList when
+ //on surface.
+ //TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
+ m_isInWater = apply;
+
+ // remove auras that need water/land
+ RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
+
+ getHostilRefManager().updateThreatTables();
+}
+
+void Player::SetGameMaster(bool on)
+{
+ if(on)
+ {
+ m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
+ setFaction(35);
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ ResetContestedPvP();
+
+ getHostilRefManager().setOnlineOfflineState(false);
+ CombatStop();
+ }
+ else
+ {
+ m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
+ setFactionForRace(getRace());
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
+ // restore FFA PvP Server state
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ // restore FFA PvP area state, remove not allowed for GM mounts
+ UpdateArea(m_areaUpdateId);
+
+ getHostilRefManager().setOnlineOfflineState(true);
+ }
+
+ ObjectAccessor::UpdateVisibilityForPlayer(this);
+}
+
+void Player::SetGMVisible(bool on)
+{
+ if(on)
+ {
+ m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
+
+ // Reapply stealth/invisibility if active or show if not any
+ if(HasAuraType(SPELL_AURA_MOD_STEALTH))
+ SetVisibility(VISIBILITY_GROUP_STEALTH);
+ else if(HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
+ SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ else
+ SetVisibility(VISIBILITY_ON);
+ }
+ else
+ {
+ m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
+
+ SetAcceptWhispers(false);
+ SetGameMaster(true);
+
+ SetVisibility(VISIBILITY_OFF);
+ }
+}
+
+bool Player::IsGroupVisibleFor(Player* p) const
+{
+ switch(sWorld.getConfig(CONFIG_GROUP_VISIBILITY))
+ {
+ default: return IsInSameGroupWith(p);
+ case 1: return IsInSameRaidWith(p);
+ case 2: return GetTeam()==p->GetTeam();
+ }
+}
+
+bool Player::IsInSameGroupWith(Player const* p) const
+{
+ return p==this || GetGroup() != NULL &&
+ GetGroup() == p->GetGroup() &&
+ GetGroup()->SameSubGroup((Player*)this, (Player*)p);
+}
+
+///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
+/// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
+void Player::UninviteFromGroup()
+{
+ if(GetGroupInvite()) // uninvited invitee
+ {
+ Group* group = GetGroupInvite();
+ group->RemoveInvite(this);
+
+ if(group->GetMembersCount() <= 1) // group has just 1 member => disband
+ {
+ if(group->IsCreated())
+ {
+ group->Disband(true);
+ objmgr.RemoveGroup(group);
+ }
+ else
+ group->RemoveAllInvites();
+
+ delete group;
+ }
+ }
+}
+
+void Player::RemoveFromGroup(Group* group, uint64 guid)
+{
+ if(group)
+ {
+ if (group->RemoveMember(guid, 0) <= 1)
+ {
+ // group->Disband(); already disbanded in RemoveMember
+ objmgr.RemoveGroup(group);
+ delete group;
+ // removemember sets the player's group pointer to NULL
+ }
+ }
+}
+
+void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
+{
+ WorldPacket data(SMSG_LOG_XPGAIN, 21);
+ data << uint64(victim ? victim->GetGUID() : 0); // guid
+ data << uint32(GivenXP+RestXP); // given experience
+ data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
+ if(victim)
+ {
+ data << uint32(GivenXP); // experience without rested bonus
+ data << float(1); // 1 - none 0 - 100% group bonus output
+ }
+ data << uint8(0); // new 2.4.0
+ GetSession()->SendPacket(&data);
+}
+
+void Player::GiveXP(uint32 xp, Unit* victim)
+{
+ if ( xp < 1 )
+ return;
+
+ if(!isAlive())
+ return;
+
+ uint32 level = getLevel();
+
+ // XP to money conversion processed in Player::RewardQuest
+ if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ return;
+
+ // handle SPELL_AURA_MOD_XP_PCT auras
+ Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
+ for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
+ xp = uint32(xp*(1.0f + (*i)->GetModifier()->m_amount / 100.0f));
+
+ // XP resting bonus for kill
+ uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0;
+
+ SendLogXPGain(xp,victim,rested_bonus_xp);
+
+ uint32 curXP = GetUInt32Value(PLAYER_XP);
+ uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
+ uint32 newXP = curXP + xp + rested_bonus_xp;
+
+ while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ newXP -= nextLvlXP;
+
+ if ( level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ GiveLevel(level + 1);
+
+ level = getLevel();
+ nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
+ }
+
+ SetUInt32Value(PLAYER_XP, newXP);
+}
+
+// Update player to next level
+// Current player experience not update (must be update by caller)
+void Player::GiveLevel(uint32 level)
+{
+ if ( level == getLevel() )
+ return;
+
+ PlayerLevelInfo info;
+ objmgr.GetPlayerLevelInfo(getRace(),getClass(),level,&info);
+
+ PlayerClassLevelInfo classInfo;
+ objmgr.GetPlayerClassLevelInfo(getClass(),level,&classInfo);
+
+ // send levelup info to client
+ WorldPacket data(SMSG_LEVELUP_INFO, (4+4+MAX_POWERS*4+MAX_STATS*4));
+ data << uint32(level);
+ data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
+ // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-6)
+ data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ // end for
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
+ data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
+
+ GetSession()->SendPacket(&data);
+
+ SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(level));
+
+ //update level, max level of skills
+ if(getLevel()!= level)
+ m_Played_time[1] = 0; // Level Played Time reset
+ SetLevel(level);
+ UpdateMaxSkills();
+
+ // save base values (bonuses already included in stored stats
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetCreateStat(Stats(i), info.stats[i]);
+
+ SetCreateHealth(classInfo.basehealth);
+ SetCreateMana(classInfo.basemana);
+
+ InitTalentForLevel();
+ InitTaxiNodesForLevel();
+
+ UpdateAllStats();
+
+ // set current level health and mana/energy to maximum after applying all mods.
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
+ if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
+ SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
+ SetPower(POWER_FOCUS, 0);
+ SetPower(POWER_HAPPINESS, 0);
+
+ // give level to summoned pet
+ Pet* pet = GetPet();
+ if(pet && pet->getPetType()==SUMMON_PET)
+ pet->GivePetLevel(level);
+}
+
+void Player::InitTalentForLevel()
+{
+ uint32 level = getLevel();
+ // talents base at level diff ( talents = level - 9 but some can be used already)
+ if(level < 10)
+ {
+ // Remove all talent points
+ if(m_usedTalentCount > 0) // Free any used talents
+ {
+ resetTalents(true);
+ SetFreeTalentPoints(0);
+ }
+ }
+ else
+ {
+ uint32 talentPointsForLevel = uint32((level-9)*sWorld.getRate(RATE_TALENT));
+ // if used more that have then reset
+ if(m_usedTalentCount > talentPointsForLevel)
+ {
+ if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
+ resetTalents(true);
+ else
+ SetFreeTalentPoints(0);
+ }
+ // else update amount of free points
+ else
+ SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
+ }
+}
+
+void Player::InitStatsForLevel(bool reapplyMods)
+{
+ if(reapplyMods) //reapply stats values only on .reset stats (level) command
+ _RemoveAllStatBonuses();
+
+ PlayerClassLevelInfo classInfo;
+ objmgr.GetPlayerClassLevelInfo(getClass(),getLevel(),&classInfo);
+
+ PlayerLevelInfo info;
+ objmgr.GetPlayerLevelInfo(getRace(),getClass(),getLevel(),&info);
+
+ SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) );
+ SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(getLevel()));
+
+ UpdateMaxSkills ();
+
+ // set default cast time multiplier
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
+
+ // reset size before reapply auras
+ SetFloatValue(OBJECT_FIELD_SCALE_X,1.0f);
+
+ // save base values (bonuses already included in stored stats
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetCreateStat(Stats(i), info.stats[i]);
+
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetStat(Stats(i), info.stats[i]);
+
+ SetCreateHealth(classInfo.basehealth);
+
+ //set create powers
+ SetCreateMana(classInfo.basemana);
+
+ SetArmor(int32(m_createStats[STAT_AGILITY]*2));
+
+ InitStatBuffMods();
+
+ //reset rating fields values
+ for(uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
+ SetUInt32Value(index, 0);
+
+ SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS,0);
+ for (int i = 0; i < 7; i++)
+ {
+ SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, 0);
+ SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, 0);
+ SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i, 1.00f);
+ }
+
+ //reset attack power, damage and attack speed fields
+ SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f );
+ SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f ); // offhand attack time
+ SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f );
+
+ SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f );
+
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER, 0 );
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 );
+ SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f);
+ SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 );
+ SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 );
+ SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f);
+
+ // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
+ SetFloatValue(PLAYER_CRIT_PERCENTAGE,0.0f);
+ SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE,0.0f);
+ SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,0.0f);
+
+ // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
+ for (uint8 i = 0; i < 7; ++i)
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
+
+ // Base parry percents
+ SetFloatValue(PLAYER_PARRY_PERCENTAGE, 5.0f);
+
+ //Base block percentage
+ SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 5.0f);
+
+ SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);
+
+ // Dodge percentage
+ SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
+
+ // set armor (resistance 0) to original value (create_agility*2)
+ SetArmor(int32(m_createStats[STAT_AGILITY]*2));
+ SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
+ SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
+ // set other resistance to original value (0)
+ for (int i = 1; i < MAX_SPELL_SCHOOL; i++)
+ {
+ SetResistance(SpellSchools(i), 0);
+ SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
+ SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
+ }
+
+ SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,0);
+ SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,0);
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ {
+ SetFloatValue(UNIT_FIELD_POWER_COST_MODIFIER+i,0.0f);
+ SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f);
+ }
+ // Init data for form but skip reapply item mods for form
+ InitDataForForm(reapplyMods);
+
+ // save new stats
+ for (int i = POWER_MANA; i < MAX_POWERS; i++)
+ SetMaxPower(Powers(i), uint32(GetCreatePowers(Powers(i))));
+
+ SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later
+
+ // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
+
+ // cleanup unit flags (will be re-applied if need at aura load).
+ RemoveFlag( UNIT_FIELD_FLAGS,
+ UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
+ UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
+ UNIT_FLAG_DISABLE_ROTATE | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
+ UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
+ UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set
+
+ // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_FLAGS_FFA_PVP);
+
+ SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); // one form stealth modified bytes
+
+ // restore if need some important flags
+ SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default
+
+ if(reapplyMods) //reapply stats values only on .reset stats (level) command
+ _ApplyAllStatBonuses();
+
+ // set current level health and mana/energy to maximum after applying all mods.
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
+ if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
+ SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
+ SetPower(POWER_FOCUS, 0);
+ SetPower(POWER_HAPPINESS, 0);
+}
+
+void Player::SendInitialSpells()
+{
+ uint16 spellCount = 0;
+
+ WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
+ data << uint8(0);
+
+ size_t countPos = data.wpos();
+ data << uint16(spellCount); // spell count placeholder
+
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ if(!itr->second->active || itr->second->disabled)
+ continue;
+
+ data << uint16(itr->first);
+ //data << uint16(itr->second->slotId);
+ data << uint16(0); // it's not slot id
+
+ spellCount +=1;
+ }
+
+ data.put<uint16>(countPos,spellCount); // write real count value
+
+ uint16 spellCooldowns = m_spellCooldowns.size();
+ data << uint16(spellCooldowns);
+ for(SpellCooldowns::const_iterator itr=m_spellCooldowns.begin(); itr!=m_spellCooldowns.end(); itr++)
+ {
+ SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
+ if(!sEntry)
+ continue;
+
+ data << uint16(itr->first);
+
+ time_t cooldown = 0;
+ time_t curTime = time(NULL);
+ if(itr->second.end > curTime)
+ cooldown = (itr->second.end-curTime)*1000;
+
+ data << uint16(itr->second.itemid); // cast item id
+ data << uint16(sEntry->Category); // spell category
+ if(sEntry->Category) // may be wrong, but anyway better than nothing...
+ {
+ data << uint32(0);
+ data << uint32(cooldown);
+ }
+ else
+ {
+ data << uint32(cooldown);
+ data << uint32(0);
+ }
+ }
+
+ GetSession()->SendPacket(&data);
+
+ sLog.outDetail( "CHARACTER: Sent Initial Spells" );
+}
+
+void Player::RemoveMail(uint32 id)
+{
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
+ {
+ if ((*itr)->messageID == id)
+ {
+ //do not delete item, because Player::removeMail() is called when returning mail to sender.
+ m_mail.erase(itr);
+ return;
+ }
+ }
+}
+
+void Player::SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
+{
+ WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_BAG_FULL?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
+ data << (uint32) mailId;
+ data << (uint32) mailAction;
+ data << (uint32) mailError;
+ if ( mailError == MAIL_ERR_BAG_FULL )
+ data << (uint32) equipError;
+ else if( mailAction == MAIL_ITEM_TAKEN )
+ {
+ data << (uint32) item_guid; // item guid low?
+ data << (uint32) item_count; // item count?
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendNewMail()
+{
+ // deliver undelivered mail
+ WorldPacket data(SMSG_RECEIVED_MAIL, 4);
+ data << (uint32) 0;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::UpdateNextMailTimeAndUnreads()
+{
+ // calculate next delivery time (min. from non-delivered mails
+ // and recalculate unReadMail
+ time_t cTime = time(NULL);
+ m_nextMailDelivereTime = 0;
+ unReadMails = 0;
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
+ {
+ if((*itr)->deliver_time > cTime)
+ {
+ if(!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
+ m_nextMailDelivereTime = (*itr)->deliver_time;
+ }
+ else if(((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
+ ++unReadMails;
+ }
+}
+
+void Player::AddNewMailDeliverTime(time_t deliver_time)
+{
+ if(deliver_time <= time(NULL)) // ready now
+ {
+ ++unReadMails;
+ SendNewMail();
+ }
+ else // not ready and no have ready mails
+ {
+ if(!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
+ m_nextMailDelivereTime = deliver_time;
+ }
+}
+
+bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, uint16 slot_id, bool disabled)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do character spell book cleanup (all characters)
+ if(loading && !learning) // spell load case
+ {
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ if(!SpellMgr::IsSpellValid(spellInfo,this,false))
+ {
+ // do character spell book cleanup (all characters)
+ if(loading && !learning) // spell load case
+ {
+ sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.",spell_id);
+
+ return false;
+ }
+
+ PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+
+ bool disabled_case = false;
+ bool superceded_old = false;
+
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ // update active state for known spell
+ if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
+ {
+ itr->second->active = active;
+
+ // loading && !learning == explicitly load from DB and then exist in it already and set correctly
+ if(loading && !learning)
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ else if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+
+ if(!active)
+ {
+ WorldPacket data(SMSG_REMOVED_SPELL, 4);
+ data << uint16(spell_id);
+ GetSession()->SendPacket(&data);
+ }
+ return active; // learn (show in spell book if active now)
+ }
+
+ if(itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
+ {
+ if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+ itr->second->disabled = disabled;
+
+ if(disabled)
+ return false;
+
+ disabled_case = true;
+ }
+ else switch(itr->second->state)
+ {
+ case PLAYERSPELL_UNCHANGED: // known saved spell
+ return false;
+ case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ state = PLAYERSPELL_CHANGED;
+ break; // need re-add
+ }
+ default: // known not saved yet spell (new or modified)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ if(loading && !learning)
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+
+ return false;
+ }
+ }
+ }
+
+ if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
+ {
+ // talent: unlearn all other talent ranks (high and low)
+ if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
+ {
+ if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
+ {
+ for(int i=0; i <5; ++i)
+ {
+ // skip learning spell and no rank spell case
+ uint32 rankSpellId = talentInfo->RankID[i];
+ if(!rankSpellId || rankSpellId==spell_id)
+ continue;
+
+ // skip unknown ranks
+ if(!HasSpell(rankSpellId))
+ continue;
+
+ removeSpell(rankSpellId);
+ }
+ }
+ }
+ // non talent spell: learn low ranks (recursive call)
+ else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id))
+ {
+ if(loading) // at spells loading, no output, but allow save
+ addSpell(prev_spell,active,true,loading,SPELL_WITHOUT_SLOT_ID,disabled);
+ else // at normal learning
+ learnSpell(prev_spell);
+ }
+
+ PlayerSpell *newspell = new PlayerSpell;
+ newspell->active = active;
+ newspell->state = state;
+ newspell->disabled = disabled;
+
+ // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
+ if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
+ {
+ for( PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr )
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED) continue;
+ SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr->first);
+ if(!i_spellInfo) continue;
+
+ if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr->first) )
+ {
+ if(itr->second->active)
+ {
+ if(spellmgr.IsHighRankOfSpell(spell_id,itr->first))
+ {
+ if(!loading) // not send spell (re-/over-)learn packets at loading
+ {
+ WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
+ data << uint16(itr->first);
+ data << uint16(spell_id);
+ GetSession()->SendPacket( &data );
+ }
+
+ // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
+ itr->second->active = false;
+ itr->second->state = PLAYERSPELL_CHANGED;
+ superceded_old = true; // new spell replace old in action bars and spell book.
+ }
+ else if(spellmgr.IsHighRankOfSpell(itr->first,spell_id))
+ {
+ if(!loading) // not send spell (re-/over-)learn packets at loading
+ {
+ WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
+ data << uint16(spell_id);
+ data << uint16(itr->first);
+ GetSession()->SendPacket( &data );
+ }
+
+ // mark new spell as disable (not learned yet for client and will not learned)
+ newspell->active = false;
+ if(newspell->state != PLAYERSPELL_NEW)
+ newspell->state = PLAYERSPELL_CHANGED;
+ }
+ }
+ }
+ }
+ }
+
+ uint16 tmpslot=slot_id;
+
+ if (tmpslot == SPELL_WITHOUT_SLOT_ID)
+ {
+ uint16 maxid = 0;
+ PlayerSpellMap::iterator itr;
+ for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+ if (itr->second->slotId > maxid)
+ maxid = itr->second->slotId;
+ }
+ tmpslot = maxid + 1;
+ }
+
+ newspell->slotId = tmpslot;
+ m_spells[spell_id] = newspell;
+
+ // return false if spell disabled
+ if (newspell->disabled)
+ return false;
+ }
+
+ uint32 talentCost = GetTalentSpellCost(spell_id);
+
+ // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
+ // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
+ if( talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL) )
+ {
+ // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
+ CastSpell(this, spell_id, true);
+ }
+ // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
+ else if (IsPassiveSpell(spell_id))
+ {
+ // if spell doesn't require a stance or the player is in the required stance
+ if( ( !spellInfo->Stances &&
+ spell_id != 5420 && spell_id != 5419 && spell_id != 7376 &&
+ spell_id != 7381 && spell_id != 21156 && spell_id != 21009 &&
+ spell_id != 21178 && spell_id != 33948 && spell_id != 40121 ) ||
+ m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))) ||
+ (spell_id == 5420 && m_form == FORM_TREE) ||
+ (spell_id == 5419 && m_form == FORM_TRAVEL) ||
+ (spell_id == 7376 && m_form == FORM_DEFENSIVESTANCE) ||
+ (spell_id == 7381 && m_form == FORM_BERSERKERSTANCE) ||
+ (spell_id == 21156 && m_form == FORM_BATTLESTANCE)||
+ (spell_id == 21178 && (m_form == FORM_BEAR || m_form == FORM_DIREBEAR) ) ||
+ (spell_id == 33948 && m_form == FORM_FLIGHT) ||
+ (spell_id == 40121 && m_form == FORM_FLIGHT_EPIC) )
+ //Check CasterAuraStates
+ if (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)))
+ CastSpell(this, spell_id, true);
+ }
+ else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) )
+ {
+ CastSpell(this, spell_id, true);
+ return false;
+ }
+
+ // update used talent points count
+ m_usedTalentCount += talentCost;
+
+ // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
+ if(uint32 freeProfs = GetFreePrimaryProffesionPoints())
+ {
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
+ SetFreePrimaryProffesions(freeProfs-1);
+ }
+
+ // add dependent skills
+ uint16 maxskill = GetMaxSkillValueForLevel();
+
+ SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
+
+ if(spellLearnSkill)
+ {
+ uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
+
+ if(skill_value < spellLearnSkill->value)
+ skill_value = spellLearnSkill->value;
+
+ uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
+
+ if(skill_max_value < new_skill_max_value)
+ skill_max_value = new_skill_max_value;
+
+ SetSkill(spellLearnSkill->skill,skill_value,skill_max_value);
+ }
+ else
+ {
+ // not ranked skills
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
+ if(!pSkill)
+ continue;
+
+ if(HasSkill(pSkill->id))
+ continue;
+
+ if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
+ // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
+ // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
+ {
+ switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0))
+ {
+ case SKILL_RANGE_LANGUAGE:
+ SetSkill(pSkill->id, 300, 300 );
+ break;
+ case SKILL_RANGE_LEVEL:
+ SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel() );
+ break;
+ case SKILL_RANGE_MONO:
+ SetSkill(pSkill->id, 1, 1 );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // learn dependent spells
+ SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
+ SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
+
+ for(SpellLearnSpellMap::const_iterator itr = spell_begin; itr != spell_end; ++itr)
+ {
+ if(!itr->second.autoLearned)
+ {
+ if(loading) // at spells loading, no output, but allow save
+ addSpell(itr->second.spell,true,true,loading);
+ else // at normal learning
+ learnSpell(itr->second.spell);
+ }
+ }
+
+ // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
+ return active && !disabled && !superceded_old;
+}
+
+void Player::learnSpell(uint32 spell_id)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+
+ bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
+ bool active = disabled ? itr->second->active : true;
+
+ bool learning = addSpell(spell_id,active);
+
+ // learn all disabled higher ranks (recursive)
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator i = nextMap.lower_bound(spell_id); i != nextMap.upper_bound(spell_id); ++i)
+ {
+ PlayerSpellMap::iterator iter = m_spells.find(i->second);
+ if (disabled && iter != m_spells.end() && iter->second->disabled)
+ learnSpell(i->second);
+ }
+
+ // prevent duplicated entires in spell book
+ if(!learning)
+ return;
+
+ WorldPacket data(SMSG_LEARNED_SPELL, 4);
+ data << uint32(spell_id);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::removeSpell(uint32 spell_id, bool disabled)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr == m_spells.end())
+ return;
+
+ if(itr->second->state == PLAYERSPELL_REMOVED || disabled && itr->second->disabled)
+ return;
+
+ // unlearn non talent higher ranks (recursive)
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator itr2 = nextMap.lower_bound(spell_id); itr2 != nextMap.upper_bound(spell_id); ++itr2)
+ if(HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second))
+ removeSpell(itr2->second,disabled);
+
+ // removing
+ WorldPacket data(SMSG_REMOVED_SPELL, 4);
+ data << uint16(spell_id);
+ GetSession()->SendPacket(&data);
+
+ if (disabled)
+ {
+ itr->second->disabled = disabled;
+ if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+ }
+ else
+ {
+ if(itr->second->state == PLAYERSPELL_NEW)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ }
+ else
+ itr->second->state = PLAYERSPELL_REMOVED;
+ }
+
+ RemoveAurasDueToSpell(spell_id);
+
+ // remove pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id))
+ RemovePetAura(petSpell);
+
+ // free talent points
+ uint32 talentCosts = GetTalentSpellCost(spell_id);
+ if(talentCosts > 0)
+ {
+ if(talentCosts < m_usedTalentCount)
+ m_usedTalentCount -= talentCosts;
+ else
+ m_usedTalentCount = 0;
+ }
+
+ // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
+ {
+ uint32 freeProfs = GetFreePrimaryProffesionPoints()+1;
+ if(freeProfs <= sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
+ SetFreePrimaryProffesions(freeProfs);
+ }
+
+ // remove dependent skill
+ SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
+ if(spellLearnSkill)
+ {
+ uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id);
+ if(!prev_spell) // first rank, remove skill
+ SetSkill(spellLearnSkill->skill,0,0);
+ else
+ {
+ // search prev. skill setting by spell ranks chain
+ SpellLearnSkillNode const* prevSkill = spellmgr.GetSpellLearnSkill(prev_spell);
+ while(!prevSkill && prev_spell)
+ {
+ prev_spell = spellmgr.GetPrevSpellInChain(prev_spell);
+ prevSkill = spellmgr.GetSpellLearnSkill(spellmgr.GetFirstSpellInChain(prev_spell));
+ }
+
+ if(!prevSkill) // not found prev skill setting, remove skill
+ SetSkill(spellLearnSkill->skill,0,0);
+ else // set to prev. skill setting values
+ {
+ uint32 skill_value = GetPureSkillValue(prevSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
+
+ if(skill_value > prevSkill->value)
+ skill_value = prevSkill->value;
+
+ uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
+
+ if(skill_max_value > new_skill_max_value)
+ skill_max_value = new_skill_max_value;
+
+ SetSkill(prevSkill->skill,skill_value,skill_max_value);
+ }
+ }
+
+ }
+ else
+ {
+ // not ranked skills
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
+ if(!pSkill)
+ continue;
+
+ if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
+ // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
+ // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
+ {
+ // not reset skills for professions and racial abilities
+ if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) &&
+ (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0) )
+ continue;
+
+ SetSkill(pSkill->id, 0, 0 );
+ }
+ }
+ }
+
+ // remove dependent spells
+ SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
+ SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
+
+ for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2)
+ removeSpell(itr2->second.spell, disabled);
+}
+
+void Player::RemoveArenaSpellCooldowns()
+{
+ // remove cooldowns on spells that has < 15 min CD
+ SpellCooldowns::iterator itr, next;
+ // iterate spell cooldowns
+ for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+ SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
+ // check if spellentry is present and if the cooldown is less than 15 mins
+ if( entry &&
+ entry->RecoveryTime <= 15 * MINUTE * 1000 &&
+ entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 )
+ {
+ // notify player
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(itr->first);
+ data << GetGUID();
+ GetSession()->SendPacket(&data);
+ // remove cooldown
+ m_spellCooldowns.erase(itr);
+ }
+ }
+}
+
+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);
+ }
+ m_spellCooldowns.clear();
+ }
+}
+
+void Player::_LoadSpellCooldowns(QueryResult *result)
+{
+ m_spellCooldowns.clear();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ time_t curTime = time(NULL);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+ uint32 item_id = fields[1].GetUInt32();
+ time_t db_time = (time_t)fields[2].GetUInt64();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outError("Player %u have unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id);
+ continue;
+ }
+
+ // skip outdated cooldown
+ if(db_time <= curTime)
+ continue;
+
+ AddSpellCooldown(spell_id, item_id, db_time);
+
+ sLog.outDebug("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_SaveSpellCooldowns()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
+
+ time_t curTime = time(NULL);
+
+ // remove outdated and save active
+ for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
+ {
+ if(itr->second.end <= curTime)
+ m_spellCooldowns.erase(itr++);
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
+ ++itr;
+ }
+ }
+}
+
+uint32 Player::resetTalentsCost() const
+{
+ // The first time reset costs 1 gold
+ if(m_resetTalentsCost < 1*GOLD)
+ return 1*GOLD;
+ // then 5 gold
+ else if(m_resetTalentsCost < 5*GOLD)
+ return 5*GOLD;
+ // After that it increases in increments of 5 gold
+ else if(m_resetTalentsCost < 10*GOLD)
+ return 10*GOLD;
+ else
+ {
+ uint32 months = (sWorld.GetGameTime() - m_resetTalentsTime)/MONTH;
+ if(months > 0)
+ {
+ // This cost will be reduced by a rate of 5 gold per month
+ int32 new_cost = int32(m_resetTalentsCost) - 5*GOLD*months;
+ // to a minimum of 10 gold.
+ return (new_cost < 10*GOLD ? 10*GOLD : new_cost);
+ }
+ else
+ {
+ // After that it increases in increments of 5 gold
+ int32 new_cost = m_resetTalentsCost + 5*GOLD;
+ // until it hits a cap of 50 gold.
+ if(new_cost > 50*GOLD)
+ new_cost = 50*GOLD;
+ return new_cost;
+ }
+ }
+}
+
+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());
+ }
+
+ uint32 level = getLevel();
+ uint32 talentPointsForLevel = level < 10 ? 0 : uint32((level-9)*sWorld.getRate(RATE_TALENT));
+
+ if (m_usedTalentCount == 0)
+ {
+ SetFreeTalentPoints(talentPointsForLevel);
+ return false;
+ }
+
+ uint32 cost = 0;
+
+ if(!no_cost)
+ {
+ cost = resetTalentsCost();
+
+ if (GetMoney() < cost)
+ {
+ SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
+ return false;
+ }
+ }
+
+ for (unsigned int i = 0; i < sTalentStore.GetNumRows(); i++)
+ {
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
+
+ if (!talentInfo) continue;
+
+ TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+
+ if(!talentTabInfo)
+ continue;
+
+ // unlearn only talents for character class
+ // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
+ // to prevent unexpected lost normal learned spell skip another class talents
+ if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
+ continue;
+
+ for (int j = 0; j < 5; j++)
+ {
+ for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
+ {
+ ++itr;
+ continue;
+ }
+
+ // remove learned spells (all ranks)
+ uint32 itrFirstId = 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))
+ {
+ removeSpell(itr->first,!IsPassiveSpell(itr->first));
+ itr = GetSpellMap().begin();
+ continue;
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ SetFreeTalentPoints(talentPointsForLevel);
+
+ if(!no_cost)
+ {
+ ModifyMoney(-(int32)cost);
+
+ m_resetTalentsCost = cost;
+ m_resetTalentsTime = time(NULL);
+ }
+
+ //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
+ RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
+
+ return true;
+}
+
+bool Player::_removeSpell(uint16 spell_id)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ return true;
+ }
+ return false;
+}
+
+Mail* Player::GetMail(uint32 id)
+{
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
+ {
+ if ((*itr)->messageID == id)
+ {
+ return (*itr);
+ }
+ }
+ return NULL;
+}
+
+void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
+{
+ if(target == this)
+ {
+ Object::_SetCreateBits(updateMask, target);
+ }
+ else
+ {
+ for(uint16 index = 0; index < m_valuesCount; index++)
+ {
+ if(GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
+ updateMask->SetBit(index);
+ }
+ }
+}
+
+void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
+{
+ if(target == this)
+ {
+ Object::_SetUpdateBits(updateMask, target);
+ }
+ else
+ {
+ Object::_SetUpdateBits(updateMask, target);
+ *updateMask &= updateVisualBits;
+ }
+}
+
+void Player::InitVisibleBits()
+{
+ updateVisualBits.SetCount(PLAYER_END);
+
+ updateVisualBits.SetBit(OBJECT_FIELD_GUID);
+ updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
+ updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHARM);
+ updateVisualBits.SetBit(UNIT_FIELD_CHARM+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_SUMMON);
+ updateVisualBits.SetBit(UNIT_FIELD_SUMMON+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY);
+
+ updateVisualBits.SetBit(UNIT_FIELD_TARGET);
+ updateVisualBits.SetBit(UNIT_FIELD_TARGET+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT);
+ updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_HEALTH);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER1);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER2);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER3);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER4);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER5);
+
+ updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5);
+
+ updateVisualBits.SetBit(UNIT_FIELD_LEVEL);
+ updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_0);
+ updateVisualBits.SetBit(UNIT_FIELD_FLAGS);
+ updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2);
+ for(uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i)
+ updateVisualBits.SetBit(i);
+ updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
+ updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME);
+ updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1);
+ updateVisualBits.SetBit(UNIT_FIELD_RANGEDATTACKTIME);
+ updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS);
+ updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH);
+ updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_1);
+ updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER);
+ updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP);
+ updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS);
+ updateVisualBits.SetBit(UNIT_CHANNEL_SPELL);
+ updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
+
+ updateVisualBits.SetBit(PLAYER_FLAGS);
+ updateVisualBits.SetBit(PLAYER_BYTES);
+ updateVisualBits.SetBit(PLAYER_BYTES_2);
+ updateVisualBits.SetBit(PLAYER_BYTES_3);
+ updateVisualBits.SetBit(PLAYER_GUILDID);
+ updateVisualBits.SetBit(PLAYER_GUILDRANK);
+ updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
+ updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
+ updateVisualBits.SetBit(PLAYER_DUEL_ARBITER);
+ updateVisualBits.SetBit(PLAYER_DUEL_ARBITER+1);
+
+ // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
+ for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i+=4)
+ updateVisualBits.SetBit(i);
+
+ for(uint16 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ updateVisualBits.SetBit((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + i*2));
+ updateVisualBits.SetBit((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (i*2) + 1));
+ }
+ //Players visible items are not inventory stuff
+ //431) = 884 (0x374) = main weapon
+ for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ // item creator
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 0);
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 1);
+
+ uint16 visual_base = PLAYER_VISIBLE_ITEM_1_0 + (i*MAX_VISIBLE_ITEM_OFFSET);
+
+ // item entry
+ updateVisualBits.SetBit(visual_base + 0);
+
+ // item enchantment IDs
+ for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
+ updateVisualBits.SetBit(visual_base + 1 + j);
+
+ // random properties
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i*MAX_VISIBLE_ITEM_OFFSET));
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i*MAX_VISIBLE_ITEM_OFFSET));
+ }
+
+ updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
+
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 1);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 2);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 1);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 2);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 3);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 4);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 5);
+}
+
+void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
+{
+ for(int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+
+ if(target == this)
+ {
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+ }
+
+ Unit::BuildCreateUpdateBlockForPlayer( data, target );
+}
+
+void Player::DestroyForPlayer( Player *target ) const
+{
+ Unit::DestroyForPlayer( target );
+
+ for(int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+
+ if(target == this)
+ {
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+ }
+}
+
+bool Player::HasSpell(uint32 spell) const
+{
+ PlayerSpellMap::const_iterator itr = m_spells.find((uint16)spell);
+ return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled);
+}
+
+TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
+{
+ if (!trainer_spell)
+ return TRAINER_SPELL_RED;
+
+ if (!trainer_spell->spell)
+ return TRAINER_SPELL_RED;
+
+ // known spell
+ if(HasSpell(trainer_spell->spell->Id))
+ return TRAINER_SPELL_GRAY;
+
+ // check race/class requirement
+ if(!IsSpellFitByClassAndRace(trainer_spell->spell->Id))
+ return TRAINER_SPELL_RED;
+
+ // check level requirement
+ if(getLevel() < ( trainer_spell->reqlevel ? trainer_spell->reqlevel : trainer_spell->spell->spellLevel))
+ return TRAINER_SPELL_RED;
+
+ if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->spell->Id))
+ {
+ // check prev.rank requirement
+ if(spell_chain->prev && !HasSpell(spell_chain->prev))
+ return TRAINER_SPELL_RED;
+
+ // check additional spell requirement
+ if(spell_chain->req && !HasSpell(spell_chain->req))
+ return TRAINER_SPELL_RED;
+ }
+
+ // check skill requirement
+ if(trainer_spell->reqskill && GetBaseSkillValue(trainer_spell->reqskill) < trainer_spell->reqskillvalue)
+ return TRAINER_SPELL_RED;
+
+ // secondary prof. or not prof. spell
+ uint32 skill = trainer_spell->spell->EffectMiscValue[1];
+
+ if(trainer_spell->spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
+ return TRAINER_SPELL_GREEN;
+
+ // check primary prof. limit
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(trainer_spell->spell->Id) && GetFreePrimaryProffesionPoints() == 0)
+ return TRAINER_SPELL_RED;
+
+ return TRAINER_SPELL_GREEN;
+}
+
+void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars)
+{
+ uint32 guid = GUID_LOPART(playerguid);
+
+ // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
+ // bones will be deleted by corpse/bones deleting thread shortly
+ ObjectAccessor::Instance().ConvertCorpseForPlayer(playerguid);
+
+ // remove from guild
+ uint32 guildId = GetGuildIdFromDB(playerguid);
+ if(guildId != 0)
+ {
+ Guild* guild = objmgr.GetGuildById(guildId);
+ if(guild)
+ guild->DelMember(guid);
+ }
+
+ // the player was uninvited already on logout so just remove from group
+ QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid);
+ if(resultGroup)
+ {
+ uint64 leaderGuid = MAKE_NEW_GUID((*resultGroup)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete resultGroup;
+ Group* group = objmgr.GetGroupByLeader(leaderGuid);
+ if(group)
+ {
+ RemoveFromGroup(group, playerguid);
+ }
+ }
+
+ // remove signs from petitions (also remove petitions if owner);
+ RemovePetitionsAndSigns(playerguid, 10);
+
+ // return back all mails with COD and Item 0 1 2 3 4 5 6
+ QueryResult *resultMail = CharacterDatabase.PQuery("SELECT id,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
+ if(resultMail)
+ {
+ do
+ {
+ Field *fields = resultMail->Fetch();
+
+ uint32 mail_id = fields[0].GetUInt32();
+ uint16 mailTemplateId= fields[1].GetUInt16();
+ uint32 sender = fields[2].GetUInt32();
+ std::string subject = fields[3].GetCppString();
+ uint32 itemTextId = fields[4].GetUInt32();
+ uint32 money = fields[5].GetUInt32();
+ bool has_items = fields[6].GetBool();
+
+ //we can return mail now
+ //so firstly delete the old one
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
+
+ MailItemsInfo mi;
+ if(has_items)
+ {
+ QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", mail_id);
+ if(resultItems)
+ {
+ do
+ {
+ Field *fields2 = resultItems->Fetch();
+
+ uint32 item_guidlow = fields2[0].GetUInt32();
+ uint32 item_template = fields2[1].GetUInt32();
+
+ ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template);
+ if(!itemProto)
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
+ continue;
+ }
+
+ Item *pItem = NewItemOrBag(itemProto);
+ if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)))
+ {
+ pItem->FSetState(ITEM_REMOVED);
+ pItem->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ mi.AddItem(item_guidlow, item_template, pItem);
+ }
+ while (resultItems->NextRow());
+
+ delete resultItems;
+ }
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
+
+ uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ WorldSession::SendReturnToSender(MAIL_NORMAL, pl_account, guid, sender, subject, itemTextId, &mi, money, 0, mailTemplateId);
+ }
+ while (resultMail->NextRow());
+
+ delete resultMail;
+ }
+
+ // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
+ // Get guids of character's pets, will deleted in transaction
+ QueryResult *resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
+
+ // NOW we can finally clear other DB data related to character
+ CharacterDatabase.BeginTransaction();
+ if (resultPets)
+ {
+ do
+ {
+ Field *fields3 = resultPets->Fetch();
+ uint32 petguidlow = fields3[0].GetUInt32();
+ Pet::DeleteFromDB(petguidlow);
+ } while (resultPets->NextRow());
+ delete resultPets;
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
+ CharacterDatabase.CommitTransaction();
+
+ //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID);
+ if(updateRealmChars) sWorld.UpdateRealmCharCount(accountId);
+}
+
+void Player::SetMovement(PlayerMovementType pType)
+{
+ WorldPacket data;
+ switch(pType)
+ {
+ case MOVE_ROOT: data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size()+4); break;
+ case MOVE_UNROOT: data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size()+4); break;
+ case MOVE_WATER_WALK: data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size()+4); break;
+ case MOVE_LAND_WALK: data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size()+4); break;
+ default:
+ sLog.outError("Player::SetMovement: Unsupported move type (%d), data not sent to client.",pType);
+ return;
+ }
+ data.append(GetPackGUID());
+ data << uint32(0);
+ GetSession()->SendPacket( &data );
+}
+
+/* Preconditions:
+ - a resurrectable corpse must not be loaded for the player (only bones)
+ - the player must be in world
+*/
+void Player::BuildPlayerRepop()
+{
+ if(getRace() == RACE_NIGHTELF)
+ CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
+ CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
+
+ // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
+ // there must be SMSG.STOP_MIRROR_TIMER
+ // there we must send 888 opcode
+
+ // the player cannot have a corpse already, only bones which are not returned by GetCorpse
+ if(GetCorpse())
+ {
+ sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
+ assert(false);
+ }
+
+ // create a corpse and place it at the player's location
+ CreateCorpse();
+ Corpse *corpse = GetCorpse();
+ if(!corpse)
+ {
+ sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
+ return;
+ }
+ GetMap()->Add(corpse);
+
+ // convert player body to ghost
+ SetHealth( 1 );
+
+ SetMovement(MOVE_WATER_WALK);
+ if(!GetSession()->isLogingOut())
+ SetMovement(MOVE_UNROOT);
+
+ // BG - remove insignia related
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ SendCorpseReclaimDelay();
+
+ // to prevent cheating
+ corpse->ResetGhostTime();
+
+ StopMirrorTimers(); //disable timers(bars)
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player?
+
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, PLAYER_STATE_FLAG_ALWAYS_STAND);
+}
+
+void Player::SendDelayResponse(const uint32 ml_seconds)
+{
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::ResurrectPlayer(float restore_percent, bool updateToWorld, bool applySickness)
+{
+ WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
+ data << uint32(-1);
+ data << float(0);
+ data << float(0);
+ data << float(0);
+ GetSession()->SendPacket(&data);
+
+ // speed change, land walk
+
+ // remove death flag + set aura
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
+ if(getRace() == RACE_NIGHTELF)
+ RemoveAurasDueToSpell(20584); // speed bonuses
+ RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
+
+ setDeathState(ALIVE);
+
+ SetMovement(MOVE_LAND_WALK);
+ SetMovement(MOVE_UNROOT);
+
+ m_deathTimer = 0;
+
+ // set health/powers (0- will be set in caller)
+ if(restore_percent>0.0f)
+ {
+ SetHealth(uint32(GetMaxHealth()*restore_percent));
+ SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
+ SetPower(POWER_RAGE, 0);
+ SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
+ }
+
+ // update visbility
+ ObjectAccessor::UpdateVisibilityForPlayer(this);
+
+ // some items limited to specific map
+ DestroyZoneLimitedItem( true, GetZoneId());
+
+ if(!applySickness || getLevel() <= 10)
+ return;
+
+ //Characters from level 1-10 are not affected by resurrection sickness.
+ //Characters from level 11-19 will suffer from one minute of sickness
+ //for each level they are above 10.
+ //Characters level 20 and up suffer from ten minutes of sickness.
+ int32 startLevel = sWorld.getConfig(CONFIG_DEATH_SICKNESS_LEVEL);
+
+ if(int32(getLevel()) >= startLevel)
+ {
+ // set resurrection sickness
+ CastSpell(this,SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,true);
+
+ // not full duration
+ if(int32(getLevel()) < startLevel+9)
+ {
+ int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE;
+
+ for(int i =0; i < 3; ++i)
+ {
+ if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i))
+ {
+ Aur->SetAuraDuration(delta*1000);
+ Aur->UpdateAuraDuration();
+ }
+ }
+ }
+ }
+}
+
+void Player::KillPlayer()
+{
+ SetMovement(MOVE_ROOT);
+
+ StopMirrorTimers(); //disable timers(bars)
+
+ setDeathState(CORPSE);
+ //SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP );
+
+ SetFlag(UNIT_DYNAMIC_FLAGS, 0x00);
+ ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable());
+
+ // 6 minutes until repop at graveyard
+ m_deathTimer = 6*MINUTE*1000;
+
+ UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
+
+ // don't create corpse at this moment, player might be falling
+
+ // update visibility
+ ObjectAccessor::UpdateObjectVisibility(this);
+}
+
+void Player::CreateCorpse()
+{
+ // prevent existence 2 corpse for player
+ SpawnCorpseBones();
+
+ uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
+
+ Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
+ SetPvPDeath(false);
+
+ if(!corpse->Create(objmgr.GenerateLowGuid(HIGHGUID_CORPSE), this, GetMapId(), GetPositionX(),
+ GetPositionY(), GetPositionZ(), GetOrientation()))
+ {
+ delete corpse;
+ return;
+ }
+
+ _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
+ _pb = GetUInt32Value(PLAYER_BYTES);
+ _pb2 = GetUInt32Value(PLAYER_BYTES_2);
+
+ uint8 race = (uint8)(_uf);
+ uint8 skin = (uint8)(_pb);
+ uint8 face = (uint8)(_pb >> 8);
+ uint8 hairstyle = (uint8)(_pb >> 16);
+ uint8 haircolor = (uint8)(_pb >> 24);
+ uint8 facialhair = (uint8)(_pb2);
+
+ _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
+ _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
+
+ corpse->SetUInt32Value( CORPSE_FIELD_BYTES_1, _cfb1 );
+ corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
+
+ uint32 flags = CORPSE_FLAG_UNK2;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
+ flags |= CORPSE_FLAG_HIDE_HELM;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
+ flags |= CORPSE_FLAG_HIDE_CLOAK;
+ if(InBattleGround())
+ flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
+ corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
+
+ corpse->SetUInt32Value( CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId() );
+
+ corpse->SetUInt32Value( CORPSE_FIELD_GUILD, GetGuildId() );
+
+ uint32 iDisplayID;
+ uint16 iIventoryType;
+ uint32 _cfi;
+ for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ if(m_items[i])
+ {
+ iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
+ iIventoryType = (uint16)m_items[i]->GetProto()->InventoryType;
+
+ _cfi = (uint16(iDisplayID)) | (iIventoryType)<< 24;
+ corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i,_cfi);
+ }
+ }
+
+ // we don't SaveToDB for players in battlegrounds so don't do it for corpses either
+ const MapEntry *entry = sMapStore.LookupEntry(corpse->GetMapId());
+ assert(entry);
+ if(entry->map_type != MAP_BATTLEGROUND)
+ corpse->SaveToDB();
+
+ // register for player, but not show
+ ObjectAccessor::Instance().AddCorpse(corpse);
+}
+
+void Player::SpawnCorpseBones()
+{
+ if(ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID()))
+ SaveToDB(); // prevent loading as ghost without corpse
+}
+
+Corpse* Player::GetCorpse() const
+{
+ return ObjectAccessor::Instance().GetCorpseForPlayerGUID(GetGUID());
+}
+
+void Player::DurabilityLossAll(double percent, bool inventory)
+{
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityLoss(pItem,percent);
+
+ if(inventory)
+ {
+ // bags not have durability
+ // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityLoss(pItem,percent);
+
+ // keys not have durability
+ //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ if(Item* pItem = GetItemByPos( i, j ))
+ DurabilityLoss(pItem,percent);
+ }
+}
+
+void Player::DurabilityLoss(Item* item, double percent)
+{
+ if(!item )
+ return;
+
+ uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+
+ if(!pMaxDurability)
+ return;
+
+ uint32 pDurabilityLoss = uint32(pMaxDurability*percent);
+
+ if(pDurabilityLoss < 1 )
+ pDurabilityLoss = 1;
+
+ DurabilityPointsLoss(item,pDurabilityLoss);
+}
+
+void Player::DurabilityPointsLossAll(int32 points, bool inventory)
+{
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityPointsLoss(pItem,points);
+
+ if(inventory)
+ {
+ // bags not have durability
+ // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityPointsLoss(pItem,points);
+
+ // keys not have durability
+ //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ if(Item* pItem = GetItemByPos( i, j ))
+ DurabilityPointsLoss(pItem,points);
+ }
+}
+
+void Player::DurabilityPointsLoss(Item* item, int32 points)
+{
+ int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+ int32 pNewDurability = pOldDurability - points;
+
+ if (pNewDurability < 0)
+ pNewDurability = 0;
+ else if (pNewDurability > pMaxDurability)
+ pNewDurability = pMaxDurability;
+
+ if (pOldDurability != pNewDurability)
+ {
+ // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
+ if ( pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
+ _ApplyItemMods(item,item->GetSlot(), false);
+
+ item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
+
+ // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
+ if ( pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
+ _ApplyItemMods(item,item->GetSlot(), true);
+
+ item->SetState(ITEM_CHANGED, this);
+ }
+}
+
+void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot)
+{
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot ))
+ DurabilityPointsLoss(pItem,1);
+}
+
+uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
+{
+ uint32 TotalCost = 0;
+ // equipped, backpack, bags itself
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ TotalCost += DurabilityRepair(( (INVENTORY_SLOT_BAG_0 << 8) | i ),cost,discountMod, guildBank);
+
+ // bank, buyback and keys not repaired
+
+ // items in inventory bags
+ for(int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
+ for(int i = 0; i < MAX_BAG_SIZE; i++)
+ TotalCost += DurabilityRepair(( (j << 8) | i ),cost,discountMod, guildBank);
+ return TotalCost;
+}
+
+uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
+{
+ Item* item = GetItemByPos(pos);
+
+ uint32 TotalCost = 0;
+ if(!item)
+ return TotalCost;
+
+ uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ if(!maxDurability)
+ return TotalCost;
+
+ uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+
+ if(cost)
+ {
+ uint32 LostDurability = maxDurability - curDurability;
+ if(LostDurability>0)
+ {
+ ItemPrototype const *ditemProto = sItemStorage.LookupEntry<ItemPrototype>(item->GetEntry());
+ if(!ditemProto)
+ {
+ sLog.outError("ERROR: RepairDurability: Unknown item id %u", ditemProto);
+ return TotalCost;
+ }
+
+ DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
+ if(!dcost)
+ {
+ sLog.outError("ERROR: RepairDurability: Wrong item lvl %u", dcost);
+ return TotalCost;
+ }
+
+ DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry((ditemProto->Quality+1)*2);
+ if(!dQualitymodEntry)
+ {
+ sLog.outError("ERROR: RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntry);
+ return TotalCost;
+ }
+
+ uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)];
+ uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
+
+ costs = uint32(costs * discountMod);
+
+ if (costs==0) //fix for ITEM_QUALITY_ARTIFACT
+ costs = 1;
+
+ if (guildBank)
+ {
+ if (GetGuildId()==0)
+ {
+ DEBUG_LOG("You are not member of a guild");
+ return TotalCost;
+ }
+
+ Guild *pGuild = objmgr.GetGuildById(GetGuildId());
+ if (!pGuild)
+ return TotalCost;
+
+ if (!pGuild->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR))
+ {
+ DEBUG_LOG("You do not have rights to withdraw for repairs");
+ return TotalCost;
+ }
+
+ if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs)
+ {
+ DEBUG_LOG("You do not have enough money withdraw amount remaining");
+ return TotalCost;
+ }
+
+ if (pGuild->GetGuildBankMoney() < costs)
+ {
+ DEBUG_LOG("There is not enough money in bank");
+ return TotalCost;
+ }
+
+ pGuild->MemberMoneyWithdraw(costs, GetGUIDLow());
+ TotalCost = costs;
+ }
+ else if (GetMoney() < costs)
+ {
+ DEBUG_LOG("You do not have enough money");
+ return TotalCost;
+ }
+ else
+ ModifyMoney( -int32(costs) );
+ }
+ }
+
+ item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
+ item->SetState(ITEM_CHANGED, this);
+
+ // reapply mods for total broken and repaired item if equipped
+ if(IsEquipmentPos(pos) && !curDurability)
+ _ApplyItemMods(item,pos & 255, true);
+ return TotalCost;
+}
+
+void Player::RepopAtGraveyard()
+{
+ // note: this can be called also when the player is alive
+ // for example from WorldSession::HandleMovementOpcodes
+
+ AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId());
+
+ // Such zones are considered unreachable as a ghost and the player must be automatically revived
+ if(!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY || GetTransport())
+ {
+ ResurrectPlayer(0.5f);
+ SpawnCorpseBones();
+ }
+
+ WorldSafeLocsEntry const *ClosestGrave = NULL;
+
+ // Special handle for battleground maps
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
+
+ if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY))
+ ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam());
+ else
+ ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
+
+ // stop countdown until repop
+ m_deathTimer = 0;
+
+ // if no grave found, stay at the current location
+ // and don't show spirit healer location
+ if(ClosestGrave)
+ {
+ TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
+ if(isDead()) // not send if alive, because it used in TeleportTo()
+ {
+ WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
+ data << ClosestGrave->map_id;
+ data << ClosestGrave->x;
+ data << ClosestGrave->y;
+ data << ClosestGrave->z;
+ GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Player::JoinedChannel(Channel *c)
+{
+ m_channels.push_back(c);
+}
+
+void Player::LeftChannel(Channel *c)
+{
+ m_channels.remove(c);
+}
+
+void Player::CleanupChannels()
+{
+ while(!m_channels.empty())
+ {
+ Channel* ch = *m_channels.begin();
+ m_channels.erase(m_channels.begin()); // remove from player's channel list
+ ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
+ if (ChannelMgr* cMgr = channelMgr(GetTeam()))
+ cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
+
+ }
+ sLog.outDebug("Player: channels cleaned up!");
+}
+
+void Player::UpdateLocalChannels(uint32 newZone )
+{
+ if(m_channels.empty())
+ return;
+
+ AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
+ if(!current_zone)
+ return;
+
+ ChannelMgr* cMgr = channelMgr(GetTeam());
+ if(!cMgr)
+ return;
+
+ std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
+
+ for(JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next)
+ {
+ next = i; ++next;
+
+ // skip non built-in channels
+ if(!(*i)->IsConstant())
+ continue;
+
+ ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId());
+ if(!ch)
+ continue;
+
+ if((ch->flags & 4) == 4) // global channel without zone name in pattern
+ continue;
+
+ // new channel
+ char new_channel_name_buf[100];
+ snprintf(new_channel_name_buf,100,ch->pattern[m_session->GetSessionDbcLocale()],current_zone_name.c_str());
+ Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf,ch->ChannelID);
+
+ if((*i)!=new_channel)
+ {
+ new_channel->Join(GetGUID(),""); // will output Changed Channel: N. Name
+
+ // leave old channel
+ (*i)->Leave(GetGUID(),false); // not send leave channel, it already replaced at client
+ std::string name = (*i)->GetName(); // stroe name, (*i)erase in LeftChannel
+ LeftChannel(*i); // remove from player's channel list
+ cMgr->LeftChannel(name); // delete if empty
+ }
+ }
+ sLog.outDebug("Player: channels cleaned up!");
+}
+
+void Player::LeaveLFGChannel()
+{
+ for(JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i )
+ {
+ if((*i)->IsLFG())
+ {
+ (*i)->Leave(GetGUID());
+ break;
+ }
+ }
+}
+
+void Player::UpdateDefense()
+{
+ uint32 defense_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_DEFENSE);
+
+ if(UpdateSkill(SKILL_DEFENSE,defense_skill_gain))
+ {
+ // update dependent from defense skill part
+ UpdateDefenseBonusesMod();
+ }
+}
+
+void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats)
+{
+ if(modGroup >= BASEMOD_END || modType >= MOD_END)
+ {
+ sLog.outError("ERROR in HandleBaseModValue(): nonexisted BaseModGroup of wrong BaseModType!");
+ return;
+ }
+
+ float val = 1.0f;
+
+ switch(modType)
+ {
+ case FLAT_MOD:
+ m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
+ break;
+ case PCT_MOD:
+ if(amount <= -100.0f)
+ amount = -200.0f;
+
+ val = (100.0f + amount) / 100.0f;
+ m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f/val);
+ break;
+ }
+
+ if(!CanModifyStats())
+ return;
+
+ switch(modGroup)
+ {
+ case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break;
+ case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break;
+ case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break;
+ case SHIELD_BLOCK_VALUE: UpdateShieldBlockValue(); break;
+ default: break;
+ }
+}
+
+float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
+{
+ if(modGroup >= BASEMOD_END || modType > MOD_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted BaseModGroup or wrong BaseModType!");
+ return 0.0f;
+ }
+
+ if(modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
+ return 0.0f;
+
+ return m_auraBaseMod[modGroup][modType];
+}
+
+float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
+{
+ if(modGroup >= BASEMOD_END)
+ {
+ sLog.outError("ERROR: wrong BaseModGroup in GetTotalBaseModValue()!");
+ return 0.0f;
+ }
+
+ if(m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
+ return 0.0f;
+
+ return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
+}
+
+uint32 Player::GetShieldBlockValue() const
+{
+ BaseModGroup modGroup = SHIELD_BLOCK_VALUE;
+
+ float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH)/20 - 1;
+
+ value = (value < 0) ? 0 : value;
+
+ return uint32(value);
+}
+
+float Player::GetMeleeCritFromAgility()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtChanceToMeleeCritBaseEntry const *critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass-1);
+ GtChanceToMeleeCritEntry const *critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (critBase==NULL || critRatio==NULL)
+ return 0.0f;
+
+ float crit=critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio;
+ return crit*100.0f;
+}
+
+float Player::GetDodgeFromAgility()
+{
+ // Table for base dodge values
+ float dodge_base[MAX_CLASSES] = {
+ 0.0075f, // Warrior
+ 0.00652f, // Paladin
+ -0.0545f, // Hunter
+ -0.0059f, // Rogue
+ 0.03183f, // Priest
+ 0.0114f, // DK
+ 0.0167f, // Shaman
+ 0.034575f, // Mage
+ 0.02011f, // Warlock
+ 0.0f, // ??
+ -0.0187f // Druid
+ };
+ // Crit/agility to dodge/agility coefficient multipliers
+ float crit_to_dodge[MAX_CLASSES] = {
+ 1.1f, // Warrior
+ 1.0f, // Paladin
+ 1.6f, // Hunter
+ 2.0f, // Rogue
+ 1.0f, // Priest
+ 1.0f, // DK?
+ 1.0f, // Shaman
+ 1.0f, // Mage
+ 1.0f, // Warlock
+ 0.0f, // ??
+ 1.7f // Druid
+ };
+
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ // Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
+ GtChanceToMeleeCritEntry const *dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (dodgeRatio==NULL || pclass > MAX_CLASSES)
+ return 0.0f;
+
+ float dodge=dodge_base[pclass-1] + GetStat(STAT_AGILITY) * dodgeRatio->ratio * crit_to_dodge[pclass-1];
+ return dodge*100.0f;
+}
+
+float Player::GetSpellCritFromIntellect()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtChanceToSpellCritBaseEntry const *critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass-1);
+ GtChanceToSpellCritEntry const *critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (critBase==NULL || critRatio==NULL)
+ return 0.0f;
+
+ float crit=critBase->base + GetStat(STAT_INTELLECT)*critRatio->ratio;
+ return crit*100.0f;
+}
+
+float Player::GetRatingCoefficient(CombatRating cr) const
+{
+ uint32 level = getLevel();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtCombatRatingsEntry const *Rating = sGtCombatRatingsStore.LookupEntry(cr*GT_MAX_LEVEL+level-1);
+ if (Rating == NULL)
+ return 1.0f; // By default use minimum coefficient (not must be called)
+
+ return Rating->ratio;
+}
+
+float Player::GetRatingBonusValue(CombatRating cr) const
+{
+ return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr);
+}
+
+uint32 Player::GetMeleeCritDamageReduction(uint32 damage) const
+{
+ float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.0f;
+ if (melee>25.0f) melee = 25.0f;
+ return uint32 (melee * damage /100.0f);
+}
+
+uint32 Player::GetRangedCritDamageReduction(uint32 damage) const
+{
+ float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.0f;
+ if (ranged>25.0f) ranged=25.0f;
+ return uint32 (ranged * damage /100.0f);
+}
+
+uint32 Player::GetSpellCritDamageReduction(uint32 damage) const
+{
+ float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.0f;
+ // In wow script resilience limited to 25%
+ if (spell>25.0f)
+ spell = 25.0f;
+ return uint32 (spell * damage / 100.0f);
+}
+
+uint32 Player::GetDotDamageReduction(uint32 damage) const
+{
+ float spellDot = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
+ // Dot resilience not limited (limit it by 100%)
+ if (spellDot > 100.0f)
+ spellDot = 100.0f;
+ return uint32 (spellDot * damage / 100.0f);
+}
+
+float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
+{
+ switch (attType)
+ {
+ case BASE_ATTACK:
+ return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
+ case OFF_ATTACK:
+ return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
+ default:
+ break;
+ }
+ return 0.0f;
+}
+
+float Player::OCTRegenHPPerSpirit()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtOCTRegenHPEntry const *baseRatio = sGtOCTRegenHPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ GtRegenHPPerSptEntry const *moreRatio = sGtRegenHPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (baseRatio==NULL || moreRatio==NULL)
+ return 0.0f;
+
+ // Formula from PaperDollFrame script
+ float spirit = GetStat(STAT_SPIRIT);
+ float baseSpirit = spirit;
+ if (baseSpirit>50) baseSpirit = 50;
+ float moreSpirit = spirit - baseSpirit;
+ float regen = baseSpirit * baseRatio->ratio + moreSpirit * moreRatio->ratio;
+ return regen;
+}
+
+float Player::OCTRegenMPPerSpirit()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+// GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ GtRegenMPPerSptEntry const *moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (moreRatio==NULL)
+ return 0.0f;
+
+ // Formula get from PaperDollFrame script
+ float spirit = GetStat(STAT_SPIRIT);
+ float regen = spirit * moreRatio->ratio;
+ return regen;
+}
+
+void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
+{
+ ApplyModUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, value, apply);
+
+ float RatingCoeffecient = GetRatingCoefficient(cr);
+ float RatingChange = 0.0f;
+
+ bool affectStats = CanModifyStats();
+
+ switch (cr)
+ {
+ case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
+ case CR_DEFENSE_SKILL:
+ UpdateDefenseBonusesMod();
+ break;
+ case CR_DODGE:
+ UpdateDodgePercentage();
+ break;
+ case CR_PARRY:
+ UpdateParryPercentage();
+ break;
+ case CR_BLOCK:
+ UpdateBlockPercentage();
+ break;
+ case CR_HIT_MELEE:
+ RatingChange = value / RatingCoeffecient;
+ m_modMeleeHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_HIT_RANGED:
+ RatingChange = value / RatingCoeffecient;
+ m_modRangedHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_HIT_SPELL:
+ RatingChange = value / RatingCoeffecient;
+ m_modSpellHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_CRIT_MELEE:
+ if(affectStats)
+ {
+ UpdateCritPercentage(BASE_ATTACK);
+ UpdateCritPercentage(OFF_ATTACK);
+ }
+ break;
+ case CR_CRIT_RANGED:
+ if(affectStats)
+ UpdateCritPercentage(RANGED_ATTACK);
+ break;
+ case CR_CRIT_SPELL:
+ if(affectStats)
+ UpdateAllSpellCritChances();
+ break;
+ case CR_HIT_TAKEN_MELEE: // Implemented in Unit::MeleeMissChanceCalc
+ case CR_HIT_TAKEN_RANGED:
+ break;
+ case CR_HIT_TAKEN_SPELL: // Implemented in Unit::MagicSpellHitResult
+ break;
+ case CR_CRIT_TAKEN_MELEE: // Implemented in Unit::RollMeleeOutcomeAgainst (only for chance to crit)
+ case CR_CRIT_TAKEN_RANGED:
+ break;
+ case CR_CRIT_TAKEN_SPELL: // Implemented in Unit::SpellCriticalBonus (only for chance to crit)
+ break;
+ case CR_HASTE_MELEE:
+ RatingChange = value / RatingCoeffecient;
+ ApplyAttackTimePercentMod(BASE_ATTACK,RatingChange,apply);
+ ApplyAttackTimePercentMod(OFF_ATTACK,RatingChange,apply);
+ break;
+ case CR_HASTE_RANGED:
+ RatingChange = value / RatingCoeffecient;
+ ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply);
+ break;
+ case CR_HASTE_SPELL:
+ RatingChange = value / RatingCoeffecient;
+ ApplyCastTimePercentMod(RatingChange,apply);
+ break;
+ case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
+ case CR_WEAPON_SKILL_OFFHAND:
+ case CR_WEAPON_SKILL_RANGED:
+ break;
+ case CR_EXPERTISE:
+ if(affectStats)
+ {
+ UpdateExpertise(BASE_ATTACK);
+ UpdateExpertise(OFF_ATTACK);
+ }
+ break;
+ }
+}
+
+void Player::SetRegularAttackTime()
+{
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ {
+ Item *tmpitem = GetWeaponForAttack(WeaponAttackType(i));
+ if(tmpitem && !tmpitem->IsBroken())
+ {
+ ItemPrototype const *proto = tmpitem->GetProto();
+ if(proto->Delay)
+ SetAttackTime(WeaponAttackType(i), proto->Delay);
+ else
+ SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME);
+ }
+ }
+}
+
+//skill+step, checking for max value
+bool Player::UpdateSkill(uint32 skill_id, uint32 step)
+{
+ if(!skill_id)
+ return false;
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill_id)
+ break;
+
+ if(i>=PLAYER_MAX_SKILLS)
+ return false;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint32 value = SKILL_VALUE(data);
+ uint32 max = SKILL_MAX(data);
+
+ if ((!max) || (!value) || (value >= max))
+ return false;
+
+ if (value*512 < max*urand(0,512))
+ {
+ uint32 new_value = value+step;
+ if(new_value > max)
+ new_value = max;
+
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max));
+ return true;
+ }
+
+ return false;
+}
+
+inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
+{
+ if ( SkillValue >= GrayLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREY)*10;
+ if ( SkillValue >= GreenLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREEN)*10;
+ if ( SkillValue >= YellowLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_YELLOW)*10;
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_ORANGE)*10;
+}
+
+bool Player::UpdateCraftSkill(uint32 spellid)
+{
+ sLog.outDebug("UpdateCraftSkill spellid %d", spellid);
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(_spell_idx->second->skillId)
+ {
+ uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
+
+ // Alchemy Discoveries here
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid);
+ if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY)
+ {
+ if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
+ learnSpell(discoveredSpell);
+ }
+
+ uint32 craft_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_CRAFTING);
+
+ return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
+ _spell_idx->second->max_value,
+ (_spell_idx->second->max_value + _spell_idx->second->min_value)/2,
+ _spell_idx->second->min_value),
+ craft_skill_gain);
+ }
+ }
+ return false;
+}
+
+bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator )
+{
+ sLog.outDebug("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel);
+
+ uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
+
+ // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
+ switch (SkillId)
+ {
+ case SKILL_HERBALISM:
+ case SKILL_LOCKPICKING:
+ case SKILL_JEWELCRAFTING:
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ case SKILL_SKINNING:
+ if( sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)==0)
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ else
+ return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain);
+ case SKILL_MINING:
+ if (sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)==0)
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ else
+ return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)),gathering_skill_gain);
+ }
+ return false;
+}
+
+bool Player::UpdateFishingSkill()
+{
+ sLog.outDebug("UpdateFishingSkill");
+
+ uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);
+
+ int32 chance = SkillValue < 75 ? 100 : 2500/(SkillValue-50);
+
+ uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
+
+ return UpdateSkillPro(SKILL_FISHING,chance*10,gathering_skill_gain);
+}
+
+bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
+{
+ sLog.outDebug("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance/10.0);
+ if ( !SkillId )
+ return false;
+
+ if(Chance <= 0) // speedup in 0 chance case
+ {
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
+ return false;
+ }
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ( SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_INDEX(i))) == SkillId ) break;
+ if ( i >= PLAYER_MAX_SKILLS )
+ return false;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint16 SkillValue = SKILL_VALUE(data);
+ uint16 MaxValue = SKILL_MAX(data);
+
+ if ( !MaxValue || !SkillValue || SkillValue >= MaxValue )
+ return false;
+
+ int32 Roll = irand(1,1000);
+
+ if ( Roll <= Chance )
+ {
+ uint32 new_value = SkillValue+step;
+ if(new_value > MaxValue)
+ new_value = MaxValue;
+
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue));
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0);
+ return true;
+ }
+
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
+ return false;
+}
+
+void Player::UpdateWeaponSkill (WeaponAttackType attType)
+{
+ // no skill gain in pvp
+ Unit *pVictim = getVictim();
+ if(pVictim && pVictim->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ if(IsInFeralForm())
+ return; // always maximized SKILL_FERAL_COMBAT in fact
+
+ if(m_form == FORM_TREE)
+ return; // use weapon but not skill up
+
+ uint32 weapon_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_WEAPON);
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ {
+ Item *tmpitem = GetWeaponForAttack(attType,true);
+
+ if (!tmpitem)
+ UpdateSkill(SKILL_UNARMED,weapon_skill_gain);
+ else if(tmpitem->GetProto()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
+ UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
+ break;
+ }
+ case OFF_ATTACK:
+ case RANGED_ATTACK:
+ {
+ Item *tmpitem = GetWeaponForAttack(attType,true);
+ if (tmpitem)
+ UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
+ break;
+ }
+ }
+ UpdateAllCritPercentages();
+}
+
+void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence)
+{
+ switch(outcome)
+ {
+ case MELEE_HIT_CRIT:
+ case MELEE_HIT_DODGE:
+ case MELEE_HIT_PARRY:
+ case MELEE_HIT_BLOCK:
+ case MELEE_HIT_BLOCK_CRIT:
+ return;
+
+ default:
+ break;
+ }
+
+ uint32 plevel = getLevel(); // if defense than pVictim == attacker
+ uint32 greylevel = MaNGOS::XP::GetGrayLevel(plevel);
+ uint32 moblevel = pVictim->getLevelForTarget(this);
+ if(moblevel < greylevel)
+ return;
+
+ if (moblevel > plevel + 5)
+ moblevel = plevel + 5;
+
+ uint32 lvldif = moblevel - greylevel;
+ if(lvldif < 3)
+ lvldif = 3;
+
+ uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType));
+ if(skilldif <= 0)
+ return;
+
+ float chance = float(3 * lvldif * skilldif) / plevel;
+ if(!defence)
+ {
+ if(getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE)
+ chance *= 0.1f * GetStat(STAT_INTELLECT);
+ }
+
+ chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%
+
+ if(roll_chance_f(chance))
+ {
+ if(defence)
+ UpdateDefense();
+ else
+ UpdateWeaponSkill(attType);
+ }
+ else
+ return;
+}
+
+void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skillid)
+ {
+ uint32 bonus_val = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+ int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);
+ int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);
+
+ if(talent) // permanent bonus stored in high part
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));
+ else // temporary/item bonus stored in low part
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus));
+ return;
+ }
+}
+
+void Player::UpdateMaxSkills()
+{
+ uint16 maxconfskill = sWorld.GetConfigMaxSkillValue();
+
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);
+ if(!pSkill)
+ continue;
+
+ if(GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL)
+ continue;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint32 max = SKILL_MAX(data);
+ uint32 val = SKILL_VALUE(data);
+
+ // update only level dependent max skill values
+ if(max!=1 && max != maxconfskill)
+ {
+ uint32 max_Skill = GetMaxSkillValueForLevel();
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(val,max_Skill));
+ }
+ }
+}
+
+void Player::UpdateSkillsToMaxSkillsForLevel()
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+ if( IsProfessionSkill(pskill) || pskill == SKILL_RIDING )
+ continue;
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+
+ uint32 max = SKILL_MAX(data);
+
+ if(max > 1)
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max));
+
+ if(pskill == SKILL_DEFENSE)
+ {
+ UpdateBlockPercentage();
+ }
+ }
+}
+
+// This functions sets a skill line value (and adds if doesn't exist yet)
+// To "remove" a skill line, set it's values to zero
+void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
+{
+ if(!id)
+ return;
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == id) break;
+
+ if(i<PLAYER_MAX_SKILLS) //has skill
+ {
+ if(currVal)
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
+ else //remove
+ {
+ // clear skill fields
+ SetUInt32Value(PLAYER_SKILL_INDEX(i),0);
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0);
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // remove spells that depend on this skill when removing the skill
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if (_spell_idx->second->skillId == id)
+ {
+ // this may remove more than one spell (dependants)
+ removeSpell(itr->first);
+ next = m_spells.begin();
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if(currVal) //add
+ {
+ for (i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
+ if(!pSkill)
+ {
+ sLog.outError("Skill not found in SkillLineStore: skill #%u", id);
+ return;
+ }
+ // enable unlearn button for primary professions only
+ if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
+ else
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
+
+ // apply skill bonuses
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // temporary bonuses
+ AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL);
+ for(AuraList::const_iterator i = mModSkill.begin(); i != mModSkill.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == int32(id))
+ (*i)->ApplyModifier(true);
+
+ // permanent bonuses
+ AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT);
+ for(AuraList::const_iterator i = mModSkillTalent.begin(); i != mModSkillTalent.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == int32(id))
+ (*i)->ApplyModifier(true);
+
+ // Learn all spells for skill
+ learnSkillRewardedSpells(id);
+ return;
+ }
+ }
+}
+
+bool Player::HasSkill(uint32 skill) const
+{
+ if(!skill)return false;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+uint16 Player::GetSkillValue(uint32 skill) const
+{
+ if(!skill)
+ return 0;
+
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+
+ int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_TEMP_BONUS(bonus);
+ result += SKILL_PERM_BONUS(bonus);
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetMaxSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+
+ int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_TEMP_BONUS(bonus);
+ result += SKILL_PERM_BONUS(bonus);
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetPureMaxSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetBaseSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetPureSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
+ }
+ }
+ return 0;
+}
+
+int16 Player::GetSkillTempBonusValue(uint32 skill) const
+{
+ if(!skill)
+ return 0;
+
+ for (int i = 0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
+ }
+ }
+
+ return 0;
+}
+
+void Player::SendInitialActionButtons()
+{
+ sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
+
+ WorldPacket data(SMSG_ACTION_BUTTONS, (MAX_ACTION_BUTTONS*4));
+ 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);
+ }
+ else
+ {
+ data << uint32(0);
+ }
+ }
+
+ GetSession()->SendPacket( &data );
+ sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
+}
+
+void Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc)
+{
+ 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;
+ }
+
+ // check cheating with adding non-known spells to action bar
+ if(type==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;
+ }
+
+ 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;
+ }
+ }
+
+ ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
+
+ if (buttonItr==m_actionButtons.end())
+ { // just add new button
+ m_actionButtons[button] = ActionButton(action,type,misc);
+ }
+ 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 );
+}
+
+void Player::removeActionButton(uint8 button)
+{
+ ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
+ if (buttonItr==m_actionButtons.end())
+ return;
+
+ if(buttonItr->second.uState==ACTIONBUTTON_NEW)
+ m_actionButtons.erase(buttonItr); // new and not saved
+ else
+ buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save
+
+ sLog.outDetail( "Action Button '%u' Removed from Player '%u'", button, GetGUIDLow() );
+}
+
+void Player::SetDontMove(bool dontMove)
+{
+ m_dontMove = dontMove;
+}
+
+bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport)
+{
+ // prevent crash when a bad coord is sent by the client
+ if(!MaNGOS::IsValidMapCoord(x,y,z,orientation))
+ {
+ sLog.outDebug("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!",x,y,z,orientation,teleport,GetGUIDLow());
+ return false;
+ }
+
+ Map *m = MapManager::Instance().GetMap(GetMapId(), this);
+
+ const float old_x = GetPositionX();
+ const float old_y = GetPositionY();
+ const float old_z = GetPositionZ();
+ const float old_r = GetOrientation();
+
+ if( teleport || old_x != x || old_y != y || old_z != z || old_r != orientation )
+ {
+ if (teleport || old_x != x || old_y != y || old_z != z)
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
+ else
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
+
+ // move and update visible state if need
+ m->PlayerRelocation(this, x, y, z, orientation);
+
+ // reread after Map::Relocation
+ m = MapManager::Instance().GetMap(GetMapId(), this);
+ x = GetPositionX();
+ y = GetPositionY();
+ z = GetPositionZ();
+ }
+
+ // code block for underwater state update
+ UpdateUnderwaterState(m, x, y, z);
+
+
+ CheckExploreSystem();
+
+ // group update
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
+
+ return true;
+}
+
+void Player::SaveRecallPosition()
+{
+ m_recallMap = GetMapId();
+ m_recallX = GetPositionX();
+ m_recallY = GetPositionY();
+ m_recallZ = GetPositionZ();
+ m_recallO = GetOrientation();
+}
+
+void Player::SendMessageToSet(WorldPacket *data, bool self)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self);
+}
+
+void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self);
+}
+
+void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self,own_team_only);
+}
+
+void Player::SendDirectMessage(WorldPacket *data)
+{
+ GetSession()->SendPacket(data);
+}
+
+void Player::CheckExploreSystem()
+{
+ if (!isAlive())
+ return;
+
+ if (isInFlight())
+ return;
+
+ uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY());
+ if(areaFlag==0xffff)
+ return;
+ int offset = areaFlag / 32;
+
+ if(offset >= 128)
+ {
+ sLog.outError("ERROR: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 64 ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset);
+ return;
+ }
+
+ uint32 val = (uint32)(1 << (areaFlag % 32));
+ uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+
+ if( !(currFields & val) )
+ {
+ SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
+
+ AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId());
+ if(!p)
+ {
+ sLog.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(),GetPositionY(),GetMapId());
+ }
+ else if(p->area_level > 0)
+ {
+ uint32 area = p->ID;
+ if (getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ SendExplorationExperience(area,0);
+ }
+ else
+ {
+ int32 diff = int32(getLevel()) - p->area_level;
+ uint32 XP = 0;
+ if (diff < -5)
+ {
+ XP = uint32(objmgr.GetBaseXP(getLevel()+5)*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+ else if (diff > 5)
+ {
+ int32 exploration_percent = (100-((diff-5)*5));
+ if (exploration_percent > 100)
+ exploration_percent = 100;
+ else if (exploration_percent < 0)
+ exploration_percent = 0;
+
+ XP = uint32(objmgr.GetBaseXP(p->area_level)*exploration_percent/100*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+ else
+ {
+ XP = uint32(objmgr.GetBaseXP(p->area_level)*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+
+ GiveXP( XP, NULL );
+ SendExplorationExperience(area,XP);
+ }
+ sLog.outDetail("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area);
+ }
+ }
+}
+
+uint32 Player::TeamForRace(uint8 race)
+{
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
+ if(!rEntry)
+ {
+ sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
+ return ALLIANCE;
+ }
+
+ switch(rEntry->TeamID)
+ {
+ case 7: return ALLIANCE;
+ case 1: return HORDE;
+ }
+
+ sLog.outError("Race %u have wrong team id in DBC: wrong DBC files?",uint32(race),rEntry->TeamID);
+ return ALLIANCE;
+}
+
+uint32 Player::getFactionForRace(uint8 race)
+{
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
+ if(!rEntry)
+ {
+ sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
+ return 0;
+ }
+
+ return rEntry->FactionID;
+}
+
+void Player::setFactionForRace(uint8 race)
+{
+ m_team = TeamForRace(race);
+ setFaction( getFactionForRace(race) );
+}
+
+void Player::UpdateReputation() const
+{
+ sLog.outDetail( "WORLD: Player::UpdateReputation" );
+
+ for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
+ {
+ SendFactionState(&(itr->second));
+ }
+}
+
+void Player::SendFactionState(FactionState const* faction) const
+{
+ if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it
+ {
+ WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0
+ data << (float) 0; // unk 2.4.0
+ data << (uint32) 1; // count
+ // for
+ data << (uint32) faction->ReputationListID;
+ data << (uint32) faction->Standing;
+ // end for
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::SendInitialReputations()
+{
+ WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5));
+ data << uint32 (0x00000080);
+
+ RepListID a = 0;
+
+ for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); itr++)
+ {
+ // fill in absent fields
+ for (; a != itr->first; a++)
+ {
+ data << uint8 (0x00);
+ data << uint32 (0x00000000);
+ }
+
+ // fill in encountered data
+ data << uint8 (itr->second.Flags);
+ data << uint32 (itr->second.Standing);
+
+ ++a;
+ }
+
+ // fill in absent fields
+ for (; a != 128; a++)
+ {
+ data << uint8 (0x00);
+ data << uint32 (0x00000000);
+ }
+
+ GetSession()->SendPacket(&data);
+}
+
+FactionState const* Player::GetFactionState( FactionEntry const* factionEntry) const
+{
+ FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ return &itr->second;
+
+ return NULL;
+}
+
+void Player::SetFactionAtWar(FactionState* faction, bool atWar)
+{
+ // not allow declare war to own faction
+ if(atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) )
+ return;
+
+ // already set
+ if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar)
+ return;
+
+ if( atWar )
+ faction->Flags |= FACTION_FLAG_AT_WAR;
+ else
+ faction->Flags &= ~FACTION_FLAG_AT_WAR;
+
+ faction->Changed = true;
+}
+
+void Player::SetFactionInactive(FactionState* faction, bool inactive)
+{
+ // always invisible or hidden faction can't be inactive
+ if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) )
+ return;
+
+ // already set
+ if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive)
+ return;
+
+ if(inactive)
+ faction->Flags |= FACTION_FLAG_INACTIVE;
+ else
+ faction->Flags &= ~FACTION_FLAG_INACTIVE;
+
+ faction->Changed = true;
+}
+
+void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId)
+{
+ FactionTemplateEntry const*factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ return;
+
+ SetFactionVisibleForFactionId(factionTemplateEntry->faction);
+}
+
+void Player::SetFactionVisibleForFactionId(uint32 FactionId)
+{
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(FactionId);
+ if(!factionEntry)
+ return;
+
+ if(factionEntry->reputationListID < 0)
+ return;
+
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr == m_factions.end())
+ return;
+
+ SetFactionVisible(&itr->second);
+}
+
+void Player::SetFactionVisible(FactionState* faction)
+{
+ // always invisible or hidden faction can't be make visible
+ if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN))
+ return;
+
+ // already set
+ if(faction->Flags & FACTION_FLAG_VISIBLE)
+ return;
+
+ faction->Flags |= FACTION_FLAG_VISIBLE;
+ faction->Changed = true;
+
+ if(!m_session->PlayerLoading())
+ {
+ // make faction visible in reputation list at client
+ WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
+ data << faction->ReputationListID;
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::SetInitialFactions()
+{
+ for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(i);
+
+ if( factionEntry && (factionEntry->reputationListID >= 0))
+ {
+ FactionState newFaction;
+ newFaction.ID = factionEntry->ID;
+ newFaction.ReputationListID = factionEntry->reputationListID;
+ newFaction.Standing = 0;
+ newFaction.Flags = GetDefaultReputationFlags(factionEntry);
+ newFaction.Changed = true;
+
+ m_factions[newFaction.ReputationListID] = newFaction;
+ }
+ }
+}
+
+uint32 Player::GetDefaultReputationFlags(const FactionEntry *factionEntry) const
+{
+ if (!factionEntry)
+ return 0;
+
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (int i=0; i < 4; i++)
+ {
+ if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
+ (factionEntry->BaseRepClassMask[i]==0 ||
+ (factionEntry->BaseRepClassMask[i] & classMask) ) )
+ return factionEntry->ReputationFlags[i];
+ }
+ return 0;
+}
+
+int32 Player::GetBaseReputation(const FactionEntry *factionEntry) const
+{
+ if (!factionEntry)
+ return 0;
+
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (int i=0; i < 4; i++)
+ {
+ if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
+ (factionEntry->BaseRepClassMask[i]==0 ||
+ (factionEntry->BaseRepClassMask[i] & classMask) ) )
+ return factionEntry->BaseRepValue[i];
+ }
+
+ // in faction.dbc exist factions with (RepListId >=0, listed in character reputation list) with all BaseRepRaceMask[i]==0
+ return 0;
+}
+
+int32 Player::GetReputation(uint32 faction_id) const
+{
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id);
+
+ if (!factionEntry)
+ {
+ sLog.outError("Player::GetReputation: Can't get reputation of %s for unknown faction (faction template id) #%u.",GetName(), faction_id);
+ return 0;
+ }
+
+ return GetReputation(factionEntry);
+}
+
+int32 Player::GetReputation(const FactionEntry *factionEntry) const
+{
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return 0;
+
+ FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ return GetBaseReputation(factionEntry) + itr->second.Standing;
+
+ return 0;
+}
+
+ReputationRank Player::GetReputationRank(uint32 faction) const
+{
+ FactionEntry const*factionEntry = sFactionStore.LookupEntry(faction);
+ if(!factionEntry)
+ return MIN_REPUTATION_RANK;
+
+ return GetReputationRank(factionEntry);
+}
+
+ReputationRank Player::ReputationToRank(int32 standing) const
+{
+ int32 Limit = Reputation_Cap + 1;
+ for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i)
+ {
+ Limit -= ReputationRank_Length[i];
+ if (standing >= Limit )
+ return ReputationRank(i);
+ }
+ return MIN_REPUTATION_RANK;
+}
+
+ReputationRank Player::GetReputationRank(const FactionEntry *factionEntry) const
+{
+ int32 Reputation = GetReputation(factionEntry);
+ return ReputationToRank(Reputation);
+}
+
+ReputationRank Player::GetBaseReputationRank(const FactionEntry *factionEntry) const
+{
+ int32 Reputation = GetBaseReputation(factionEntry);
+ return ReputationToRank(Reputation);
+}
+
+bool Player::ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation)
+{
+ FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ {
+ sLog.outError("Player::ModifyFactionReputation: Can't update reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
+ return false;
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
+
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return false;
+
+ return ModifyFactionReputation(factionEntry, DeltaReputation);
+}
+
+bool Player::ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
+ if (flist)
+ {
+ bool res = false;
+ for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
+ {
+ FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
+ if(factionEntryCalc)
+ res = ModifyOneFactionReputation(factionEntryCalc, standing);
+ }
+ return res;
+ }
+ else
+ return ModifyOneFactionReputation(factionEntry, standing);
+}
+
+bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ {
+ int32 BaseRep = GetBaseReputation(factionEntry);
+ int32 new_rep = BaseRep + itr->second.Standing + standing;
+
+ if (new_rep > Reputation_Cap)
+ new_rep = Reputation_Cap;
+ else
+ if (new_rep < Reputation_Bottom)
+ new_rep = Reputation_Bottom;
+
+ if(ReputationToRank(new_rep) <= REP_HOSTILE)
+ SetFactionAtWar(&itr->second,true);
+
+ itr->second.Standing = new_rep - BaseRep;
+ itr->second.Changed = true;
+
+ SetFactionVisible(&itr->second);
+
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ if(uint32 questid = GetQuestSlotQuestId(i))
+ {
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( qInfo && qInfo->GetRepObjectiveFaction() == factionEntry->ID )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if(GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ }
+ else if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ {
+ if(GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
+ IncompleteQuest( questid );
+ }
+ }
+ }
+ }
+
+ SendFactionState(&(itr->second));
+
+ return true;
+ }
+ return false;
+}
+
+bool Player::SetFactionReputation(uint32 FactionTemplateId, int32 standing)
+{
+ FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ {
+ sLog.outError("Player::SetFactionReputation: Can't set reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
+ return false;
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
+
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return false;
+
+ return SetFactionReputation(factionEntry, standing);
+}
+
+bool Player::SetFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
+ if (flist)
+ {
+ bool res = false;
+ for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
+ {
+ FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
+ if(factionEntryCalc)
+ res = SetOneFactionReputation(factionEntryCalc, standing);
+ }
+ return res;
+ }
+ else
+ return SetOneFactionReputation(factionEntry, standing);
+}
+
+bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ {
+ if (standing > Reputation_Cap)
+ standing = Reputation_Cap;
+ else
+ if (standing < Reputation_Bottom)
+ standing = Reputation_Bottom;
+
+ int32 BaseRep = GetBaseReputation(factionEntry);
+ itr->second.Standing = standing - BaseRep;
+ itr->second.Changed = true;
+
+ SetFactionVisible(&itr->second);
+
+ if(ReputationToRank(standing) <= REP_HOSTILE)
+ SetFactionAtWar(&itr->second,true);
+
+ SendFactionState(&(itr->second));
+ return true;
+ }
+ return false;
+}
+
+//Calculate total reputation percent player gain with quest/creature level
+int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest)
+{
+ // for grey creature kill received 20%, in other case 100.
+ int32 percent = (!for_quest && (creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel()))) ? 20 : 100;
+
+ int32 repMod = GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN);
+
+ percent += rep > 0 ? repMod : -repMod;
+
+ if(percent <=0)
+ return 0;
+
+ return int32(sWorld.getRate(RATE_REPUTATION_GAIN)*rep*percent/100);
+}
+
+//Calculates how many reputation points player gains in victim's enemy factions
+void Player::RewardReputation(Unit *pVictim, float rate)
+{
+ if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(pVictim->GetEntry());
+
+ if(!Rep)
+ return;
+
+ if(Rep->repfaction1 && (!Rep->team_dependent || GetTeam()==ALLIANCE))
+ {
+ int32 donerep1 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue1,false);
+ donerep1 = int32(donerep1*rate);
+ FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1);
+ uint32 current_reputation_rank1 = GetReputationRank(factionEntry1);
+ if(factionEntry1 && current_reputation_rank1 <= Rep->reputration_max_cap1)
+ ModifyFactionReputation(factionEntry1, donerep1);
+
+ // Wiki: Team factions value divided by 2
+ if(Rep->is_teamaward1)
+ {
+ FactionEntry const *team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team);
+ if(team1_factionEntry)
+ ModifyFactionReputation(team1_factionEntry, donerep1 / 2);
+ }
+ }
+
+ if(Rep->repfaction2 && (!Rep->team_dependent || GetTeam()==HORDE))
+ {
+ int32 donerep2 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue2,false);
+ donerep2 = int32(donerep2*rate);
+ FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2);
+ uint32 current_reputation_rank2 = GetReputationRank(factionEntry2);
+ if(factionEntry2 && current_reputation_rank2 <= Rep->reputration_max_cap2)
+ ModifyFactionReputation(factionEntry2, donerep2);
+
+ // Wiki: Team factions value divided by 2
+ if(Rep->is_teamaward2)
+ {
+ FactionEntry const *team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team);
+ if(team2_factionEntry)
+ ModifyFactionReputation(team2_factionEntry, donerep2 / 2);
+ }
+ }
+}
+
+//Calculate how many reputation points player gain with the quest
+void Player::RewardReputation(Quest const *pQuest)
+{
+ // quest reputation reward/loss
+ for(int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ {
+ if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] )
+ {
+ int32 rep = CalculateReputationGain(pQuest->GetQuestLevel(),pQuest->RewRepValue[i],true);
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]);
+ if(factionEntry)
+ ModifyFactionReputation(factionEntry, rep);
+ }
+ }
+
+ // TODO: implement reputation spillover
+}
+
+void Player::UpdateArenaFields(void)
+{
+ /* arena calcs go here */
+}
+
+void Player::UpdateHonorFields()
+{
+ /// called when rewarding honor and at each save
+ uint64 now = time(NULL);
+ uint64 today = uint64(time(NULL) / DAY) * DAY;
+
+ if(m_lastHonorUpdateTime < today)
+ {
+ uint64 yesterday = today - DAY;
+
+ uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));
+
+ // update yesterday's contribution
+ if(m_lastHonorUpdateTime >= yesterday )
+ {
+ SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
+
+ // this is the first update today, reset today's contribution
+ SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
+ SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0,kills_today));
+ }
+ else
+ {
+ // no honor/kills yesterday or today, reset
+ SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
+ SetUInt32Value(PLAYER_FIELD_KILLS, 0);
+ }
+ }
+
+ m_lastHonorUpdateTime = now;
+}
+
+///Calculate the amount of honor gained based on the victim
+///and the size of the group for which the honor is divided
+///An exact honor value can also be given (overriding the calcs)
+bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor)
+{
+ // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
+ if(GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
+ return false;
+
+ uint64 victim_guid = 0;
+ uint32 victim_rank = 0;
+ time_t now = time(NULL);
+
+ // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
+ UpdateHonorFields();
+
+ if(honor <= 0)
+ {
+ if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
+ return false;
+
+ victim_guid = uVictim->GetGUID();
+
+ if( uVictim->GetTypeId() == TYPEID_PLAYER )
+ {
+ Player *pVictim = (Player *)uVictim;
+
+ if( GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm() )
+ return false;
+
+ float f = 1; //need for total kills (?? need more info)
+ uint32 k_grey = 0;
+ uint32 k_level = getLevel();
+ uint32 v_level = pVictim->getLevel();
+
+ {
+ // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
+ // [0] Just name
+ // [1..14] Alliance honor titles and player name
+ // [15..28] Horde honor titles and player name
+ // [29..38] Other title and player name
+ // [39+] Nothing
+ uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
+ // Get Killer titles, CharTitlesEntry::bit_index
+ // Ranks:
+ // title[1..14] -> rank[5..18]
+ // title[15..28] -> rank[5..18]
+ // title[other] -> 0
+ if (victim_title == 0)
+ victim_guid = 0; // Don't show HK: <rank> message, only log.
+ else if (victim_title < 15)
+ victim_rank = victim_title + 4;
+ else if (victim_title < 29)
+ victim_rank = victim_title - 14 + 4;
+ else
+ victim_guid = 0; // Don't show HK: <rank> message, only log.
+ }
+
+ if(k_level <= 5)
+ k_grey = 0;
+ else if( k_level <= 39 )
+ k_grey = k_level - 5 - k_level/10;
+ else
+ k_grey = k_level - 1 - k_level/5;
+
+ if(v_level<=k_grey)
+ return false;
+
+ float diff_level = (k_level == k_grey) ? 1 : ((float(v_level) - float(k_grey)) / (float(k_level) - float(k_grey)));
+
+ int32 v_rank =1; //need more info
+
+ honor = ((f * diff_level * (190 + v_rank*10))/6);
+ honor *= ((float)k_level) / 70.0f; //factor of dependence on levels of the killer
+
+ // count the number of playerkills in one day
+ ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
+ // and those in a lifetime
+ ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true);
+ }
+ else
+ {
+ Creature *cVictim = (Creature *)uVictim;
+
+ if (!cVictim->isRacialLeader())
+ return false;
+
+ honor = 100; // ??? need more info
+ victim_rank = 19; // HK: Leader
+ }
+ }
+
+ if (uVictim != NULL)
+ {
+ honor *= sWorld.getRate(RATE_HONOR);
+
+ if(groupsize > 1)
+ honor /= groupsize;
+
+ honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor
+ }
+
+ // honor - for show honor points in log
+ // victim_guid - for show victim name in log
+ // victim_rank [1..4] HK: <dishonored rank>
+ // victim_rank [5..19] HK: <alliance\horde rank>
+ // victim_rank [0,20+] HK: <>
+ WorldPacket data(SMSG_PVP_CREDIT,4+8+4);
+ data << (uint32) honor;
+ data << (uint64) victim_guid;
+ data << (uint32) victim_rank;
+
+ GetSession()->SendPacket(&data);
+
+ // add honor points
+ ModifyHonorPoints(int32(honor));
+
+ ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, uint32(honor), true);
+ return true;
+}
+
+void Player::ModifyHonorPoints( int32 value )
+{
+ if(value < 0)
+ {
+ if (GetHonorPoints() > sWorld.getConfig(CONFIG_MAX_HONOR_POINTS))
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) + value);
+ else
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() > uint32(-value) ? GetHonorPoints() + value : 0);
+ }
+ else
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() < sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) - value ? GetHonorPoints() + value : sWorld.getConfig(CONFIG_MAX_HONOR_POINTS));
+}
+
+void Player::ModifyArenaPoints( int32 value )
+{
+ if(value < 0)
+ {
+ if (GetArenaPoints() > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) + value);
+ else
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() > uint32(-value) ? GetArenaPoints() + value : 0);
+ }
+ else
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() < sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) - value ? GetArenaPoints() + value : sWorld.getConfig(CONFIG_MAX_ARENA_POINTS));
+}
+
+uint32 Player::GetGuildIdFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+ ss<<"SELECT guildid FROM guild_member WHERE guid='"<<guid<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint32 v = result->Fetch()[0].GetUInt32();
+ delete result;
+ return v;
+ }
+ else
+ return 0;
+}
+
+uint32 Player::GetRankFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+ ss<<"SELECT rank FROM guild_member WHERE guid='"<<guid<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint32 v = result->Fetch()[0].GetUInt32();
+ delete result;
+ return v;
+ }
+ else
+ return 0;
+}
+
+uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
+{
+ // need fix it!
+ QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ // init id to 0, check the arena type before assigning a value to id
+ uint32 id = 0;
+ do
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM arena_team WHERE arenateamid='%u'", id);
+ if(result2)
+ {
+ uint8 dbtype = (*result2)[0].GetUInt32();
+ delete result2;
+ if(dbtype == type)
+ {
+ // if the type matches, we've found the id
+ id = (*result)[0].GetUInt32();
+ break;
+ }
+ }
+ } while(result->NextRow());
+ delete result;
+ return id;
+ }
+ // no arenateam for the specified guid, return 0
+ return 0;
+}
+
+uint32 Player::GetZoneIdFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+
+ ss<<"SELECT zone FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if (!result)
+ return 0;
+ Field* fields = result->Fetch();
+ uint32 zone = fields[0].GetUInt32();
+ delete result;
+
+ if (!zone)
+ {
+ // stored zone is zero, use generic and slow zone detection
+ ss.str("");
+ ss<<"SELECT map,position_x,position_y FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ result = CharacterDatabase.Query(ss.str().c_str());
+ if( !result )
+ return 0;
+ fields = result->Fetch();
+ uint32 map = fields[0].GetUInt32();
+ float posx = fields[1].GetFloat();
+ float posy = fields[2].GetFloat();
+ delete result;
+
+ zone = MapManager::Instance().GetZoneId(map,posx,posy);
+
+ ss.str("");
+ ss << "UPDATE characters SET zone='"<<zone<<"' WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ CharacterDatabase.Execute(ss.str().c_str());
+ }
+
+ return zone;
+}
+
+void Player::UpdateArea(uint32 newArea)
+{
+ // FFA_PVP flags are area and not zone id dependent
+ // so apply them accordingly
+ m_areaUpdateId = newArea;
+
+ AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
+
+ if(area && (area->flags & AREA_FLAG_ARENA))
+ {
+ if(!isGameMaster())
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ }
+ else
+ {
+ // remove ffa flag only if not ffapvp realm
+ // removal in sanctuaries and capitals is handled in zone update
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ }
+
+ UpdateAreaDependentAuras(newArea);
+}
+
+void Player::UpdateZone(uint32 newZone)
+{
+ m_zoneUpdateId = newZone;
+ m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+
+ // zone changed, so area changed as well, update it
+ UpdateArea(GetAreaId());
+
+ AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
+ if(!zone)
+ return;
+
+ if (sWorld.getConfig(CONFIG_WEATHER))
+ {
+ Weather *wth = sWorld.FindWeather(zone->ID);
+ if(wth)
+ {
+ wth->SendWeatherUpdateToPlayer(this);
+ }
+ else
+ {
+ if(!sWorld.AddWeather(zone->ID))
+ {
+ // send fine weather packet to remove old zone's weather
+ Weather::SendFineWeatherUpdateToPlayer(this);
+ }
+ }
+ }
+
+ pvpInfo.inHostileArea =
+ GetTeam() == ALLIANCE && zone->team == AREATEAM_HORDE ||
+ GetTeam() == HORDE && zone->team == AREATEAM_ALLY ||
+ sWorld.IsPvPRealm() && zone->team == AREATEAM_NONE ||
+ InBattleGround(); // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
+
+ if(pvpInfo.inHostileArea) // in hostile area
+ {
+ if(!IsPvP() || pvpInfo.endTimer != 0)
+ UpdatePvP(true, true);
+ }
+ else // in friendly area
+ {
+ if(IsPvP() && !HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0)
+ pvpInfo.endTimer = time(0); // start toggle-off
+ }
+
+ if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary
+ {
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
+ if(sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ else
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
+ }
+
+ if(zone->flags & AREA_FLAG_CAPITAL) // in capital city
+ {
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_IN_CITY);
+ InnEnter(time(0),GetMapId(),0,0,0);
+
+ if(sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ else // anywhere else
+ {
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) // but resting (walk from city or maybe in tavern or leave tavern recently)
+ {
+ if(GetRestType()==REST_TYPE_IN_TAVERN) // has been in tavern. Is still in?
+ {
+ if(GetMapId()!=GetInnPosMapId() || sqrt((GetPositionX()-GetInnPosX())*(GetPositionX()-GetInnPosX())+(GetPositionY()-GetInnPosY())*(GetPositionY()-GetInnPosY())+(GetPositionZ()-GetInnPosZ())*(GetPositionZ()-GetInnPosZ()))>40)
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_NO);
+
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ }
+ else // not in tavern (leave city then)
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_NO);
+
+ // Set player to FFA PVP when not in rested enviroment.
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ }
+ }
+
+ // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
+ // if player resurrected at teleport this will be applied in resurrect code
+ if(isAlive())
+ DestroyZoneLimitedItem( true, newZone );
+
+ // recent client version not send leave/join channel packets for built-in local channels
+ UpdateLocalChannels( newZone );
+
+ // group update
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE);
+
+ UpdateZoneDependentAuras(newZone);
+}
+
+//If players are too far way of duel flag... then player loose the duel
+void Player::CheckDuelDistance(time_t currTime)
+{
+ if(!duel)
+ return;
+
+ uint64 duelFlagGUID = GetUInt64Value(PLAYER_DUEL_ARBITER);
+ GameObject* obj = ObjectAccessor::GetGameObject(*this, duelFlagGUID);
+ if(!obj)
+ return;
+
+ if(duel->outOfBound == 0)
+ {
+ if(!IsWithinDistInMap(obj, 50))
+ {
+ duel->outOfBound = currTime;
+
+ WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
+ GetSession()->SendPacket(&data);
+ }
+ }
+ else
+ {
+ if(IsWithinDistInMap(obj, 40))
+ {
+ duel->outOfBound = 0;
+
+ WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
+ GetSession()->SendPacket(&data);
+ }
+ else if(currTime >= (duel->outOfBound+10))
+ {
+ DuelComplete(DUEL_FLED);
+ }
+ }
+}
+
+void Player::DuelComplete(DuelCompleteType type)
+{
+ // duel not requested
+ if(!duel)
+ return;
+
+ WorldPacket data(SMSG_DUEL_COMPLETE, (1));
+ data << (uint8)((type != DUEL_INTERUPTED) ? 1 : 0);
+ GetSession()->SendPacket(&data);
+ duel->opponent->GetSession()->SendPacket(&data);
+
+ if(type != DUEL_INTERUPTED)
+ {
+ data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
+ data << (uint8)((type==DUEL_WON) ? 0 : 1); // 0 = just won; 1 = fled
+ data << duel->opponent->GetName();
+ data << GetName();
+ SendMessageToSet(&data,true);
+ }
+
+ // 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 = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
+ if(obj)
+ duel->initiator->RemoveGameObject(obj,true);
+
+ /* remove auras */
+ std::vector<uint32> auras2remove;
+ AuraMap const& vAuras = duel->opponent->GetAuras();
+ for (AuraMap::const_iterator i = vAuras.begin(); i != vAuras.end(); i++)
+ {
+ if (!i->second->IsPositive() && i->second->GetCasterGUID() == GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
+ auras2remove.push_back(i->second->GetId());
+ }
+
+ for(size_t i=0; i<auras2remove.size(); i++)
+ duel->opponent->RemoveAurasDueToSpell(auras2remove[i]);
+
+ auras2remove.clear();
+ AuraMap const& auras = GetAuras();
+ for (AuraMap::const_iterator i = auras.begin(); i != auras.end(); i++)
+ {
+ if (!i->second->IsPositive() && i->second->GetCasterGUID() == duel->opponent->GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
+ auras2remove.push_back(i->second->GetId());
+ }
+ for(size_t i=0; i<auras2remove.size(); i++)
+ RemoveAurasDueToSpell(auras2remove[i]);
+
+ // cleanup combo points
+ if(GetComboTarget()==duel->opponent->GetGUID())
+ ClearComboPoints();
+ else if(GetComboTarget()==duel->opponent->GetPetGUID())
+ ClearComboPoints();
+
+ if(duel->opponent->GetComboTarget()==GetGUID())
+ duel->opponent->ClearComboPoints();
+ else if(duel->opponent->GetComboTarget()==GetPetGUID())
+ duel->opponent->ClearComboPoints();
+
+ //cleanups
+ SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+ duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+
+ delete duel->opponent->duel;
+ duel->opponent->duel = NULL;
+ delete duel;
+ duel = NULL;
+}
+
+//---------------------------------------------------------//
+
+void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply)
+{
+ if(slot >= INVENTORY_SLOT_BAG_END || !item)
+ return;
+
+ // not apply/remove mods for broken item
+ if(item->IsBroken())
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+
+ if(!proto)
+ return;
+
+ sLog.outDetail("applying mods for item %u ",item->GetGUIDLow());
+
+ uint32 attacktype = Player::GetAttackBySlot(slot);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(item,WeaponAttackType(attacktype),apply);
+
+ _ApplyItemBonuses(proto,slot,apply);
+
+ if( slot==EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+
+ ApplyItemEquipSpell(item,apply);
+ ApplyEnchantment(item, apply);
+
+ if(proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
+ CorrectMetaGemEnchants(slot, apply);
+
+ sLog.outDebug("_ApplyItemMods complete.");
+}
+
+void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply)
+{
+ if(slot >= INVENTORY_SLOT_BAG_END || !proto)
+ return;
+
+ for (int i = 0; i < 10; i++)
+ {
+ float val = float (proto->ItemStat[i].ItemStatValue);
+
+ if(val==0)
+ continue;
+
+ switch (proto->ItemStat[i].ItemStatType)
+ {
+ case ITEM_MOD_MANA:
+ HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
+ break;
+ case ITEM_MOD_HEALTH: // modify HP
+ HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
+ break;
+ case ITEM_MOD_AGILITY: // modify agility
+ HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_AGILITY, val, apply);
+ break;
+ case ITEM_MOD_STRENGTH: //modify strength
+ HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_STRENGTH, val, apply);
+ break;
+ case ITEM_MOD_INTELLECT: //modify intellect
+ HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_INTELLECT, val, apply);
+ break;
+ case ITEM_MOD_SPIRIT: //modify spirit
+ HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_SPIRIT, val, apply);
+ break;
+ case ITEM_MOD_STAMINA: //modify stamina
+ HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_STAMINA, val, apply);
+ break;
+ case ITEM_MOD_DEFENSE_SKILL_RATING:
+ ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
+ break;
+ case ITEM_MOD_DODGE_RATING:
+ ApplyRatingMod(CR_DODGE, int32(val), apply);
+ break;
+ case ITEM_MOD_PARRY_RATING:
+ ApplyRatingMod(CR_PARRY, int32(val), apply);
+ break;
+ case ITEM_MOD_BLOCK_RATING:
+ ApplyRatingMod(CR_BLOCK, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_MELEE_RATING:
+ ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_RANGED_RATING:
+ ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_SPELL_RATING:
+ ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_MELEE_RATING:
+ ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_RANGED_RATING:
+ ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_SPELL_RATING:
+ ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_MELEE_RATING:
+ ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_RANGED_RATING:
+ ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_SPELL_RATING:
+ ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_RATING:
+ ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_RATING:
+ ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_RESILIENCE_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_RATING:
+ ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_EXPERTISE_RATING:
+ ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
+ break;
+ }
+ }
+
+ if (proto->Armor)
+ HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(proto->Armor), apply);
+
+ if (proto->Block)
+ HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(proto->Block), apply);
+
+ if (proto->HolyRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply);
+
+ if (proto->FireRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply);
+
+ if (proto->NatureRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply);
+
+ if (proto->FrostRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply);
+
+ if (proto->ShadowRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply);
+
+ if (proto->ArcaneRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply);
+
+ WeaponAttackType attType = BASE_ATTACK;
+ float damage = 0.0f;
+
+ if( slot == EQUIPMENT_SLOT_RANGED && (
+ proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
+ proto->InventoryType == INVTYPE_RANGEDRIGHT ))
+ {
+ attType = RANGED_ATTACK;
+ }
+ else if(slot==EQUIPMENT_SLOT_OFFHAND)
+ {
+ attType = OFF_ATTACK;
+ }
+
+ if (proto->Damage[0].DamageMin > 0 )
+ {
+ damage = apply ? proto->Damage[0].DamageMin : BASE_MINDAMAGE;
+ SetBaseWeaponDamage(attType, MINDAMAGE, damage);
+ //sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
+ }
+
+ if (proto->Damage[0].DamageMax > 0 )
+ {
+ damage = apply ? proto->Damage[0].DamageMax : BASE_MAXDAMAGE;
+ SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
+ }
+
+ if(!IsUseEquipedWeapon(slot==EQUIPMENT_SLOT_MAINHAND))
+ return;
+
+ if (proto->Delay)
+ {
+ if(slot == EQUIPMENT_SLOT_RANGED)
+ SetAttackTime(RANGED_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ else if(slot==EQUIPMENT_SLOT_MAINHAND)
+ SetAttackTime(BASE_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ else if(slot==EQUIPMENT_SLOT_OFFHAND)
+ SetAttackTime(OFF_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ }
+
+ if(CanModifyStats() && (damage || proto->Delay))
+ UpdateDamagePhysical(attType);
+}
+
+void Player::_ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply)
+{
+ AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT);
+ for(AuraList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr)
+ _ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply);
+
+ AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
+ for(AuraList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr)
+ _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
+
+ AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
+ for(AuraList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr)
+ _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
+}
+
+void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
+{
+ // generic not weapon specific case processes in aura code
+ if(aura->GetSpellProto()->EquippedItemClass == -1)
+ return;
+
+ BaseModGroup mod = BASEMOD_END;
+ switch(attackType)
+ {
+ case BASE_ATTACK: mod = CRIT_PERCENTAGE; break;
+ case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break;
+ case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
+ default: return;
+ }
+
+ if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
+ {
+ HandleBaseModValue(mod, FLAT_MOD, float (aura->GetModifier()->m_amount), apply);
+ }
+}
+
+void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
+{
+ // ignore spell mods for not wands
+ Modifier const* modifier = aura->GetModifier();
+ if((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0)
+ return;
+
+ // generic not weapon specific case processes in aura code
+ if(aura->GetSpellProto()->EquippedItemClass == -1)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_END;
+ switch(attackType)
+ {
+ case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
+ case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
+ case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
+ default: return;
+ }
+
+ UnitModifierType unitModType = TOTAL_VALUE;
+ switch(modifier->m_auraname)
+ {
+ case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break;
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
+ default: return;
+ }
+
+ if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
+ {
+ HandleStatModifier(unitMod, unitModType, float(modifier->m_amount),apply);
+ }
+}
+
+void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
+{
+ if(!item)
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+ if(!proto)
+ return;
+
+ for (int i = 0; i < 5; i++)
+ {
+ _Spell const& spellData = proto->Spells[i];
+
+ // no spell
+ if(!spellData.SpellId )
+ continue;
+
+ // wrong triggering type
+ if(apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
+ continue;
+
+ // check if it is valid spell
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellData.SpellId);
+ if(!spellproto)
+ continue;
+
+ ApplyEquipSpell(spellproto,item,apply,form_change);
+ }
+}
+
+void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change)
+{
+ if(apply)
+ {
+ // Cannot be used in this stance/form
+ if(GetErrorAtShapeshiftedCast(spellInfo, m_form)!=0)
+ return;
+
+ if(form_change) // check aura active state from other form
+ {
+ bool found = false;
+ for (int k=0; k < 3; ++k)
+ {
+ spellEffectPair spair = spellEffectPair(spellInfo->Id, k);
+ for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair); ++iter)
+ {
+ if(!item || iter->second->GetCastItemGUID() == item->GetGUID())
+ {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ break;
+ }
+
+ if(found) // and skip re-cast already active aura at form change
+ return;
+ }
+
+ DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
+
+ CastSpell(this,spellInfo,true,item);
+ }
+ else
+ {
+ if(form_change) // check aura compatibility
+ {
+ // Cannot be used in this stance/form
+ if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==0)
+ return; // and remove only not compatible at form change
+ }
+
+ if(item)
+ RemoveAurasDueToItemSpell(item,spellInfo->Id); // un-apply all spells , not only at-equipped
+ else
+ RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
+ }
+}
+
+void Player::UpdateEquipSpellsAtFormChange()
+{
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] && !m_items[i]->IsBroken())
+ {
+ ApplyItemEquipSpell(m_items[i],false,true); // remove spells that not fit to form
+ ApplyItemEquipSpell(m_items[i],true,true); // add spells that fit form but not active
+ }
+ }
+
+ // item set bonuses not dependent from item broken state
+ for(size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex)
+ {
+ ItemSetEffect* eff = ItemSetEff[setindex];
+ if(!eff)
+ continue;
+
+ for(uint32 y=0;y<8; ++y)
+ {
+ SpellEntry const* spellInfo = eff->spells[y];
+ if(!spellInfo)
+ continue;
+
+ ApplyEquipSpell(spellInfo,NULL,false,true); // remove spells that not fit to form
+ ApplyEquipSpell(spellInfo,NULL,true,true); // add spells that fit form but not active
+ }
+ }
+}
+
+void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType)
+{
+ if(!item || item->IsBroken())
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+ if(!proto)
+ return;
+
+ if (!Target || Target == this )
+ return;
+
+ for (int i = 0; i < 5; i++)
+ {
+ _Spell const& spellData = proto->Spells[i];
+
+ // no spell
+ if(!spellData.SpellId )
+ continue;
+
+ // wrong triggering type
+ if(spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown Item spellid %i", spellData.SpellId);
+ continue;
+ }
+
+ // not allow proc extra attack spell at extra attack
+ if( m_extraAttacks && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
+ return;
+
+ float chance = spellInfo->procChance;
+
+ if(spellData.SpellPPMRate)
+ {
+ uint32 WeaponSpeed = GetAttackTime(attType);
+ chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate);
+ }
+ else if(chance > 100.0f)
+ {
+ chance = GetWeaponProcChance();
+ }
+
+ if (roll_chance_f(chance))
+ this->CastSpell(Target, spellInfo->Id, true, item);
+ }
+
+ // item combat enchantments
+ for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
+ {
+ uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant) continue;
+ for (int s=0;s<3;s++)
+ {
+ if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
+ if (!spellInfo)
+ {
+ sLog.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
+ continue;
+ }
+
+ float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
+ if (roll_chance_f(chance))
+ {
+ if(IsPositiveSpell(pEnchant->spellid[s]))
+ CastSpell(this, pEnchant->spellid[s], true, item);
+ else
+ CastSpell(Target, pEnchant->spellid[s], true, item);
+ }
+ }
+ }
+}
+
+void Player::_RemoveAllItemMods()
+{
+ sLog.outDebug("_RemoveAllItemMods start.");
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ // item set bonuses not dependent from item broken state
+ if(proto->ItemSet)
+ RemoveItemsSetItem(this,proto);
+
+ if(m_items[i]->IsBroken())
+ continue;
+
+ ApplyItemEquipSpell(m_items[i],false);
+ ApplyEnchantment(m_items[i], false);
+ }
+ }
+
+ 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;
+
+ uint32 attacktype = Player::GetAttackBySlot(i);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),false);
+
+ _ApplyItemBonuses(proto,i, false);
+
+ if( i == EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+ }
+ }
+
+ sLog.outDebug("_RemoveAllItemMods complete.");
+}
+
+void Player::_ApplyAllItemMods()
+{
+ sLog.outDebug("_ApplyAllItemMods start.");
+
+ 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;
+
+ uint32 attacktype = Player::GetAttackBySlot(i);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),true);
+
+ _ApplyItemBonuses(proto,i, true);
+
+ if( i == EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+ }
+ }
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ // item set bonuses not dependent from item broken state
+ if(proto->ItemSet)
+ AddItemsSetItem(this,m_items[i]);
+
+ if(m_items[i]->IsBroken())
+ continue;
+
+ ApplyItemEquipSpell(m_items[i],true);
+ ApplyEnchantment(m_items[i], true);
+ }
+ }
+
+ sLog.outDebug("_ApplyAllItemMods complete.");
+}
+
+void Player::_ApplyAmmoBonuses()
+{
+ // check ammo
+ uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
+ if(!ammo_id)
+ return;
+
+ float currentAmmoDPS;
+
+ ItemPrototype const *ammo_proto = objmgr.GetItemPrototype( ammo_id );
+ if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
+ currentAmmoDPS = 0.0f;
+ else
+ currentAmmoDPS = ammo_proto->Damage[0].DamageMin;
+
+ if(currentAmmoDPS == GetAmmoDPS())
+ return;
+
+ m_ammoDPS = currentAmmoDPS;
+
+ if(CanModifyStats())
+ UpdateDamagePhysical(RANGED_ATTACK);
+}
+
+bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const
+{
+ if(!ammo_proto)
+ return false;
+
+ // check ranged weapon
+ Item *weapon = GetWeaponForAttack( RANGED_ATTACK );
+ if(!weapon || weapon->IsBroken() )
+ return false;
+
+ ItemPrototype const* weapon_proto = weapon->GetProto();
+ if(!weapon_proto || weapon_proto->Class!=ITEM_CLASS_WEAPON )
+ return false;
+
+ // check ammo ws. weapon compatibility
+ switch(weapon_proto->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ if(ammo_proto->SubClass!=ITEM_SUBCLASS_ARROW)
+ return false;
+ break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ if(ammo_proto->SubClass!=ITEM_SUBCLASS_BULLET)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
+ Called by remove insignia spell effect */
+void Player::RemovedInsignia(Player* looterPlr)
+{
+ if (!GetBattleGroundId())
+ return;
+
+ // If not released spirit, do it !
+ if(m_deathTimer > 0)
+ {
+ m_deathTimer = 0;
+ BuildPlayerRepop();
+ RepopAtGraveyard();
+ }
+
+ Corpse *corpse = GetCorpse();
+ if (!corpse)
+ return;
+
+ // We have to convert player corpse to bones, not to be able to resurrect there
+ // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
+ Corpse *bones = ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
+ if (!bones)
+ return;
+
+ // Now we must make bones lootable, and send player loot
+ bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
+
+ // We store the level of our player in the gold field
+ // We retrieve this information at Player::SendLoot()
+ bones->loot.gold = getLevel();
+ bones->lootRecipient = looterPlr;
+ looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
+}
+
+/*Loot type MUST be
+1-corpse, go
+2-skinning
+3-Fishing
+*/
+
+void Player::SendLootRelease( uint64 guid )
+{
+ WorldPacket data( SMSG_LOOT_RELEASE_RESPONSE, (8+1) );
+ data << uint64(guid) << uint8(1);
+ SendDirectMessage( &data );
+}
+
+void Player::SendLoot(uint64 guid, LootType loot_type)
+{
+ Loot *loot = 0;
+ PermissionTypes permission = ALL_PERMISSION;
+
+ sLog.outDebug("Player::SendLoot");
+ if (IS_GAMEOBJECT_GUID(guid))
+ {
+ sLog.outDebug(" IS_GAMEOBJECT_GUID(guid)");
+ GameObject *go =
+ ObjectAccessor::GetGameObject(*this, guid);
+
+ // not check distance for GO in case owned GO (fishing bobber case, for example)
+ // And permit out of range GO with no owner in case fishing hole
+ if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this,INTERACTION_DISTANCE)))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &go->loot;
+
+ if(go->getLootState() == GO_READY)
+ {
+ uint32 lootid = go->GetLootId();
+
+ if(lootid)
+ {
+ sLog.outDebug(" if(lootid)");
+ loot->clear();
+ loot->FillLoot(lootid, LootTemplates_Gameobject, this);
+ }
+
+ if(loot_type == LOOT_FISHING)
+ go->getFishLoot(loot);
+
+ go->SetLootState(GO_ACTIVATED);
+ }
+ }
+ else if (IS_ITEM_GUID(guid))
+ {
+ Item *item = GetItemByGuid( guid );
+
+ if (!item)
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ if(loot_type == LOOT_DISENCHANTING)
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this);
+ }
+ }
+ else if(loot_type == LOOT_PROSPECTING)
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this);
+ }
+ }
+ else
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetEntry(), LootTemplates_Item, this);
+
+ loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot);
+ }
+ }
+ }
+ else if (IS_CORPSE_GUID(guid)) // remove insignia
+ {
+ Corpse *bones = ObjectAccessor::GetCorpse(*this, guid);
+
+ if (!bones || !((loot_type == LOOT_CORPSE) || (loot_type == LOOT_INSIGNIA)) || (bones->GetType() != CORPSE_BONES) )
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &bones->loot;
+
+ if (!bones->lootForBody)
+ {
+ bones->lootForBody = true;
+ uint32 pLevel = bones->loot.gold;
+ bones->loot.clear();
+ // It may need a better formula
+ // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
+ bones->loot.gold = (uint32)( urand(50, 150) * 0.016f * pow( ((float)pLevel)/5.76f, 2.5f) * sWorld.getRate(RATE_DROP_MONEY) );
+ }
+
+ if (bones->lootRecipient != this)
+ permission = NONE_PERMISSION;
+ }
+ else
+ {
+ Creature *creature = ObjectAccessor::GetCreature(*this, guid);
+
+ // must be in range and creature must be alive for pickpocket and must be dead for another loot
+ if (!creature || creature->isAlive()!=(loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this,INTERACTION_DISTANCE))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ if(loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &creature->loot;
+
+ if(loot_type == LOOT_PICKPOCKETING)
+ {
+ if ( !creature->lootForPickPocketed )
+ {
+ creature->lootForPickPocketed = true;
+ loot->clear();
+
+ if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
+ loot->FillLoot(lootid, LootTemplates_Pickpocketing, this);
+
+ // Generate extra money for pick pocket loot
+ const uint32 a = urand(0, creature->getLevel()/2);
+ const uint32 b = urand(0, getLevel()/2);
+ loot->gold = uint32(10 * (a + b) * sWorld.getRate(RATE_DROP_MONEY));
+ }
+ }
+ else
+ {
+ // the player whose group may loot the corpse
+ Player *recipient = creature->GetLootRecipient();
+ if (!recipient)
+ {
+ creature->SetLootRecipient(this);
+ recipient = this;
+ }
+
+ if (creature->lootForPickPocketed)
+ {
+ creature->lootForPickPocketed = false;
+ loot->clear();
+ }
+
+ if(!creature->lootForBody)
+ {
+ creature->lootForBody = true;
+ loot->clear();
+
+ if (uint32 lootid = creature->GetCreatureInfo()->lootid)
+ loot->FillLoot(lootid, LootTemplates_Creature, recipient);
+
+ loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
+
+ if(Group* group = recipient->GetGroup())
+ {
+ group->UpdateLooterGuid(creature,true);
+
+ switch (group->GetLootMethod())
+ {
+ case GROUP_LOOT:
+ // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
+ group->GroupLoot(recipient->GetGUID(), loot, creature);
+ break;
+ case NEED_BEFORE_GREED:
+ group->NeedBeforeGreed(recipient->GetGUID(), loot, creature);
+ break;
+ case MASTER_LOOT:
+ group->MasterLoot(recipient->GetGUID(), loot, creature);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // possible only if creature->lootForBody && loot->empty() at spell cast check
+ if (loot_type == LOOT_SKINNING)
+ {
+ loot->clear();
+ loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this);
+ }
+ // set group rights only for loot_type != LOOT_SKINNING
+ else
+ {
+ if(Group* group = GetGroup())
+ {
+ if( group == recipient->GetGroup() )
+ {
+ if(group->GetLootMethod() == FREE_FOR_ALL)
+ permission = ALL_PERMISSION;
+ else if(group->GetLooterGuid() == GetGUID())
+ {
+ if(group->GetLootMethod() == MASTER_LOOT)
+ permission = MASTER_PERMISSION;
+ else
+ permission = ALL_PERMISSION;
+ }
+ else
+ permission = GROUP_PERMISSION;
+ }
+ else
+ permission = NONE_PERMISSION;
+ }
+ else if(recipient == this)
+ permission = ALL_PERMISSION;
+ else
+ permission = NONE_PERMISSION;
+ }
+ }
+ }
+
+ SetLootGUID(guid);
+
+ QuestItemList *q_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerQuestItems = loot->GetPlayerQuestItems();
+ QuestItemMap::const_iterator itr = lootPlayerQuestItems.find(GetGUIDLow());
+ if (itr == lootPlayerQuestItems.end())
+ q_list = loot->FillQuestLoot(this);
+ else
+ q_list = itr->second;
+ }
+
+ QuestItemList *ffa_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerFFAItems = loot->GetPlayerFFAItems();
+ QuestItemMap::const_iterator itr = lootPlayerFFAItems.find(GetGUIDLow());
+ if (itr == lootPlayerFFAItems.end())
+ ffa_list = loot->FillFFALoot(this);
+ else
+ ffa_list = itr->second;
+ }
+
+ QuestItemList *conditional_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = loot->GetPlayerNonQuestNonFFAConditionalItems();
+ QuestItemMap::const_iterator itr = lootPlayerNonQuestNonFFAConditionalItems.find(GetGUIDLow());
+ if (itr == lootPlayerNonQuestNonFFAConditionalItems.end())
+ conditional_list = loot->FillNonQuestNonFFAConditionalLoot(this);
+ else
+ conditional_list = itr->second;
+ }
+
+ // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead
+ if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA)
+ loot_type = LOOT_SKINNING;
+
+ if(loot_type == LOOT_FISHINGHOLE)
+ loot_type = LOOT_FISHING;
+
+ WorldPacket data(SMSG_LOOT_RESPONSE, (9+50)); // we guess size
+
+ data << uint64(guid);
+ data << uint8(loot_type);
+ data << LootView(*loot, q_list, ffa_list, conditional_list, this, permission);
+
+ SendDirectMessage(&data);
+
+ // add 'this' player as one of the players that are looting 'loot'
+ if (permission != NONE_PERMISSION)
+ loot->AddLooter(GetGUID());
+
+ if ( loot_type == LOOT_CORPSE && !IS_ITEM_GUID(guid) )
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
+}
+
+void Player::SendNotifyLootMoneyRemoved()
+{
+ WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendNotifyLootItemRemoved(uint8 lootSlot)
+{
+ WorldPacket data(SMSG_LOOT_REMOVED, 1);
+ data << uint8(lootSlot);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
+{
+ WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
+ data << Field;
+ data << Value;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendInitWorldStates()
+{
+ // data depends on zoneid/mapid...
+ BattleGround* bg = GetBattleGround();
+ uint16 NumberOfFields = 0;
+ uint32 mapid = GetMapId();
+ uint32 zoneid = GetZoneId();
+ uint32 areaid = GetAreaId();
+ sLog.outDebug("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid);
+ // may be exist better way to do this...
+ switch(zoneid)
+ {
+ case 0:
+ case 1:
+ case 4:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 36:
+ case 38:
+ case 40:
+ case 41:
+ case 51:
+ case 267:
+ case 1519:
+ case 1537:
+ case 2257:
+ case 2918:
+ NumberOfFields = 6;
+ break;
+ case 2597:
+ NumberOfFields = 81;
+ break;
+ case 3277:
+ NumberOfFields = 14;
+ break;
+ case 3358:
+ case 3820:
+ NumberOfFields = 38;
+ break;
+ case 3483:
+ NumberOfFields = 22;
+ break;
+ case 3519:
+ NumberOfFields = 36;
+ break;
+ case 3521:
+ NumberOfFields = 35;
+ break;
+ case 3698:
+ case 3702:
+ case 3968:
+ NumberOfFields = 9;
+ break;
+ case 3703:
+ NumberOfFields = 9;
+ break;
+ default:
+ NumberOfFields = 10;
+ break;
+ }
+
+ WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(NumberOfFields*8)));
+ data << uint32(mapid); // mapid
+ data << uint32(zoneid); // zone id
+ data << uint32(areaid); // area id, new 2.1.0
+ data << uint16(NumberOfFields); // count of uint64 blocks
+ data << uint32(0x8d8) << uint32(0x0); // 1
+ data << uint32(0x8d7) << uint32(0x0); // 2
+ data << uint32(0x8d6) << uint32(0x0); // 3
+ data << uint32(0x8d5) << uint32(0x0); // 4
+ data << uint32(0x8d4) << uint32(0x0); // 5
+ data << uint32(0x8d3) << uint32(0x0); // 6
+ if(mapid == 530) // Outland
+ {
+ data << uint32(0x9bf) << uint32(0x0); // 7
+ data << uint32(0x9bd) << uint32(0xF); // 8
+ data << uint32(0x9bb) << uint32(0xF); // 9
+ }
+ switch(zoneid)
+ {
+ case 1:
+ case 11:
+ case 12:
+ case 38:
+ case 40:
+ case 51:
+ case 1519:
+ case 1537:
+ case 2257:
+ break;
+ case 2597: // AV
+ data << uint32(0x7ae) << uint32(0x1); // 7
+ data << uint32(0x532) << uint32(0x1); // 8
+ data << uint32(0x531) << uint32(0x0); // 9
+ data << uint32(0x52e) << uint32(0x0); // 10
+ data << uint32(0x571) << uint32(0x0); // 11
+ data << uint32(0x570) << uint32(0x0); // 12
+ data << uint32(0x567) << uint32(0x1); // 13
+ data << uint32(0x566) << uint32(0x1); // 14
+ data << uint32(0x550) << uint32(0x1); // 15
+ data << uint32(0x544) << uint32(0x0); // 16
+ data << uint32(0x536) << uint32(0x0); // 17
+ data << uint32(0x535) << uint32(0x1); // 18
+ data << uint32(0x518) << uint32(0x0); // 19
+ data << uint32(0x517) << uint32(0x0); // 20
+ data << uint32(0x574) << uint32(0x0); // 21
+ data << uint32(0x573) << uint32(0x0); // 22
+ data << uint32(0x572) << uint32(0x0); // 23
+ data << uint32(0x56f) << uint32(0x0); // 24
+ data << uint32(0x56e) << uint32(0x0); // 25
+ data << uint32(0x56d) << uint32(0x0); // 26
+ data << uint32(0x56c) << uint32(0x0); // 27
+ data << uint32(0x56b) << uint32(0x0); // 28
+ data << uint32(0x56a) << uint32(0x1); // 29
+ data << uint32(0x569) << uint32(0x1); // 30
+ data << uint32(0x568) << uint32(0x1); // 13
+ data << uint32(0x565) << uint32(0x0); // 32
+ data << uint32(0x564) << uint32(0x0); // 33
+ data << uint32(0x563) << uint32(0x0); // 34
+ data << uint32(0x562) << uint32(0x0); // 35
+ data << uint32(0x561) << uint32(0x0); // 36
+ data << uint32(0x560) << uint32(0x0); // 37
+ data << uint32(0x55f) << uint32(0x0); // 38
+ data << uint32(0x55e) << uint32(0x0); // 39
+ data << uint32(0x55d) << uint32(0x0); // 40
+ data << uint32(0x3c6) << uint32(0x4); // 41
+ data << uint32(0x3c4) << uint32(0x6); // 42
+ data << uint32(0x3c2) << uint32(0x4); // 43
+ data << uint32(0x516) << uint32(0x1); // 44
+ data << uint32(0x515) << uint32(0x0); // 45
+ data << uint32(0x3b6) << uint32(0x6); // 46
+ data << uint32(0x55c) << uint32(0x0); // 47
+ data << uint32(0x55b) << uint32(0x0); // 48
+ data << uint32(0x55a) << uint32(0x0); // 49
+ data << uint32(0x559) << uint32(0x0); // 50
+ data << uint32(0x558) << uint32(0x0); // 51
+ data << uint32(0x557) << uint32(0x0); // 52
+ data << uint32(0x556) << uint32(0x0); // 53
+ data << uint32(0x555) << uint32(0x0); // 54
+ data << uint32(0x554) << uint32(0x1); // 55
+ data << uint32(0x553) << uint32(0x1); // 56
+ data << uint32(0x552) << uint32(0x1); // 57
+ data << uint32(0x551) << uint32(0x1); // 58
+ data << uint32(0x54f) << uint32(0x0); // 59
+ data << uint32(0x54e) << uint32(0x0); // 60
+ data << uint32(0x54d) << uint32(0x1); // 61
+ data << uint32(0x54c) << uint32(0x0); // 62
+ data << uint32(0x54b) << uint32(0x0); // 63
+ data << uint32(0x545) << uint32(0x0); // 64
+ data << uint32(0x543) << uint32(0x1); // 65
+ data << uint32(0x542) << uint32(0x0); // 66
+ data << uint32(0x540) << uint32(0x0); // 67
+ data << uint32(0x53f) << uint32(0x0); // 68
+ data << uint32(0x53e) << uint32(0x0); // 69
+ data << uint32(0x53d) << uint32(0x0); // 70
+ data << uint32(0x53c) << uint32(0x0); // 71
+ data << uint32(0x53b) << uint32(0x0); // 72
+ data << uint32(0x53a) << uint32(0x1); // 73
+ data << uint32(0x539) << uint32(0x0); // 74
+ data << uint32(0x538) << uint32(0x0); // 75
+ data << uint32(0x537) << uint32(0x0); // 76
+ data << uint32(0x534) << uint32(0x0); // 77
+ data << uint32(0x533) << uint32(0x0); // 78
+ data << uint32(0x530) << uint32(0x0); // 79
+ data << uint32(0x52f) << uint32(0x0); // 80
+ data << uint32(0x52d) << uint32(0x1); // 81
+ break;
+ case 3277: // WS
+ if (bg && bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
+ data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
+ data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
+ data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
+ data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
+ data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
+ data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
+ data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
+ }
+ break;
+ case 3358: // AB
+ if (bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0x6e7) << uint32(0x0); // 7 1767 stables alliance
+ data << uint32(0x6e8) << uint32(0x0); // 8 1768 stables horde
+ data << uint32(0x6e9) << uint32(0x0); // 9 1769 unk, ST?
+ data << uint32(0x6ea) << uint32(0x0); // 10 1770 stables (show/hide)
+ data << uint32(0x6ec) << uint32(0x0); // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
+ data << uint32(0x6ed) << uint32(0x0); // 12 1773 farm (show/hide)
+ data << uint32(0x6ee) << uint32(0x0); // 13 1774 farm color
+ data << uint32(0x6ef) << uint32(0x0); // 14 1775 gold mine color, may be FM?
+ data << uint32(0x6f0) << uint32(0x0); // 15 1776 alliance resources
+ data << uint32(0x6f1) << uint32(0x0); // 16 1777 horde resources
+ data << uint32(0x6f2) << uint32(0x0); // 17 1778 horde bases
+ data << uint32(0x6f3) << uint32(0x0); // 18 1779 alliance bases
+ data << uint32(0x6f4) << uint32(0x7d0); // 19 1780 max resources (2000)
+ data << uint32(0x6f6) << uint32(0x0); // 20 1782 blacksmith color
+ data << uint32(0x6f7) << uint32(0x0); // 21 1783 blacksmith (show/hide)
+ data << uint32(0x6f8) << uint32(0x0); // 22 1784 unk, bs?
+ data << uint32(0x6f9) << uint32(0x0); // 23 1785 unk, bs?
+ data << uint32(0x6fb) << uint32(0x0); // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
+ data << uint32(0x6fc) << uint32(0x0); // 25 1788 gold mine (0 - conflict, 1 - horde)
+ data << uint32(0x6fd) << uint32(0x0); // 26 1789 gold mine (1 - show/0 - hide)
+ data << uint32(0x6fe) << uint32(0x0); // 27 1790 gold mine color
+ data << uint32(0x700) << uint32(0x0); // 28 1792 gold mine color, wtf?, may be LM?
+ data << uint32(0x701) << uint32(0x0); // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
+ data << uint32(0x702) << uint32(0x0); // 30 1794 lumber mill (show/hide)
+ data << uint32(0x703) << uint32(0x0); // 31 1795 lumber mill color color
+ data << uint32(0x732) << uint32(0x1); // 32 1842 stables (1 - uncontrolled)
+ data << uint32(0x733) << uint32(0x1); // 33 1843 gold mine (1 - uncontrolled)
+ data << uint32(0x734) << uint32(0x1); // 34 1844 lumber mill (1 - uncontrolled)
+ data << uint32(0x735) << uint32(0x1); // 35 1845 farm (1 - uncontrolled)
+ data << uint32(0x736) << uint32(0x1); // 36 1846 blacksmith (1 - uncontrolled)
+ data << uint32(0x745) << uint32(0x2); // 37 1861 unk
+ data << uint32(0x7a3) << uint32(0x708); // 38 1955 warning limit (1800)
+ }
+ break;
+ case 3820: // EY
+ if (bg && bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0xac1) << uint32(0x0); // 7 2753 Horde Bases
+ data << uint32(0xac0) << uint32(0x0); // 8 2752 Alliance Bases
+ data << uint32(0xab6) << uint32(0x0); // 9 2742 Mage Tower - Horde conflict
+ data << uint32(0xab5) << uint32(0x0); // 10 2741 Mage Tower - Alliance conflict
+ data << uint32(0xab4) << uint32(0x0); // 11 2740 Fel Reaver - Horde conflict
+ data << uint32(0xab3) << uint32(0x0); // 12 2739 Fel Reaver - Alliance conflict
+ data << uint32(0xab2) << uint32(0x0); // 13 2738 Draenei - Alliance conflict
+ data << uint32(0xab1) << uint32(0x0); // 14 2737 Draenei - Horde conflict
+ data << uint32(0xab0) << uint32(0x0); // 15 2736 unk // 0 at start
+ data << uint32(0xaaf) << uint32(0x0); // 16 2735 unk // 0 at start
+ data << uint32(0xaad) << uint32(0x0); // 17 2733 Draenei - Horde control
+ data << uint32(0xaac) << uint32(0x0); // 18 2732 Draenei - Alliance control
+ data << uint32(0xaab) << uint32(0x1); // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xaaa) << uint32(0x0); // 20 2730 Mage Tower - Alliance control
+ data << uint32(0xaa9) << uint32(0x0); // 21 2729 Mage Tower - Horde control
+ data << uint32(0xaa8) << uint32(0x1); // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xaa7) << uint32(0x0); // 23 2727 Fel Reaver - Horde control
+ data << uint32(0xaa6) << uint32(0x0); // 24 2726 Fel Reaver - Alliance control
+ data << uint32(0xaa5) << uint32(0x1); // 25 2725 Fel Reaver uncontroled (1 - yes, 0 - no)
+ data << uint32(0xaa4) << uint32(0x0); // 26 2724 Boold Elf - Horde control
+ data << uint32(0xaa3) << uint32(0x0); // 27 2723 Boold Elf - Alliance control
+ data << uint32(0xaa2) << uint32(0x1); // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xac5) << uint32(0x1); // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
+ data << uint32(0xad2) << uint32(0x1); // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
+ data << uint32(0xad1) << uint32(0x1); // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
+ data << uint32(0xabe) << uint32(0x0); // 32 2750 Horde resources
+ data << uint32(0xabd) << uint32(0x0); // 33 2749 Alliance resources
+ data << uint32(0xa05) << uint32(0x8e); // 34 2565 unk, constant?
+ data << uint32(0xaa0) << uint32(0x0); // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
+ data << uint32(0xa9f) << uint32(0x0); // 36 2719 Capturing progress-bar (0 - left, 100 - right)
+ data << uint32(0xa9e) << uint32(0x0); // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
+ data << uint32(0xc0d) << uint32(0x17b); // 38 3085 unk
+ // and some more ... unknown
+ }
+ break;
+ case 3483: // Hellfire Peninsula
+ data << uint32(0x9ba) << uint32(0x1); // 10
+ data << uint32(0x9b9) << uint32(0x1); // 11
+ data << uint32(0x9b5) << uint32(0x0); // 12
+ data << uint32(0x9b4) << uint32(0x1); // 13
+ data << uint32(0x9b3) << uint32(0x0); // 14
+ data << uint32(0x9b2) << uint32(0x0); // 15
+ data << uint32(0x9b1) << uint32(0x1); // 16
+ data << uint32(0x9b0) << uint32(0x0); // 17
+ data << uint32(0x9ae) << uint32(0x0); // 18 horde pvp objectives captured
+ data << uint32(0x9ac) << uint32(0x0); // 19
+ data << uint32(0x9a8) << uint32(0x0); // 20
+ data << uint32(0x9a7) << uint32(0x0); // 21
+ data << uint32(0x9a6) << uint32(0x1); // 22
+ break;
+ case 3519: // Terokkar Forest
+ data << uint32(0xa41) << uint32(0x0); // 10
+ data << uint32(0xa40) << uint32(0x14); // 11
+ data << uint32(0xa3f) << uint32(0x0); // 12
+ data << uint32(0xa3e) << uint32(0x0); // 13
+ data << uint32(0xa3d) << uint32(0x5); // 14
+ data << uint32(0xa3c) << uint32(0x0); // 15
+ data << uint32(0xa87) << uint32(0x0); // 16
+ data << uint32(0xa86) << uint32(0x0); // 17
+ data << uint32(0xa85) << uint32(0x0); // 18
+ data << uint32(0xa84) << uint32(0x0); // 19
+ data << uint32(0xa83) << uint32(0x0); // 20
+ data << uint32(0xa82) << uint32(0x0); // 21
+ data << uint32(0xa81) << uint32(0x0); // 22
+ data << uint32(0xa80) << uint32(0x0); // 23
+ data << uint32(0xa7e) << uint32(0x0); // 24
+ data << uint32(0xa7d) << uint32(0x0); // 25
+ data << uint32(0xa7c) << uint32(0x0); // 26
+ data << uint32(0xa7b) << uint32(0x0); // 27
+ data << uint32(0xa7a) << uint32(0x0); // 28
+ data << uint32(0xa79) << uint32(0x0); // 29
+ data << uint32(0x9d0) << uint32(0x5); // 30
+ data << uint32(0x9ce) << uint32(0x0); // 31
+ data << uint32(0x9cd) << uint32(0x0); // 32
+ data << uint32(0x9cc) << uint32(0x0); // 33
+ data << uint32(0xa88) << uint32(0x0); // 34
+ data << uint32(0xad0) << uint32(0x0); // 35
+ data << uint32(0xacf) << uint32(0x1); // 36
+ break;
+ case 3521: // Zangarmarsh
+ data << uint32(0x9e1) << uint32(0x0); // 10
+ data << uint32(0x9e0) << uint32(0x0); // 11
+ data << uint32(0x9df) << uint32(0x0); // 12
+ data << uint32(0xa5d) << uint32(0x1); // 13
+ data << uint32(0xa5c) << uint32(0x0); // 14
+ data << uint32(0xa5b) << uint32(0x1); // 15
+ data << uint32(0xa5a) << uint32(0x0); // 16
+ data << uint32(0xa59) << uint32(0x1); // 17
+ data << uint32(0xa58) << uint32(0x0); // 18
+ data << uint32(0xa57) << uint32(0x0); // 19
+ data << uint32(0xa56) << uint32(0x0); // 20
+ data << uint32(0xa55) << uint32(0x1); // 21
+ data << uint32(0xa54) << uint32(0x0); // 22
+ data << uint32(0x9e7) << uint32(0x0); // 23
+ data << uint32(0x9e6) << uint32(0x0); // 24
+ data << uint32(0x9e5) << uint32(0x0); // 25
+ data << uint32(0xa00) << uint32(0x0); // 26
+ data << uint32(0x9ff) << uint32(0x1); // 27
+ data << uint32(0x9fe) << uint32(0x0); // 28
+ data << uint32(0x9fd) << uint32(0x0); // 29
+ data << uint32(0x9fc) << uint32(0x1); // 30
+ data << uint32(0x9fb) << uint32(0x0); // 31
+ data << uint32(0xa62) << uint32(0x0); // 32
+ data << uint32(0xa61) << uint32(0x1); // 33
+ data << uint32(0xa60) << uint32(0x1); // 34
+ data << uint32(0xa5f) << uint32(0x0); // 35
+ break;
+ case 3698: // Nagrand Arena
+ data << uint32(0xa0f) << uint32(0x0); // 7
+ data << uint32(0xa10) << uint32(0x0); // 8
+ data << uint32(0xa11) << uint32(0x0); // 9
+ break;
+ case 3702: // Blade's Edge Arena
+ data << uint32(0x9f0) << uint32(0x0); // 7
+ data << uint32(0x9f1) << uint32(0x0); // 8
+ data << uint32(0x9f3) << uint32(0x0); // 9
+ break;
+ case 3968: // Ruins of Lordaeron
+ data << uint32(0xbb8) << uint32(0x0); // 7
+ data << uint32(0xbb9) << uint32(0x0); // 8
+ data << uint32(0xbba) << uint32(0x0); // 9
+ break;
+ case 3703: // Shattrath City
+ break;
+ default:
+ data << uint32(0x914) << uint32(0x0); // 7
+ data << uint32(0x913) << uint32(0x0); // 8
+ data << uint32(0x912) << uint32(0x0); // 9
+ data << uint32(0x915) << uint32(0x0); // 10
+ break;
+ }
+ GetSession()->SendPacket(&data);
+}
+
+uint32 Player::GetXPRestBonus(uint32 xp)
+{
+ uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus
+
+ if(rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
+ rested_bonus = xp;
+
+ SetRestBonus( GetRestBonus() - rested_bonus);
+
+ sLog.outDetail("Player gain %u xp (+ %u Rested Bonus). Rested points=%f",xp+rested_bonus,rested_bonus,GetRestBonus());
+ return rested_bonus;
+}
+
+void Player::SetBindPoint(uint64 guid)
+{
+ WorldPacket data(SMSG_BINDER_CONFIRM, 8);
+ data << uint64(guid);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendTalentWipeConfirm(uint64 guid)
+{
+ WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8+4));
+ data << uint64(guid);
+ data << uint32(resetTalentsCost());
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendPetSkillWipeConfirm()
+{
+ Pet* pet = GetPet();
+ if(!pet)
+ return;
+ WorldPacket data(SMSG_PET_UNLEARN_CONFIRM, (8+4));
+ data << pet->GetGUID();
+ data << uint32(pet->resetTalentsCost());
+ GetSession()->SendPacket( &data );
+}
+
+/*********************************************************/
+/*** STORAGE SYSTEM ***/
+/*********************************************************/
+
+void Player::SetVirtualItemSlot( uint8 i, Item* item)
+{
+ assert(i < 3);
+ if(i < 2 && item)
+ {
+ if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+ return;
+ uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
+ if(charges == 0)
+ return;
+ if(charges > 1)
+ item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT,charges-1);
+ else if(charges <= 1)
+ {
+ ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
+ }
+ }
+}
+
+void Player::SetSheath( uint32 sheathed )
+{
+ switch (sheathed)
+ {
+ case SHEATH_STATE_UNARMED: // no prepared weapon
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,NULL);
+ break;
+ case SHEATH_STATE_MELEE: // prepared melee weapon
+ {
+ SetVirtualItemSlot(0,GetWeaponForAttack(BASE_ATTACK,true));
+ SetVirtualItemSlot(1,GetWeaponForAttack(OFF_ATTACK,true));
+ SetVirtualItemSlot(2,NULL);
+ }; break;
+ case SHEATH_STATE_RANGED: // prepared ranged weapon
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,GetWeaponForAttack(RANGED_ATTACK,true));
+ break;
+ default:
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,NULL);
+ break;
+ }
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); // this must visualize Sheath changing for other players...
+}
+
+uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const
+{
+ uint8 pClass = getClass();
+
+ uint8 slots[4];
+ slots[0] = NULL_SLOT;
+ slots[1] = NULL_SLOT;
+ slots[2] = NULL_SLOT;
+ slots[3] = NULL_SLOT;
+ switch( proto->InventoryType )
+ {
+ case INVTYPE_HEAD:
+ slots[0] = EQUIPMENT_SLOT_HEAD;
+ break;
+ case INVTYPE_NECK:
+ slots[0] = EQUIPMENT_SLOT_NECK;
+ break;
+ case INVTYPE_SHOULDERS:
+ slots[0] = EQUIPMENT_SLOT_SHOULDERS;
+ break;
+ case INVTYPE_BODY:
+ slots[0] = EQUIPMENT_SLOT_BODY;
+ break;
+ case INVTYPE_CHEST:
+ slots[0] = EQUIPMENT_SLOT_CHEST;
+ break;
+ case INVTYPE_ROBE:
+ slots[0] = EQUIPMENT_SLOT_CHEST;
+ break;
+ case INVTYPE_WAIST:
+ slots[0] = EQUIPMENT_SLOT_WAIST;
+ break;
+ case INVTYPE_LEGS:
+ slots[0] = EQUIPMENT_SLOT_LEGS;
+ break;
+ case INVTYPE_FEET:
+ slots[0] = EQUIPMENT_SLOT_FEET;
+ break;
+ case INVTYPE_WRISTS:
+ slots[0] = EQUIPMENT_SLOT_WRISTS;
+ break;
+ case INVTYPE_HANDS:
+ slots[0] = EQUIPMENT_SLOT_HANDS;
+ break;
+ case INVTYPE_FINGER:
+ slots[0] = EQUIPMENT_SLOT_FINGER1;
+ slots[1] = EQUIPMENT_SLOT_FINGER2;
+ break;
+ case INVTYPE_TRINKET:
+ slots[0] = EQUIPMENT_SLOT_TRINKET1;
+ slots[1] = EQUIPMENT_SLOT_TRINKET2;
+ break;
+ case INVTYPE_CLOAK:
+ slots[0] = EQUIPMENT_SLOT_BACK;
+ break;
+ case INVTYPE_WEAPON:
+ {
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+
+ // suggest offhand slot only if know dual wielding
+ // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
+ if(CanDualWield())
+ slots[1] = EQUIPMENT_SLOT_OFFHAND;
+ };break;
+ case INVTYPE_SHIELD:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_RANGED:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_2HWEAPON:
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+ break;
+ case INVTYPE_TABARD:
+ slots[0] = EQUIPMENT_SLOT_TABARD;
+ break;
+ case INVTYPE_WEAPONMAINHAND:
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+ break;
+ case INVTYPE_WEAPONOFFHAND:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_HOLDABLE:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_THROWN:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_RANGEDRIGHT:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_BAG:
+ slots[0] = INVENTORY_SLOT_BAG_1;
+ slots[1] = INVENTORY_SLOT_BAG_2;
+ slots[2] = INVENTORY_SLOT_BAG_3;
+ slots[3] = INVENTORY_SLOT_BAG_4;
+ break;
+ case INVTYPE_RELIC:
+ {
+ switch(proto->SubClass)
+ {
+ case ITEM_SUBCLASS_ARMOR_LIBRAM:
+ if (pClass == CLASS_PALADIN)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_IDOL:
+ if (pClass == CLASS_DRUID)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_TOTEM:
+ if (pClass == CLASS_SHAMAN)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_MISC:
+ if (pClass == CLASS_WARLOCK)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ }
+ break;
+ }
+ default :
+ return NULL_SLOT;
+ }
+
+ if( slot != NULL_SLOT )
+ {
+ if( swap || !GetItemByPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] == slot )
+ return slot;
+ }
+ }
+ }
+ else
+ {
+ // search free slot at first
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) )
+ {
+ // in case 2hand equipped weapon offhand slot empty but not free
+ if(slots[i]==EQUIPMENT_SLOT_OFFHAND)
+ {
+ Item* mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+ if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
+ return slots[i];
+ }
+ else
+ return slots[i];
+ }
+ }
+
+ // if not found free and can swap return first appropriate from used
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] != NULL_SLOT && swap )
+ return slots[i];
+ }
+ }
+
+ // no free position
+ return NULL_SLOT;
+}
+
+uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const
+{
+ Item *pItem;
+ uint32 tempcount = 0;
+
+ uint8 res = EQUIP_ERR_OK;
+
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false);
+ if(ires==EQUIP_ERR_OK)
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ else
+ res = ires;
+ }
+ }
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ }
+ }
+
+ // not found req. item count and have unequippable items
+ return res;
+}
+
+uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) const
+{
+ uint32 count = 0;
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ count += pBag->GetItemCount(item,skipItem);
+ }
+
+ if(skipItem && skipItem->GetProto()->GemProperties)
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
+ count += pItem->GetGemCountWithID(item);
+ }
+ }
+
+ if(inBankAlso)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ count += pBag->GetItemCount(item,skipItem);
+ }
+
+ if(skipItem && skipItem->GetProto()->GemProperties)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
+ count += pItem->GetGemCountWithID(item);
+ }
+ }
+ }
+
+ return count;
+}
+
+Item* Player::GetItemByGuid( uint64 guid ) const
+{
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos( j );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ }
+ }
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos( j );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+Item* Player::GetItemByPos( uint16 pos ) const
+{
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+ return GetItemByPos( bag, slot );
+}
+
+Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
+ return m_items[slot];
+ else if(bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END
+ || bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if ( pBag )
+ return pBag->GetItemByPos(slot);
+ }
+ return NULL;
+}
+
+Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) const
+{
+ uint16 slot;
+ switch (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: return NULL;
+ }
+
+ Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+ if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
+ return NULL;
+
+ if(!useable)
+ return item;
+
+ if( item->IsBroken() || !IsUseEquipedWeapon(attackType==BASE_ATTACK) )
+ return NULL;
+
+ return item;
+}
+
+Item* Player::GetShield(bool useable) const
+{
+ Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR)
+ return NULL;
+
+ if(!useable)
+ return item;
+
+ if( item->IsBroken())
+ return NULL;
+
+ return item;
+}
+
+uint32 Player::GetAttackBySlot( uint8 slot )
+{
+ switch(slot)
+ {
+ case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK;
+ case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK;
+ case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK;
+ default: return MAX_ATTACK;
+ }
+}
+
+bool Player::HasBankBagSlot( uint8 slot ) const
+{
+ uint32 maxslot = GetByteValue(PLAYER_BYTES_2, 2) + BANK_SLOT_BAG_START;
+ if( slot < maxslot )
+ return true;
+ return false;
+}
+
+bool Player::IsInventoryPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END ) )
+ return true;
+ if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
+ return true;
+ return false;
+}
+
+bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot < EQUIPMENT_SLOT_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
+ return true;
+ return false;
+}
+
+bool Player::IsBankPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
+ return true;
+ if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
+ return true;
+ return false;
+}
+
+bool Player::IsBagPos( uint16 pos )
+{
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
+ return true;
+ return false;
+}
+
+bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
+{
+ uint32 tempcount = 0;
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ if(inBankAlso)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+Item* Player::GetItemOrItemWithGemEquipped( uint32 item ) const
+{
+ Item *pItem;
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ return pItem;
+ }
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(item);
+ if (pProto && pProto->GemProperties)
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto()->Socket[0].Color )
+ {
+ if (pItem->GetGemCountWithID(item) > 0 )
+ return pItem;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const
+{
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
+ if( !pProto )
+ {
+ if(no_space_count)
+ *no_space_count = count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ // no maximum
+ if(pProto->MaxCount == 0)
+ return EQUIP_ERR_OK;
+
+ uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
+
+ if( curcount + count > pProto->MaxCount )
+ {
+ if(no_space_count)
+ *no_space_count = count +curcount - pProto->MaxCount;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ return EQUIP_ERR_OK;
+}
+
+bool Player::HasItemTotemCategory( uint32 TotemCategory ) const
+{
+ Item *pItem;
+ for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ for(uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; ++j)
+ {
+ pItem = GetItemByPos( i, j );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const
+{
+ Item* pItem2 = GetItemByPos( bag, slot );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ uint32 need_space;
+
+ // empty specific slot - check item fit to slot
+ if( !pItem2 || swap )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ // keyring case
+ if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS))
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ // prevent cheating
+ if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END)
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+ }
+ else
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( !pBag )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ ItemPrototype const* pBagProto = pBag->GetProto();
+ if( !pBagProto )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ if( !ItemCanGoIntoBag(pProto,pBagProto) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+ }
+
+ // non empty stack with space
+ need_space = pProto->Stackable;
+ }
+ // non empty slot, check item type
+ else
+ {
+ // check item type
+ if(pItem2->GetEntry() != pProto->ItemId)
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ // check free space
+ if(pItem2->GetCount() >= pProto->Stackable)
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ need_space = pProto->Stackable - pItem2->GetCount();
+ }
+
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+ }
+ return EQUIP_ERR_OK;
+}
+
+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)
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( !pBag )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ ItemPrototype const* pBagProto = pBag->GetProto();
+ 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) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ if( !ItemCanGoIntoBag(pProto,pBagProto) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
+ if(j==skip_slot)
+ continue;
+
+ Item* pItem2 = GetItemByPos( bag, j );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
+ {
+ uint32 need_space = pProto->Stackable - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ else
+ {
+ uint32 need_space = pProto->Stackable;
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
+{
+ 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)
+ continue;
+
+ Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
+ {
+ uint32 need_space = pProto->Stackable - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+ ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ else
+ {
+ uint32 need_space = pProto->Stackable;
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const
+{
+ 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(no_space_count)
+ *no_space_count = count;
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
+ }
+
+ if(pItem && pItem->IsBindedNotWith(GetGUID()))
+ {
+ if(no_space_count)
+ *no_space_count = count;
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+ }
+
+ // 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(count==no_similar_count)
+ {
+ if(no_space_count)
+ *no_space_count = no_similar_count;
+ return res;
+ }
+ count -= no_similar_count;
+ }
+
+ // in specific slot
+ if( bag != NULL_BAG && slot != NULL_SLOT )
+ {
+ res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // in specific bag
+ if( bag != NULL_BAG )
+ {
+ // search stack in bag for merge to
+ if( pProto->Stackable > 1 )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
+ {
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ else // equipped bag
+ {
+ // we need check 2 time (specilized/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)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // search free slot in bag for place to
+ if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
+ {
+ // search free slot - keyring case
+ 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(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ else // equipped bag
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // not specific bag or have space for partly store only in specific bag
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ if( pProto->BagFamily )
+ {
+ for(int 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)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // search free slot - special bag case
+ if( pProto->BagFamily )
+ {
+ 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(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // 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(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+
+ return EQUIP_ERR_INVENTORY_FULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint8 Player::CanStoreItems( Item **pItems,int count) const
+{
+ Item *pItem2;
+
+ // fill space table
+ int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START];
+ int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
+ int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START];
+
+ memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START));
+ memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE);
+ memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START));
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_slot_items[i-INVENTORY_SLOT_ITEM_START] = pItem2->GetCount();
+ }
+ }
+
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_keys[i-KEYRING_SLOT_START] = pItem2->GetCount();
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem2 = GetItemByPos( i, j );
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_bags[i-INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount();
+ }
+ }
+ }
+ }
+ }
+
+ // check free space for all items
+ for (int k=0;k<count;k++)
+ {
+ Item *pItem = pItems[k];
+
+ // no item
+ if (!pItem) continue;
+
+ sLog.outDebug( "STORAGE: CanStoreItems %i. item = %u, count = %u", k+1, pItem->GetEntry(), pItem->GetCount());
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // strange item
+ if( !pProto )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+
+ // item it 'bind'
+ if(pItem->IsBindedNotWith(GetGUID()))
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+
+ // item is 'one item only'
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ bool b_found = false;
+
+ for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; t++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem2 = GetItemByPos( t, j );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (b_found) continue;
+ }
+
+ // special bag case
+ if( pProto->BagFamily )
+ {
+ bool b_found = false;
+ if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
+ {
+ uint32 keyringSize = GetMaxKeyringSize();
+ for(uint32 t = KEYRING_SLOT_START; t < KEYRING_SLOT_START+keyringSize; ++t)
+ {
+ if( inv_keys[t-KEYRING_SLOT_START] == 0 )
+ {
+ inv_keys[t-KEYRING_SLOT_START] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+
+ // not plain container check
+ if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) &&
+ ItemCanGoIntoBag(pProto,pBagProto) )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (b_found) continue;
+ }
+
+ // search free slot
+ bool b_found = false;
+ for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
+ {
+ if( inv_slot_items[t-INVENTORY_SLOT_ITEM_START] == 0 )
+ {
+ inv_slot_items[t-INVENTORY_SLOT_ITEM_START] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ // search free slot in bags
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto && ItemCanGoIntoBag(pProto,pBagProto))
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // no free slot found?
+ if (!b_found)
+ return EQUIP_ERR_INVENTORY_FULL;
+ }
+
+ return EQUIP_ERR_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const
+{
+ dest = 0;
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ uint8 result = CanEquipItem(slot, dest, pItem, swap );
+ delete pItem;
+ return result;
+ }
+
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const
+{
+ dest = 0;
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount());
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if(pItem->IsBindedNotWith(GetGUID()))
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+
+ // check count of items (skip for auto move for same player from bank)
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // do not allow equipping gear except weapons, offhands, projectiles, relics in
+ // - combat
+ // - in-progress arenas
+ if( !pProto->CanChangeEquipStateInCombat() )
+ {
+ if( isInCombat() )
+ return EQUIP_ERR_NOT_IN_COMBAT;
+
+ if(BattleGround* bg = GetBattleGround())
+ if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
+ return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
+ }
+
+ if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0)
+ return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err
+
+ uint8 eslot = FindEquipSlot( pProto, slot, swap );
+ if( eslot == NULL_SLOT )
+ return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
+
+ uint8 msg = CanUseItem( pItem , not_loading );
+ if( msg != EQUIP_ERR_OK )
+ return msg;
+ if( !swap && GetItemByPos( INVENTORY_SLOT_BAG_0, eslot ) )
+ return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE;
+
+ // check unique-equipped on item
+ if (pProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)
+ {
+ // there is an equip limit on this item
+ Item* tItem = GetItemOrItemWithGemEquipped(pProto->ItemId);
+ if (tItem && (!swap || tItem->GetSlot() != eslot ) )
+ return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
+ }
+
+ // check unique-equipped on gems
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID);
+ if(pGem && (pGem->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
+ {
+ Item* tItem = GetItemOrItemWithGemEquipped(enchantEntry->GemID);
+ if(tItem && (!swap || tItem->GetSlot() != eslot ))
+ return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
+ }
+ }
+
+ // check unique-equipped special item classes
+ if (pProto->Class == ITEM_CLASS_QUIVER)
+ {
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ if( Item* pBag = GetItemByPos( INVENTORY_SLOT_BAG_0, i ) )
+ {
+ if( ItemPrototype const* pBagProto = pBag->GetProto() )
+ {
+ if( pBagProto->Class==pProto->Class && pBagProto->SubClass==pProto->SubClass &&
+ (!swap || pBag->GetSlot() != eslot ) )
+ {
+ if(pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH)
+ return EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH;
+ else
+ return EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER;
+ }
+ }
+ }
+ }
+ }
+
+ uint32 type = pProto->InventoryType;
+
+ if(eslot == EQUIPMENT_SLOT_OFFHAND)
+ {
+ if( type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND )
+ {
+ if(!CanDualWield())
+ return EQUIP_ERR_CANT_DUAL_WIELD;
+ }
+
+ Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+ if(mainItem)
+ {
+ if(mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
+ }
+ }
+
+ // equip two-hand weapon case (with possible unequip 2 items)
+ if( type == INVTYPE_2HWEAPON )
+ {
+ if(eslot != EQUIPMENT_SLOT_MAINHAND)
+ return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
+
+ // offhand item must can be stored in inventitory for offhand item and it also must be unequipped
+ Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
+ ItemPosCountVec off_dest;
+ if( offItem && (!not_loading ||
+ CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK ||
+ CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL;
+ }
+ dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
+ return EQUIP_ERR_OK;
+ }
+ }
+ if( !swap )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+ else
+ return EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
+}
+
+uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
+{
+ // Applied only to equipped items and bank bags
+ if(!IsEquipmentPos(pos) && !IsBagPos(pos))
+ return EQUIP_ERR_OK;
+
+ Item* pItem = GetItemByPos(pos);
+
+ // Applied only to existed equipped item
+ if( !pItem )
+ return EQUIP_ERR_OK;
+
+ sLog.outDebug( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
+
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( !pProto )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+
+ // do not allow unequipping gear except weapons, offhands, projectiles, relics in
+ // - combat
+ // - in-progress arenas
+ if( !pProto->CanChangeEquipStateInCombat() )
+ {
+ if( isInCombat() )
+ return EQUIP_ERR_NOT_IN_COMBAT;
+
+ if(BattleGround* bg = GetBattleGround())
+ if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
+ return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
+ }
+
+ if(!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
+ return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;
+
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const
+{
+ if( !pItem )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
+
+ uint32 count = pItem->GetCount();
+
+ sLog.outDebug( "STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( !pProto )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
+
+ if( pItem->IsBindedNotWith(GetGUID()) )
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+
+ // check count of items (skip for auto move for same player from bank)
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // in specific slot
+ if( bag != NULL_BAG && slot != NULL_SLOT )
+ {
+ if( pProto->InventoryType == INVTYPE_BAG )
+ {
+ Bag *pBag = (Bag*)pItem;
+ if( pBag )
+ {
+ if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
+ {
+ if( !HasBankBagSlot( slot ) )
+ return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;
+ if( uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK )
+ return cantuse;
+ }
+ else
+ {
+ if( !pBag->IsEmpty() )
+ return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
+ }
+ }
+ }
+ else
+ {
+ if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
+ return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;
+ }
+
+ res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // in specific bag
+ if( bag != NULL_BAG )
+ {
+ if( pProto->InventoryType == INVTYPE_BAG )
+ {
+ Bag *pBag = (Bag*)pItem;
+ if( pBag && !pBag->IsEmpty() )
+ return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
+ }
+
+ // search stack in bag for merge to
+ if( pProto->Stackable > 1 )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ else
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free slot in bag
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ else
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // not specific bag or have spece for partly store only in specific bag
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ // in slots
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ // in special bags
+ if( pProto->BagFamily )
+ {
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free place in special bag
+ if( pProto->BagFamily )
+ {
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free space
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ return EQUIP_ERR_BANK_FULL;
+}
+
+uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const
+{
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: CanUseItem item = %u", pItem->GetEntry());
+ if( !isAlive() && not_loading )
+ return EQUIP_ERR_YOU_ARE_DEAD;
+ //if( isStunned() )
+ // return EQUIP_ERR_YOU_ARE_STUNNED;
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if( pItem->IsBindedNotWith(GetGUID()) )
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ if( pItem->GetSkill() != 0 )
+ {
+ if( GetSkillValue( pItem->GetSkill() ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ }
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ if( pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank )
+ return EQUIP_ERR_CANT_EQUIP_REPUTATION;
+ if( getLevel() < pProto->RequiredLevel )
+ return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
+ return EQUIP_ERR_OK;
+ }
+ }
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+bool Player::CanUseItem( ItemPrototype const *pProto )
+{
+ // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
+
+ if( pProto )
+ {
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return false;
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return false;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return false;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return false;
+ if( getLevel() < pProto->RequiredLevel )
+ return false;
+ return true;
+ }
+ return false;
+}
+
+uint8 Player::CanUseAmmo( uint32 item ) const
+{
+ sLog.outDebug( "STORAGE: CanUseAmmo item = %u", item);
+ if( !isAlive() )
+ return EQUIP_ERR_YOU_ARE_DEAD;
+ //if( isStunned() )
+ // return EQUIP_ERR_YOU_ARE_STUNNED;
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ if( pProto->InventoryType!= INVTYPE_AMMO )
+ return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE;
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ /*if( GetReputation() < pProto->RequiredReputation )
+ return EQUIP_ERR_CANT_EQUIP_REPUTATION;
+ */
+ if( getLevel() < pProto->RequiredLevel )
+ return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
+
+ // Requires No Ammo
+ if(GetDummyAura(46699))
+ return EQUIP_ERR_BAG_FULL6;
+
+ return EQUIP_ERR_OK;
+ }
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+void Player::SetAmmo( uint32 item )
+{
+ if(!item)
+ return;
+
+ // already set
+ if( GetUInt32Value(PLAYER_AMMO_ID) == item )
+ return;
+
+ // check ammo
+ if(item)
+ {
+ uint8 msg = CanUseAmmo( item );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return;
+ }
+ }
+
+ SetUInt32Value(PLAYER_AMMO_ID, item);
+
+ _ApplyAmmoBonuses();
+}
+
+void Player::RemoveAmmo()
+{
+ SetUInt32Value(PLAYER_AMMO_ID, 0);
+
+ m_ammoDPS = 0.0f;
+
+ if(CanModifyStats())
+ UpdateDamagePhysical(RANGED_ATTACK);
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Player::StoreNewItem( ItemPosCountVec const& dest, uint32 item, bool update,int32 randomPropertyId )
+{
+ uint32 count = 0;
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
+ count += itr->count;
+
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ ItemAddedQuestCheck( item, count );
+ if(randomPropertyId)
+ pItem->SetItemRandomProperties(randomPropertyId);
+ pItem = StoreItem( dest, pItem, update );
+ }
+ return pItem;
+}
+
+Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update )
+{
+ if( !pItem )
+ return NULL;
+
+ Item* lastItem = pItem;
+
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
+ {
+ uint16 pos = itr->pos;
+ uint32 count = itr->count;
+
+ ++itr;
+
+ if(itr == dest.end())
+ {
+ lastItem = _StoreItem(pos,pItem,count,false,update);
+ break;
+ }
+
+ lastItem = _StoreItem(pos,pItem,count,true,update);
+ }
+
+ return lastItem;
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update )
+{
+ if( !pItem )
+ return NULL;
+
+ 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);
+
+ Item *pItem2 = GetItemByPos( bag, slot );
+
+ if( !pItem2 )
+ {
+ if(clone)
+ pItem = pItem->CloneItem(count,this);
+ else
+ pItem->SetCount(count);
+
+ if(!pItem)
+ return NULL;
+
+ if( pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
+ pItem->GetProto()->Bonding == BIND_QUEST_ITEM ||
+ pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
+ pItem->SetBinding( true );
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ m_items[slot] = pItem;
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
+
+ pItem->SetSlot( slot );
+ pItem->SetContainer( NULL );
+
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+
+ pItem->SetState(ITEM_CHANGED, this);
+ }
+ else
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( pBag )
+ {
+ pBag->StoreItem( slot, pItem, update );
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+ pItem->SetState(ITEM_CHANGED, this);
+ pBag->SetState(ITEM_CHANGED, this);
+ }
+ }
+
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ return pItem;
+ }
+ else
+ {
+ if( pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
+ pItem2->GetProto()->Bonding == BIND_QUEST_ITEM ||
+ pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
+ pItem2->SetBinding( true );
+
+ pItem2->SetCount( pItem2->GetCount() + count );
+ if( IsInWorld() && update )
+ pItem2->SendUpdateToPlayer( this );
+
+ if(!clone)
+ {
+ // delete item (it not in any slot currently)
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer( this );
+ }
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
+ pItem->SetState(ITEM_REMOVED, this);
+ }
+ // AddItemDurations(pItem2); - pItem2 already have duration listed for player
+ AddEnchantmentDurations(pItem2);
+
+ pItem2->SetState(ITEM_CHANGED, this);
+
+ return pItem2;
+ }
+}
+
+Item* Player::EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update )
+{
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ ItemAddedQuestCheck( item, count );
+ Item * retItem = EquipItem( pos, pItem, update );
+
+ return retItem;
+ }
+ return NULL;
+}
+
+Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
+{
+ if( pItem )
+ {
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+
+ Item *pItem2 = GetItemByPos( bag, slot );
+
+ if( !pItem2 )
+ {
+ VisualizeItem( slot, pItem);
+
+ if(isAlive())
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+ if(pProto && pProto->ItemSet)
+ AddItemsSetItem(this,pItem);
+
+ _ApplyItemMods(pItem, slot, true);
+
+ if(pProto && isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer == 0)
+ {
+ m_weaponChangeTimer = DEFAULT_SWITCH_WEAPON;
+ if (getClass() == CLASS_ROGUE)
+ m_weaponChangeTimer = ROGUE_SWITCH_WEAPON;
+ }
+ }
+
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+
+ ApplyEquipCooldown(pItem);
+
+ if( slot == EQUIPMENT_SLOT_MAINHAND )
+ UpdateExpertise(BASE_ATTACK);
+ else if( slot == EQUIPMENT_SLOT_OFFHAND )
+ UpdateExpertise(OFF_ATTACK);
+ }
+ else
+ {
+ pItem2->SetCount( pItem2->GetCount() + pItem->GetCount() );
+ if( IsInWorld() && update )
+ pItem2->SendUpdateToPlayer( this );
+
+ // delete item (it not in any slot currently)
+ //pItem->DeleteFromDB();
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer( this );
+ }
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
+ pItem->SetState(ITEM_REMOVED, this);
+ pItem2->SetState(ITEM_CHANGED, this);
+
+ ApplyEquipCooldown(pItem2);
+
+ return pItem2;
+ }
+ }
+
+ return pItem;
+}
+
+void Player::QuickEquipItem( uint16 pos, Item *pItem)
+{
+ if( pItem )
+ {
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ uint8 slot = pos & 255;
+ VisualizeItem( slot, pItem);
+
+ if( IsInWorld() )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+ }
+}
+
+void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
+{
+ // PLAYER_VISIBLE_ITEM_i_CREATOR // Size: 2
+ // PLAYER_VISIBLE_ITEM_i_0 // Size: 12
+ // entry // Size: 1
+ // inspected enchantments // Size: 6
+ // ? // Size: 5
+ // PLAYER_VISIBLE_ITEM_i_PROPERTIES // Size: 1 (property,suffix factor)
+ // PLAYER_VISIBLE_ITEM_i_PAD // Size: 1
+ // // = 16
+
+ if(pItem)
+ {
+ SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetUInt64Value(ITEM_FIELD_CREATOR));
+
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 0, pItem->GetEntry());
+
+ for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
+ SetUInt32Value(VisibleBase + 1 + i, pItem->GetEnchantmentId(EnchantmentSlot(i)));
+
+ // Use SetInt16Value to prevent set high part to FFFF for negative value
+ SetInt16Value( PLAYER_VISIBLE_ITEM_1_PROPERTIES + (slot * MAX_VISIBLE_ITEM_OFFSET), 0, pItem->GetItemRandomPropertyId());
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetItemSuffixFactor());
+ }
+ else
+ {
+ SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 0, 0);
+
+ for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
+ SetUInt32Value(VisibleBase + 1 + i, 0);
+
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+ }
+}
+
+void Player::VisualizeItem( uint8 slot, Item *pItem)
+{
+ if(!pItem)
+ return;
+
+ // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
+ if( pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
+ pItem->SetBinding( true );
+
+ sLog.outDebug( "STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());
+
+ m_items[slot] = pItem;
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
+ pItem->SetSlot( slot );
+ pItem->SetContainer( NULL );
+
+ if( slot < EQUIPMENT_SLOT_END )
+ SetVisibleItemSlot(slot,pItem);
+
+ pItem->SetState(ITEM_CHANGED, this);
+}
+
+void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
+{
+ // note: removeitem does not actually change the item
+ // it only takes the item out of storage temporarily
+ // note2: if removeitem is to be used for delinking
+ // the item must be removed from the player's updatequeue
+
+ Item *pItem = GetItemByPos( bag, slot );
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ if ( slot < INVENTORY_SLOT_BAG_END )
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+
+ if(pProto && pProto->ItemSet)
+ RemoveItemsSetItem(this,pProto);
+
+ _ApplyItemMods(pItem, slot, false);
+
+ // remove item dependent auras and casts (only weapon and armor slots)
+ if(slot < EQUIPMENT_SLOT_END)
+ RemoveItemDependentAurasAndCasts(pItem);
+
+ // remove held enchantments
+ if ( slot == EQUIPMENT_SLOT_MAINHAND )
+ {
+ if (pItem->GetItemSuffixFactor())
+ {
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
+ }
+ else
+ {
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
+ }
+ }
+ }
+
+ m_items[slot] = NULL;
+ SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
+
+ if ( slot < EQUIPMENT_SLOT_END )
+ SetVisibleItemSlot(slot,NULL);
+ }
+ else
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( pBag )
+ pBag->RemoveItem(slot, update);
+ }
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
+ // pItem->SetUInt64Value( ITEM_FIELD_OWNER, 0 ); not clear owner at remove (it will be set at store). This used in mail and auction code
+ pItem->SetSlot( NULL_SLOT );
+ if( IsInWorld() && update )
+ pItem->SendUpdateToPlayer( this );
+
+ if( slot == EQUIPMENT_SLOT_MAINHAND )
+ UpdateExpertise(BASE_ATTACK);
+ else if( slot == EQUIPMENT_SLOT_OFFHAND )
+ UpdateExpertise(OFF_ATTACK);
+ }
+}
+
+// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
+void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
+{
+ if(Item* it = GetItemByPos(bag,slot))
+ {
+ ItemRemovedQuestCheck(it->GetEntry(),it->GetCount());
+ RemoveItem( bag,slot,update);
+ it->RemoveFromUpdateQueueOf(this);
+ if(it->IsInWorld())
+ {
+ it->RemoveFromWorld();
+ it->DestroyForPlayer( this );
+ }
+ }
+}
+
+// Common operation need to add item from inventory without delete in trade, guild bank, mail....
+void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
+{
+ // update quest counters
+ ItemAddedQuestCheck(pItem->GetEntry(),pItem->GetCount());
+
+ // store item
+ Item* pLastItem = StoreItem( dest, pItem, update);
+
+ // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
+ if(pLastItem==pItem)
+ {
+ // update owner for last item (this can be original item with wrong owner
+ if(pLastItem->GetOwnerGUID() != GetGUID())
+ pLastItem->SetOwnerGUID(GetGUID());
+
+ // if this original item then it need create record in inventory
+ // in case trade we laready have item in other player inventory
+ pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
+ }
+}
+
+void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
+{
+ Item *pItem = GetItemByPos( bag, slot );
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
+
+ // start from destroy contained items (only equipped bag can have its)
+ if (pItem->IsBag() && pItem->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot
+ {
+ for (int i = 0; i < MAX_BAG_SIZE; i++)
+ DestroyItem(slot,i,update);
+ }
+
+ if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
+
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() );
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+
+ SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
+
+ // equipment and equipped bags can have applied bonuses
+ if ( slot < INVENTORY_SLOT_BAG_END )
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+ if(pProto && pProto->ItemSet)
+ RemoveItemsSetItem(this,pProto);
+
+ _ApplyItemMods(pItem, slot, false);
+ }
+
+ if ( slot < EQUIPMENT_SLOT_END )
+ {
+ // remove item dependent auras and casts (only weapon and armor slots)
+ RemoveItemDependentAurasAndCasts(pItem);
+
+ // equipment visual show
+ SetVisibleItemSlot(slot,NULL);
+ }
+
+ m_items[slot] = NULL;
+ }
+ else if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
+ pBag->RemoveItem(slot, update);
+
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer(this);
+ }
+
+ //pItem->SetOwnerGUID(0);
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
+ pItem->SetSlot( NULL_SLOT );
+ pItem->SetState(ITEM_REMOVED, this);
+ }
+}
+
+void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check)
+{
+ sLog.outDebug( "STORAGE: DestroyItemCount item = %u, count = %u", item, count);
+ Item *pItem;
+ ItemPrototype const *pProto;
+ uint32 remcount = 0;
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ // all items in inventory can unequipped
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ // all keys can be unequipped
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+
+ // in inventory bags
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetEntry() == item )
+ {
+ // all items in bags can be unequipped
+ if( pItem->GetCount() + remcount <= count )
+ {
+ remcount += pItem->GetCount();
+ DestroyItem( i, j, update );
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() && update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ if(!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK )
+ {
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+}
+
+void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
+{
+ sLog.outDebug( "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone );
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( i, j, update);
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+}
+
+void Player::DestroyConjuredItems( bool update )
+{
+ // used when entering arena
+ // distroys all conjured items
+ sLog.outDebug( "STORAGE: DestroyConjuredItems" );
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( i, j, update);
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+}
+
+void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update )
+{
+ if(!pItem)
+ return;
+
+ sLog.outDebug( "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(),pItem->GetEntry(), count);
+
+ if( pItem->GetCount() <= count )
+ {
+ count-= pItem->GetCount();
+
+ DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), update);
+ }
+ else
+ {
+ ItemRemovedQuestCheck( pItem->GetEntry(), count);
+ pItem->SetCount( pItem->GetCount() - count );
+ count = 0;
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ }
+}
+
+void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
+{
+ uint8 srcbag = src >> 8;
+ uint8 srcslot = src & 255;
+
+ uint8 dstbag = dst >> 8;
+ uint8 dstslot = dst & 255;
+
+ Item *pSrcItem = GetItemByPos( srcbag, srcslot );
+ if( !pSrcItem )
+ {
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
+ return;
+ }
+
+ // not let split all items (can be only at cheating)
+ if(pSrcItem->GetCount() == count)
+ {
+ SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
+ return;
+ }
+
+ // not let split more existed items (can be only at cheating)
+ if(pSrcItem->GetCount() < count)
+ {
+ SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL );
+ return;
+ }
+
+ if(pSrcItem->m_lootGenerated) // prevent split looting item (item
+ {
+ //best error message found for attempting to split while looting
+ SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
+ return;
+ }
+
+ sLog.outDebug( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
+ Item *pNewItem = pSrcItem->CloneItem( count, this );
+ if( !pNewItem )
+ {
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInventoryPos( dst ) )
+ {
+ // change item amount before check (for unique max count check)
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ StoreItem( dest, pNewItem, true);
+ }
+ else if( IsBankPos ( dst ) )
+ {
+ // change item amount before check (for unique max count check)
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ ItemPosCountVec dest;
+ uint8 msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ BankItem( dest, pNewItem, true);
+ }
+ else if( IsEquipmentPos ( dst ) )
+ {
+ // change item amount before check (for unique max count check), provide space for splitted items
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ uint16 dest;
+ uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ EquipItem( dest, pNewItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+}
+
+void Player::SwapItem( uint16 src, uint16 dst )
+{
+ uint8 srcbag = src >> 8;
+ uint8 srcslot = src & 255;
+
+ uint8 dstbag = dst >> 8;
+ uint8 dstslot = dst & 255;
+
+ Item *pSrcItem = GetItemByPos( srcbag, srcslot );
+ Item *pDstItem = GetItemByPos( dstbag, dstslot );
+
+ if( !pSrcItem )
+ return;
+
+ sLog.outDebug( "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
+
+ if(!isAlive() )
+ {
+ SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem );
+ return;
+ }
+
+ if(pSrcItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
+ return;
+ }
+
+ // check unequip potability for equipped items and bank bags
+ if(IsEquipmentPos ( src ) || IsBagPos ( src ))
+ {
+ // bags can be swapped with empty bag slots
+ uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ));
+ if(msg != EQUIP_ERR_OK)
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+ }
+
+ // prevent put equipped/bank bag in self
+ if( IsBagPos ( src ) && srcslot == dstbag)
+ {
+ SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
+ return;
+ }
+
+ if( !pDstItem )
+ {
+ if( IsInventoryPos( dst ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ StoreItem( dest, pSrcItem, true);
+ }
+ else if( IsBankPos ( dst ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false);
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ BankItem( dest, pSrcItem, true);
+ }
+ else if( IsEquipmentPos ( dst ) )
+ {
+ uint16 dest;
+ uint8 msg = CanEquipItem( dstslot, dest, pSrcItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ EquipItem( dest, pSrcItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else // if (!pDstItem)
+ {
+ if(pDstItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL );
+ return;
+ }
+
+ // check unequip potability for equipped items and bank bags
+ if(IsEquipmentPos ( dst ) || IsBagPos ( dst ))
+ {
+ // bags can be swapped with empty bag slots
+ uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) );
+ if(msg != EQUIP_ERR_OK)
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+ }
+
+ // attempt merge to / fill target item
+ {
+ uint8 msg;
+ ItemPosCountVec sDest;
+ uint16 eDest;
+ if( IsInventoryPos( dst ) )
+ msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false );
+ else if( IsBankPos ( dst ) )
+ msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false );
+ else if( IsEquipmentPos ( dst ) )
+ msg = CanEquipItem( dstslot, eDest, pSrcItem, false );
+ else
+ return;
+
+ // can be merge/fill
+ if(msg == EQUIP_ERR_OK)
+ {
+ if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->Stackable )
+ {
+ RemoveItem(srcbag, srcslot, true);
+
+ if( IsInventoryPos( dst ) )
+ StoreItem( sDest, pSrcItem, true);
+ else if( IsBankPos ( dst ) )
+ BankItem( sDest, pSrcItem, true);
+ else if( IsEquipmentPos ( dst ) )
+ {
+ EquipItem( eDest, pSrcItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else
+ {
+ pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->Stackable );
+ pDstItem->SetCount( pSrcItem->GetProto()->Stackable );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ pDstItem->SetState(ITEM_CHANGED, this);
+ if( IsInWorld() )
+ {
+ pSrcItem->SendUpdateToPlayer( this );
+ pDstItem->SendUpdateToPlayer( this );
+ }
+ }
+ return;
+ }
+ }
+
+ // impossible merge/fill, do real swap
+ uint8 msg;
+
+ // check src->dest move possibility
+ ItemPosCountVec sDest;
+ uint16 eDest;
+ if( IsInventoryPos( dst ) )
+ msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true );
+ else if( IsBankPos( dst ) )
+ msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true );
+ else if( IsEquipmentPos( dst ) )
+ {
+ msg = CanEquipItem( dstslot, eDest, pSrcItem, true );
+ if( msg == EQUIP_ERR_OK )
+ msg = CanUnequipItem( eDest, true );
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+
+ // check dest->src move possibility
+ ItemPosCountVec sDest2;
+ uint16 eDest2;
+ if( IsInventoryPos( src ) )
+ msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true );
+ else if( IsBankPos( src ) )
+ msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true );
+ else if( IsEquipmentPos( src ) )
+ {
+ msg = CanEquipItem( srcslot, eDest2, pDstItem, true);
+ if( msg == EQUIP_ERR_OK )
+ msg = CanUnequipItem( eDest2, true);
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pDstItem, pSrcItem );
+ return;
+ }
+
+ // now do moves, remove...
+ RemoveItem(dstbag, dstslot, false);
+ RemoveItem(srcbag, srcslot, false);
+
+ // add to dest
+ if( IsInventoryPos( dst ) )
+ StoreItem(sDest, pSrcItem, true);
+ else if( IsBankPos( dst ) )
+ BankItem(sDest, pSrcItem, true);
+ else if( IsEquipmentPos( dst ) )
+ EquipItem(eDest, pSrcItem, true);
+
+ // add to src
+ if( IsInventoryPos( src ) )
+ StoreItem(sDest2, pDstItem, true);
+ else if( IsBankPos( src ) )
+ BankItem(sDest2, pDstItem, true);
+ else if( IsEquipmentPos( src ) )
+ EquipItem(eDest2, pDstItem, true);
+
+ AutoUnequipOffhandIfNeed();
+ }
+}
+
+void Player::AddItemToBuyBackSlot( Item *pItem )
+{
+ if( pItem )
+ {
+ uint32 slot = m_currentBuybackSlot;
+ // if current back slot non-empty search oldest or free
+ if(m_items[slot])
+ {
+ uint32 oldest_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 );
+ uint32 oldest_slot = BUYBACK_SLOT_START;
+
+ for(uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i )
+ {
+ // found empty
+ if(!m_items[i])
+ {
+ slot = i;
+ break;
+ }
+
+ uint32 i_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
+
+ if(oldest_time > i_time)
+ {
+ oldest_time = i_time;
+ oldest_slot = i;
+ }
+ }
+
+ // find oldest
+ slot = oldest_slot;
+ }
+
+ RemoveItemFromBuyBackSlot( slot, true );
+ sLog.outDebug( "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
+
+ m_items[slot] = pItem;
+ time_t base = time(NULL);
+ uint32 etime = uint32(base - m_logintime + (30 * 3600));
+ uint32 eslot = slot - BUYBACK_SLOT_START;
+
+ SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, pItem->GetGUID() );
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, pProto->SellPrice * pItem->GetCount() );
+ else
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime );
+
+ // move to next (for non filled list is move most optimized choice)
+ if(m_currentBuybackSlot < BUYBACK_SLOT_END-1)
+ ++m_currentBuybackSlot;
+ }
+}
+
+Item* Player::GetItemFromBuyBackSlot( uint32 slot )
+{
+ sLog.outDebug( "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
+ if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
+ return m_items[slot];
+ return NULL;
+}
+
+void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
+{
+ sLog.outDebug( "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
+ if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
+ {
+ Item *pItem = m_items[slot];
+ if( pItem )
+ {
+ pItem->RemoveFromWorld();
+ if(del) pItem->SetState(ITEM_REMOVED, this);
+ }
+
+ m_items[slot] = NULL;
+
+ uint32 eslot = slot - BUYBACK_SLOT_START;
+ SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0 );
+
+ // if current backslot is filled set to now free slot
+ if(m_items[m_currentBuybackSlot])
+ m_currentBuybackSlot = slot;
+ }
+}
+
+void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2 )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)",msg);
+ WorldPacket data( SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18) );
+ data << uint8(msg);
+
+ if(msg)
+ {
+ data << uint64(pItem ? pItem->GetGUID() : 0);
+ data << uint64(pItem2 ? pItem2->GetGUID() : 0);
+ data << uint8(0); // not 0 there...
+
+ if(msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I)
+ {
+ uint32 level = 0;
+
+ if(pItem)
+ if(ItemPrototype const* proto = pItem->GetProto())
+ level = proto->RequiredLevel;
+
+ data << uint32(level); // new 2.4.0
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_BUY_FAILED" );
+ WorldPacket data( SMSG_BUY_FAILED, (8+4+4+1) );
+ data << uint64(pCreature ? pCreature->GetGUID() : 0);
+ data << uint32(item);
+ if( param > 0 )
+ data << uint32(param);
+ data << uint8(msg);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_SELL_ITEM" );
+ WorldPacket data( SMSG_SELL_ITEM,(8+8+(param?4:0)+1)); // last check 2.0.10
+ data << uint64(pCreature ? pCreature->GetGUID() : 0);
+ data << uint64(guid);
+ if( param > 0 )
+ data << uint32(param);
+ data << uint8(msg);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ClearTrade()
+{
+ tradeGold = 0;
+ acceptTrade = false;
+ for(int i = 0; i < TRADE_SLOT_COUNT; i++)
+ tradeItems[i] = NULL_SLOT;
+}
+
+void Player::TradeCancel(bool sendback)
+{
+ if(pTrader)
+ {
+ // send yellow "Trade cancelled" message to both traders
+ WorldSession* ws;
+ ws = GetSession();
+ if(sendback)
+ ws->SendCancelTrade();
+ ws = pTrader->GetSession();
+ if(!ws->PlayerLogout())
+ ws->SendCancelTrade();
+
+ // cleanup
+ ClearTrade();
+ pTrader->ClearTrade();
+ // prevent loss of reference
+ pTrader->pTrader = NULL;
+ pTrader = NULL;
+ }
+}
+
+void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
+{
+ if(m_itemDuration.empty())
+ return;
+
+ sLog.outDebug("Player::UpdateItemDuration(%u,%u)", time,realtimeonly);
+
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); )
+ {
+ Item* item = *itr;
+ ++itr; // current element can be erased in UpdateDuration
+
+ if (realtimeonly && item->GetProto()->Duration < 0 || !realtimeonly)
+ item->UpdateDuration(this,time);
+ }
+}
+
+void Player::UpdateEnchantTime(uint32 time)
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
+ {
+ assert(itr->item);
+ next=itr;
+ if(!itr->item->GetEnchantmentId(itr->slot))
+ {
+ next = m_enchantDuration.erase(itr);
+ }
+ else if(itr->leftduration <= time)
+ {
+ ApplyEnchantment(itr->item,itr->slot,false,false);
+ itr->item->ClearEnchantment(itr->slot);
+ next = m_enchantDuration.erase(itr);
+ }
+ else if(itr->leftduration > time)
+ {
+ itr->leftduration -= time;
+ ++next;
+ }
+ }
+}
+
+void Player::AddEnchantmentDurations(Item *item)
+{
+ for(int x=0;x<MAX_ENCHANTMENT_SLOT;++x)
+ {
+ if(!item->GetEnchantmentId(EnchantmentSlot(x)))
+ continue;
+
+ uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
+ if( duration > 0 )
+ AddEnchantmentDuration(item,EnchantmentSlot(x),duration);
+ }
+}
+
+void Player::RemoveEnchantmentDurations(Item *item)
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();)
+ {
+ if(itr->item == item)
+ {
+ // save duration in item
+ item->SetEnchantmentDuration(EnchantmentSlot(itr->slot),itr->leftduration);
+ itr = m_enchantDuration.erase(itr);
+ }
+ else
+ ++itr;
+ }
+}
+
+
+void Player::RemoveAllEnchantments(EnchantmentSlot slot)
+{
+ // remove enchantments from equipped items first to clean up the m_enchantDuration list
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
+ {
+ next = itr;
+ if(itr->slot==slot)
+ {
+ if(itr->item && itr->item->GetEnchantmentId(slot))
+ {
+ // remove from stats
+ ApplyEnchantment(itr->item,slot,false,false);
+ // remove visual
+ itr->item->ClearEnchantment(slot);
+ }
+ // remove from update list
+ next = m_enchantDuration.erase(itr);
+ }
+ else
+ ++next;
+ }
+
+ // remove enchants from inventory items
+ // NOTE: no need to remove these from stats, since these aren't equipped
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEnchantmentId(slot) )
+ pItem->ClearEnchantment(slot);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetEnchantmentId(slot) )
+ pItem->ClearEnchantment(slot);
+ }
+ }
+ }
+ }
+}
+
+// duration == 0 will remove item enchant
+void Player::AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration)
+{
+ if(!item)
+ return;
+
+ if(slot >= MAX_ENCHANTMENT_SLOT)
+ return;
+
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ if(itr->item == item && itr->slot == slot)
+ {
+ itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
+ m_enchantDuration.erase(itr);
+ break;
+ }
+ }
+ if(item && duration > 0 )
+ {
+ GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(),slot,uint32(duration/1000));
+ m_enchantDuration.push_back(EnchantDuration(item,slot,duration));
+ }
+}
+
+void Player::ApplyEnchantment(Item *item,bool apply)
+{
+ for(uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
+ ApplyEnchantment(item, EnchantmentSlot(slot), apply);
+}
+
+void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur, bool ignore_condition)
+{
+ if(!item)
+ return;
+
+ if(!item->IsEquipped())
+ return;
+
+ if(slot >= MAX_ENCHANTMENT_SLOT)
+ return;
+
+ uint32 enchant_id = item->GetEnchantmentId(slot);
+ if(!enchant_id)
+ return;
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ if(!ignore_condition && pEnchant->EnchantmentCondition && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
+ return;
+
+ for (int s=0; s<3; s++)
+ {
+ uint32 enchant_display_type = pEnchant->type[s];
+ uint32 enchant_amount = pEnchant->amount[s];
+ uint32 enchant_spell_id = pEnchant->spellid[s];
+
+ switch(enchant_display_type)
+ {
+ case ITEM_ENCHANTMENT_TYPE_NONE:
+ break;
+ case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
+ // processed in Player::CastItemCombatSpell
+ break;
+ case ITEM_ENCHANTMENT_TYPE_DAMAGE:
+ if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
+ HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
+ else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
+ HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
+ else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
+ HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
+ break;
+ case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
+ if(enchant_spell_id)
+ {
+ if(apply)
+ {
+ int32 basepoints = int32(enchant_amount);
+ // Random Property Exist - try found basepoints for spell (basepoints depencs from item suffix factor)
+ if (item->GetItemRandomPropertyId() !=0 && !enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if (item_rand)
+ {
+ // Search enchant_amount
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand->enchant_id[k] == enchant_id)
+ {
+ basepoints = int32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+ // Cast custom spell vs all equal basepoints getted from enchant_amount
+ if (basepoints)
+ CastCustomSpell(this,enchant_spell_id,&basepoints,&basepoints,&basepoints,true,item);
+ else
+ CastSpell(this,enchant_spell_id,true,item);
+ }
+ else
+ RemoveAurasDueToItemSpell(item,enchant_spell_id);
+ }
+ break;
+ case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
+ if (!enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if(item_rand)
+ {
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand->enchant_id[k] == enchant_id)
+ {
+ enchant_amount = uint32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+
+ HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
+ break;
+ case ITEM_ENCHANTMENT_TYPE_STAT:
+ {
+ if (!enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if(item_rand_suffix)
+ {
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand_suffix->enchant_id[k] == enchant_id)
+ {
+ enchant_amount = uint32((item_rand_suffix->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+
+ sLog.outDebug("Adding %u to stat nb %u",enchant_amount,enchant_spell_id);
+ switch (enchant_spell_id)
+ {
+ case ITEM_MOD_AGILITY:
+ sLog.outDebug("+ %u AGILITY",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_AGILITY, enchant_amount, apply);
+ break;
+ case ITEM_MOD_STRENGTH:
+ sLog.outDebug("+ %u STRENGTH",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_STRENGTH, enchant_amount, apply);
+ break;
+ case ITEM_MOD_INTELLECT:
+ sLog.outDebug("+ %u INTELLECT",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_INTELLECT, enchant_amount, apply);
+ break;
+ case ITEM_MOD_SPIRIT:
+ sLog.outDebug("+ %u SPIRIT",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_SPIRIT, enchant_amount, apply);
+ break;
+ case ITEM_MOD_STAMINA:
+ sLog.outDebug("+ %u STAMINA",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_STAMINA, enchant_amount, apply);
+ break;
+ case ITEM_MOD_DEFENSE_SKILL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
+ sLog.outDebug("+ %u DEFENCE", enchant_amount);
+ break;
+ case ITEM_MOD_DODGE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_DODGE, enchant_amount, apply);
+ sLog.outDebug("+ %u DODGE", enchant_amount);
+ break;
+ case ITEM_MOD_PARRY_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_PARRY, enchant_amount, apply);
+ sLog.outDebug("+ %u PARRY", enchant_amount);
+ break;
+ case ITEM_MOD_BLOCK_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
+ sLog.outDebug("+ %u SHIELD_BLOCK", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_MELEE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
+ sLog.outDebug("+ %u MELEE_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_RANGED_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
+ sLog.outDebug("+ %u RANGED_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u SPELL_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_MELEE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
+ sLog.outDebug("+ %u MELEE_CRIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_RANGED_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
+ sLog.outDebug("+ %u RANGED_CRIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u SPELL_CRIT", enchant_amount);
+ break;
+// Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
+// in Enchantments
+// case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HASTE_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HASTE_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
+// break;
+ case ITEM_MOD_HASTE_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
+ break;
+ case ITEM_MOD_HIT_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u HIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u CRITICAL", enchant_amount);
+ break;
+// Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
+// case ITEM_MOD_HIT_TAKEN_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+ case ITEM_MOD_RESILIENCE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u RESILIENCE", enchant_amount);
+ break;
+ case ITEM_MOD_HASTE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u HASTE", enchant_amount);
+ break;
+ case ITEM_MOD_EXPERTISE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
+ sLog.outDebug("+ %u EXPERTISE", enchant_amount);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
+ {
+ if(getClass() == CLASS_SHAMAN)
+ {
+ float addValue = 0.0f;
+ if(item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
+ {
+ addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
+ HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
+ }
+ else if(item->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
+ {
+ addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
+ HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
+ }
+ }
+ break;
+ }
+ default:
+ sLog.outError("Unknown item enchantment display type: %d",enchant_display_type);
+ break;
+ } /*switch(enchant_display_type)*/
+ } /*for*/
+
+ // visualize enchantment at player and equipped items
+ if(slot < MAX_INSPECTED_ENCHANTMENT_SLOT)
+ {
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (item->GetSlot() * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 1 + slot, apply? item->GetEnchantmentId(slot) : 0);
+ }
+
+ if(apply_dur)
+ {
+ if(apply)
+ {
+ // set duration
+ uint32 duration = item->GetEnchantmentDuration(slot);
+ if(duration > 0)
+ AddEnchantmentDuration(item,slot,duration);
+ }
+ else
+ {
+ // duration == 0 will remove EnchantDuration
+ AddEnchantmentDuration(item,slot,0);
+ }
+ }
+}
+
+void Player::SendEnchantmentDurations()
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(),itr->slot,uint32(itr->leftduration)/1000);
+ }
+}
+
+void Player::SendItemDurations()
+{
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end();++itr)
+ {
+ (*itr)->SendTimeUpdate(this);
+ }
+}
+
+void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, bool broadcast)
+{
+ if(!item) // prevent crash
+ return;
+
+ // last check 2.0.10
+ WorldPacket data( SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4) );
+ data << GetGUID(); // player GUID
+ data << uint32(received); // 0=looted, 1=from npc
+ data << uint32(created); // 0=received, 1=created
+ data << uint32(1); // always 0x01 (probably meant to be count of listed items)
+ data << (uint8)item->GetBagSlot(); // bagslot
+ // item slot, but when added to stack: 0xFFFFFFFF
+ data << (uint32) ((item->GetCount()==count) ? item->GetSlot() : -1);
+ data << uint32(item->GetEntry()); // item id
+ data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
+ data << uint32(item->GetItemRandomPropertyId()); // random item property id
+ data << uint32(count); // count of items
+ data << GetItemCount(item->GetEntry()); // count of items in inventory
+
+ if (broadcast && GetGroup())
+ GetGroup()->BroadcastPacket(&data);
+ else
+ GetSession()->SendPacket(&data);
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+
+void Player::PrepareQuestMenu( uint64 guid )
+{
+ Object *pObject;
+ QuestRelations* pObjectQR;
+ QuestRelations* pObjectQIR;
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ pObject = (Object*)pCreature;
+ pObjectQR = &objmgr.mCreatureQuestRelations;
+ pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
+ }
+ else
+ {
+ GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
+ if( pGameObject )
+ {
+ pObject = (Object*)pGameObject;
+ pObjectQR = &objmgr.mGOQuestRelations;
+ pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
+ }
+ else
+ return;
+ }
+
+ QuestMenu *qm = PlayerTalkClass->GetQuestMenu();
+ qm->ClearMenu();
+
+ for(QuestRelations::const_iterator i = pObjectQIR->lower_bound(pObject->GetEntry()); i != pObjectQIR->upper_bound(pObject->GetEntry()); ++i)
+ {
+ uint32 quest_id = i->second;
+ QuestStatus status = GetQuestStatus( quest_id );
+ if ( status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus( quest_id ) )
+ qm->AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
+ else if ( status == QUEST_STATUS_INCOMPLETE )
+ qm->AddMenuItem(quest_id, DIALOG_STATUS_INCOMPLETE);
+ 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)
+ {
+ uint32 quest_id = i->second;
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if(!pQuest) continue;
+
+ QuestStatus status = GetQuestStatus( quest_id );
+
+ if (pQuest->IsAutoComplete() && CanTakeQuest(pQuest, false))
+ qm->AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
+ else if ( status == QUEST_STATUS_NONE && CanTakeQuest( pQuest, false ) )
+ qm->AddMenuItem(quest_id, DIALOG_STATUS_AVAILABLE);
+ }
+}
+
+void Player::SendPreparedQuest( uint64 guid )
+{
+ QuestMenu* pQuestMenu = PlayerTalkClass->GetQuestMenu();
+ if( !pQuestMenu || pQuestMenu->MenuItemCount() < 1 )
+ return;
+
+ uint32 status = pQuestMenu->GetItem(0).m_qIcon;
+ if ( pQuestMenu->MenuItemCount() == 1 )
+ {
+ // Auto open -- maybe also should verify there is no greeting
+ uint32 quest_id = pQuestMenu->GetItem(0).m_qId;
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if ( pQuest )
+ {
+ if( status == DIALOG_STATUS_REWARD_REP && !GetQuestRewardStatus( quest_id ) )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanRewardQuest(pQuest,false), true );
+ else if( status == DIALOG_STATUS_INCOMPLETE )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, false, true );
+ // Send completable on repeatable quest if player don't have quest
+ else if( pQuest->IsRepeatable() )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanCompleteRepeatableQuest(pQuest), true );
+ else
+ PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, guid, true );
+ }
+ }
+ else
+ {
+ QEmote qe;
+ qe._Delay = 0;
+ qe._Emote = 0;
+ std::string title = "";
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ uint32 textid = pCreature->GetNpcTextId();
+ GossipText * gossiptext = objmgr.GetGossipText(textid);
+ if( !gossiptext )
+ {
+ qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
+ qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
+ title = "";
+ }
+ else
+ {
+ qe = gossiptext->Options[0].Emotes[0];
+
+ if(!gossiptext->Options[0].Text_0.empty())
+ {
+ title = gossiptext->Options[0].Text_0;
+
+ int loc_idx = GetSession()->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
+ if (nl)
+ {
+ if (nl->Text_0[0].size() > loc_idx && !nl->Text_0[0][loc_idx].empty())
+ title = nl->Text_0[0][loc_idx];
+ }
+ }
+ }
+ else
+ {
+ title = gossiptext->Options[0].Text_1;
+
+ int loc_idx = GetSession()->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
+ if (nl)
+ {
+ if (nl->Text_1[0].size() > loc_idx && !nl->Text_1[0][loc_idx].empty())
+ title = nl->Text_1[0][loc_idx];
+ }
+ }
+ }
+ }
+ }
+ PlayerTalkClass->SendQuestGiverQuestList( qe, title, guid );
+ }
+}
+
+bool Player::IsActiveQuest( uint32 quest_id ) const
+{
+ QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id);
+
+ return itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE;
+}
+
+Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
+{
+ Object *pObject;
+ QuestRelations* pObjectQR;
+ QuestRelations* pObjectQIR;
+
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ pObject = (Object*)pCreature;
+ pObjectQR = &objmgr.mCreatureQuestRelations;
+ pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
+ }
+ else
+ {
+ GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
+ if( pGameObject )
+ {
+ pObject = (Object*)pGameObject;
+ pObjectQR = &objmgr.mGOQuestRelations;
+ pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
+ }
+ else
+ return NULL;
+ }
+
+ uint32 nextQuestID = pQuest->GetNextQuestInChain();
+ for(QuestRelations::const_iterator itr = pObjectQR->lower_bound(pObject->GetEntry()); itr != pObjectQR->upper_bound(pObject->GetEntry()); ++itr)
+ {
+ if (itr->second == nextQuestID)
+ return objmgr.GetQuestTemplate(nextQuestID);
+ }
+
+ return NULL;
+}
+
+bool Player::CanSeeStartQuest( Quest const *pQuest )
+{
+ if( SatisfyQuestRace( pQuest, false ) && SatisfyQuestSkillOrClass( pQuest, false ) &&
+ SatisfyQuestExclusiveGroup( pQuest, false ) && SatisfyQuestReputation( pQuest, false ) &&
+ SatisfyQuestPreviousQuest( pQuest, false ) && SatisfyQuestNextChain( pQuest, false ) &&
+ SatisfyQuestPrevChain( pQuest, false ) && SatisfyQuestDay( pQuest, false ) )
+ {
+ return getLevel() + sWorld.getConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= pQuest->GetMinLevel();
+ }
+
+ return false;
+}
+
+bool Player::CanTakeQuest( Quest const *pQuest, bool msg )
+{
+ return SatisfyQuestStatus( pQuest, msg ) && SatisfyQuestExclusiveGroup( pQuest, msg )
+ && SatisfyQuestRace( pQuest, msg ) && SatisfyQuestLevel( pQuest, msg )
+ && SatisfyQuestSkillOrClass( pQuest, msg ) && SatisfyQuestReputation( pQuest, msg )
+ && SatisfyQuestPreviousQuest( pQuest, msg ) && SatisfyQuestTimed( pQuest, msg )
+ && SatisfyQuestNextChain( pQuest, msg ) && SatisfyQuestPrevChain( pQuest, msg )
+ && SatisfyQuestDay( pQuest, msg );
+}
+
+bool Player::CanAddQuest( Quest const *pQuest, bool msg )
+{
+ if( !SatisfyQuestLog( msg ) )
+ return false;
+
+ uint32 srcitem = pQuest->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = pQuest->GetSrcItemCount();
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
+
+ // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
+ if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ return true;
+ else if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Player::CanCompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+ if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ return false; // not allow re-complete quest
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+
+ if(!qInfo)
+ return false;
+
+ // auto complete quest
+ if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
+ return true;
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( qInfo->ReqItemCount[i]!= 0 && q_status.m_itemcount[i] < qInfo->ReqItemCount[i] )
+ return false;
+ }
+ }
+
+ if ( qInfo->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( qInfo->ReqCreatureOrGOId[i] == 0 )
+ continue;
+
+ if( qInfo->ReqCreatureOrGOCount[i] != 0 && q_status.m_creatureOrGOcount[i] < qInfo->ReqCreatureOrGOCount[i] )
+ return false;
+ }
+ }
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT ) && !q_status.m_explored )
+ return false;
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && q_status.m_timer == 0 )
+ return false;
+
+ if ( qInfo->GetRewOrReqMoney() < 0 )
+ {
+ if ( GetMoney() < uint32(-qInfo->GetRewOrReqMoney()) )
+ return false;
+ }
+
+ uint32 repFacId = qInfo->GetRepObjectiveFaction();
+ if ( repFacId && GetReputation(repFacId) < qInfo->GetRepObjectiveValue() )
+ return false;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Player::CanCompleteRepeatableQuest( Quest const *pQuest )
+{
+ // Solve problem that player don't have the quest and try complete it.
+ // if repeatable she must be able to complete event if player don't have it.
+ // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
+ if( !CanTakeQuest(pQuest, false) )
+ return false;
+
+ if (pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER) )
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ if( pQuest->ReqItemId[i] && pQuest->ReqItemCount[i] && !HasItemCount(pQuest->ReqItemId[i],pQuest->ReqItemCount[i]) )
+ return false;
+
+ if( !CanRewardQuest(pQuest, false) )
+ return false;
+
+ return true;
+}
+
+bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
+{
+ // not auto complete quest and not completed quest (only cheating case, then ignore without message)
+ if(!pQuest->IsAutoComplete() && GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE)
+ return false;
+
+ // daily quest can't be rewarded (10 daily quest already completed)
+ if(!SatisfyQuestDay(pQuest,true))
+ return false;
+
+ // rewarded and not repeatable quest (only cheating case, then ignore without message)
+ if(GetQuestRewardStatus(pQuest->GetQuestId()))
+ return false;
+
+ // prevent receive reward with quest items in bank
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( pQuest->ReqItemCount[i]!= 0 &&
+ GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i] )
+ {
+ if(msg)
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return false;
+ }
+ }
+ }
+
+ // prevent receive reward with low money and GetRewOrReqMoney() < 0
+ if(pQuest->GetRewOrReqMoney() < 0 && GetMoney() < uint32(-pQuest->GetRewOrReqMoney()) )
+ return false;
+
+ return true;
+}
+
+bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
+{
+ // prevent receive reward with quest items in bank or for not completed quest
+ if(!CanRewardQuest(pQuest,msg))
+ return false;
+
+ if ( pQuest->GetRewChoiceItemsCount() > 0 )
+ {
+ if( pQuest->RewChoiceItemId[reward] )
+ {
+ ItemPosCountVec dest;
+ uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] );
+ if( res != EQUIP_ERR_OK )
+ {
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+ }
+ }
+
+ if ( pQuest->GetRewItemsCount() > 0 )
+ {
+ for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if( pQuest->RewItemId[i] )
+ {
+ ItemPosCountVec dest;
+ uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] );
+ if( res != EQUIP_ERR_OK )
+ {
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void Player::AddQuest( Quest const *pQuest, Object *questGiver )
+{
+ uint16 log_slot = FindQuestSlot( 0 );
+ assert(log_slot < MAX_QUEST_LOG_SIZE);
+
+ uint32 quest_id = pQuest->GetQuestId();
+
+ // if not exist then created with set uState==NEW and rewarded=false
+ QuestStatusData& questStatusData = mQuestStatus[quest_id];
+ if (questStatusData.uState != QUEST_NEW)
+ questStatusData.uState = QUEST_CHANGED;
+
+ // check for repeatable quests status reset
+ questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
+ questStatusData.m_explored = false;
+
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ questStatusData.m_itemcount[i] = 0;
+ }
+
+ if ( pQuest->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ questStatusData.m_creatureOrGOcount[i] = 0;
+ }
+
+ GiveQuestSourceItem( pQuest );
+ AdjustQuestReqItemCount( pQuest );
+
+ if( pQuest->GetRepObjectiveFaction() )
+ SetFactionVisibleForFactionId(pQuest->GetRepObjectiveFaction());
+
+ uint32 qtime = 0;
+ if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
+ {
+ uint32 limittime = pQuest->GetLimitTime();
+
+ // shared timed quest
+ if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
+ limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000;
+
+ AddTimedQuest( quest_id );
+ questStatusData.m_timer = limittime * 1000;
+ qtime = static_cast<uint32>(time(NULL)) + limittime;
+ }
+ else
+ questStatusData.m_timer = 0;
+
+ SetQuestSlot(log_slot, quest_id, qtime);
+
+ //starting initial quest script
+ if(questGiver && pQuest->GetQuestStartScript()!=0)
+ sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
+
+ UpdateForQuestsGO();
+}
+
+void Player::CompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ SetQuestStatus( quest_id, QUEST_STATUS_COMPLETE );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ SetQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
+
+ if(Quest const* qInfo = objmgr.GetQuestTemplate(quest_id))
+ {
+ if( qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED) )
+ RewardQuest(qInfo,0,this,false);
+ else
+ SendQuestComplete( quest_id );
+ }
+ }
+}
+
+void Player::IncompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ SetQuestStatus( quest_id, QUEST_STATUS_INCOMPLETE );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ RemoveQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
+ }
+}
+
+void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce )
+{
+ uint32 quest_id = pQuest->GetQuestId();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++ )
+ {
+ if ( pQuest->ReqItemId[i] )
+ DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true);
+ }
+
+ //if( qInfo->HasSpecialFlag( QUEST_FLAGS_TIMED ) )
+ // SetTimedQuest( 0 );
+ m_timedquests.erase(pQuest->GetQuestId());
+
+ if ( pQuest->GetRewChoiceItemsCount() > 0 )
+ {
+ if( pQuest->RewChoiceItemId[reward] )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, pQuest->RewChoiceItemId[reward], true);
+ SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false);
+ }
+ }
+ }
+
+ if ( pQuest->GetRewItemsCount() > 0 )
+ {
+ for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if( pQuest->RewItemId[i] )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, pQuest->RewItemId[i], true);
+ SendNewItem(item, pQuest->RewItemCount[i], true, false);
+ }
+ }
+ }
+ }
+
+ 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)
+ SetQuestSlot(log_slot,0);
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ // 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) )
+ GiveXP( XP , NULL );
+ else
+ ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) );
+
+ // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
+ ModifyMoney( pQuest->GetRewOrReqMoney() );
+
+ // title reward
+ if(pQuest->GetCharTitleId())
+ {
+ if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
+ SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
+ }
+
+ // Send reward mail
+ if(pQuest->GetRewMailTemplateId())
+ {
+ MailMessageType mailType;
+ uint32 senderGuidOrEntry;
+ switch(questGiver->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ mailType = MAIL_CREATURE;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_GAMEOBJECT:
+ mailType = MAIL_GAMEOBJECT;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_ITEM:
+ mailType = MAIL_ITEM;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_PLAYER:
+ mailType = MAIL_NORMAL;
+ senderGuidOrEntry = questGiver->GetGUIDLow();
+ break;
+ default:
+ mailType = MAIL_NORMAL;
+ senderGuidOrEntry = GetGUIDLow();
+ break;
+ }
+
+ Loot questMailLoot;
+
+ questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this);
+
+ // fill mail
+ MailItemsInfo mi; // item list preparing
+
+ for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.items.size(); ++i)
+ {
+ if(LootItem* lootitem = questMailLoot.LootItemInSlot(i,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);
+ }
+ }
+ }
+
+ for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.quest_items.size(); ++i)
+ {
+ if(LootItem* lootitem = questMailLoot.LootItemInSlot(i+questMailLoot.items.size(),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);
+ }
+ }
+ }
+
+ WorldSession::SendMailTo(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId());
+ }
+
+ if(pQuest->IsDaily())
+ SetDailyQuestStatus(quest_id);
+
+ if ( !pQuest->IsRepeatable() )
+ SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
+ else
+ SetQuestStatus(quest_id, QUEST_STATUS_NONE);
+
+ q_status.m_rewarded = true;
+
+ if(announce)
+ SendQuestReward( pQuest, XP, questGiver );
+
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+}
+
+void Player::FailQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ IncompleteQuest( quest_id );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ SetQuestSlotTimer(log_slot, 1 );
+ SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
+ }
+ SendQuestFailed( quest_id );
+ }
+}
+
+void Player::FailTimedQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ q_status.m_timer = 0;
+
+ IncompleteQuest( quest_id );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ SetQuestSlotTimer(log_slot, 1 );
+ SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
+ }
+ SendQuestTimerFailed( quest_id );
+ }
+}
+
+bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
+{
+ int32 zoneOrSort = qInfo->GetZoneOrSort();
+ int32 skillOrClass = qInfo->GetSkillOrClass();
+
+ // skip zone zoneOrSort and 0 case skillOrClass
+ if( zoneOrSort >= 0 && skillOrClass == 0 )
+ return true;
+
+ int32 questSort = -zoneOrSort;
+ uint8 reqSortClass = ClassByQuestSort(questSort);
+
+ // check class sort cases in zoneOrSort
+ if( reqSortClass != 0 && getClass() != reqSortClass)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ // check class
+ if( skillOrClass < 0 )
+ {
+ uint8 reqClass = -int32(skillOrClass);
+ if(getClass() != reqClass)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ // check skill
+ else if( skillOrClass > 0 )
+ {
+ uint32 reqSkill = skillOrClass;
+ if( GetSkillValue( reqSkill ) < qInfo->GetRequiredSkillValue() )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
+{
+ if( getLevel() < qInfo->GetMinLevel() )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestLog( bool msg )
+{
+ // exist free slot
+ if( FindQuestSlot(0) < MAX_QUEST_LOG_SIZE )
+ return true;
+
+ if( msg )
+ {
+ WorldPacket data( SMSG_QUESTLOG_FULL, 0 );
+ GetSession()->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent QUEST_LOG_FULL_MESSAGE" );
+ }
+ return false;
+}
+
+bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
+{
+ // No previous quest (might be first quest in a series)
+ if( qInfo->prevQuests.empty())
+ return true;
+
+ for(Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter )
+ {
+ uint32 prevId = abs(*iter);
+
+ QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
+ Quest const* qPrevInfo = objmgr.GetQuestTemplate(prevId);
+
+ if( qPrevInfo && i_prevstatus != mQuestStatus.end() )
+ {
+ // If any of the positive previous quests completed, return true
+ if( *iter > 0 && i_prevstatus->second.m_rewarded )
+ {
+ // skip one-from-all exclusive group
+ if(qPrevInfo->GetExclusiveGroup() >= 0)
+ return true;
+
+ // each-from-all exclusive group ( < 0)
+ // can be start if only all quests in prev quest exclusive group complited and rewarded
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == prevId)
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest from group also must be completed and rewarded(reported)
+ if( i_exstatus == mQuestStatus.end() || !i_exstatus->second.m_rewarded )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+ }
+ // If any of the negative previous quests active, return true
+ if( *iter < 0 && (i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
+ || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId))))
+ {
+ // skip one-from-all exclusive group
+ if(qPrevInfo->GetExclusiveGroup() >= 0)
+ return true;
+
+ // each-from-all exclusive group ( < 0)
+ // can be start if only all quests in prev quest exclusive group active
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == prevId)
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest from group also must be active
+ if( i_exstatus == mQuestStatus.end() ||
+ i_exstatus->second.m_status != QUEST_STATUS_INCOMPLETE &&
+ (i_prevstatus->second.m_status != QUEST_STATUS_COMPLETE || GetQuestRewardStatus(prevId)) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+
+ // Has only positive prev. quests in non-rewarded state
+ // and negative prev. quests in non-active state
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+
+ return false;
+}
+
+bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg )
+{
+ uint32 reqraces = qInfo->GetRequiredRaces();
+ if ( reqraces == 0 )
+ return true;
+ if( (reqraces & getRaceMask()) == 0 )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_FAILED_WRONG_RACE );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
+{
+ uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
+ if(fIdMin && GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
+ if(fIdMax && GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::SatisfyQuestStatus( Quest const* qInfo, bool msg )
+{
+ QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetQuestId() );
+ if ( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_ALREADY_ON );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestTimed( Quest const* qInfo, bool msg )
+{
+ if ( (find(m_timedquests.begin(), m_timedquests.end(), qInfo->GetQuestId()) != m_timedquests.end()) && qInfo->HasFlag(QUEST_MANGOS_FLAGS_TIMED) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_ONLY_ONE_TIMED );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
+{
+ // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
+ if(qInfo->GetExclusiveGroup() <= 0)
+ return true;
+
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == qInfo->GetQuestId())
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest already started or completed
+ if( i_exstatus != mQuestStatus.end()
+ && (i_exstatus->second.m_status == QUEST_STATUS_COMPLETE || i_exstatus->second.m_status == QUEST_STATUS_INCOMPLETE) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestNextChain( Quest const* qInfo, bool msg )
+{
+ if(!qInfo->GetNextQuestInChain())
+ return true;
+
+ // next quest in chain already started or completed
+ QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetNextQuestInChain() );
+ if( itr != mQuestStatus.end()
+ && (itr->second.m_status == QUEST_STATUS_COMPLETE || itr->second.m_status == QUEST_STATUS_INCOMPLETE) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ // check for all quests further up the chain
+ // only necessary if there are quest chains with more than one quest that can be skipped
+ //return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg );
+ return true;
+}
+
+bool Player::SatisfyQuestPrevChain( Quest const* qInfo, bool msg )
+{
+ // No previous quest in chain
+ if( qInfo->prevChainQuests.empty())
+ return true;
+
+ for(Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter )
+ {
+ uint32 prevId = *iter;
+
+ QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
+
+ if( i_prevstatus != mQuestStatus.end() )
+ {
+ // If any of the previous quests in chain active, return false
+ if( i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
+ || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId)))
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+
+ // check for all quests further down the chain
+ // only necessary if there are quest chains with more than one quest that can be skipped
+ //if( !SatisfyQuestPrevChain( prevId, msg ) )
+ // return false;
+ }
+
+ // No previous quest in chain active
+ return true;
+}
+
+bool Player::SatisfyQuestDay( Quest const* qInfo, bool msg )
+{
+ if(!qInfo->IsDaily())
+ return true;
+
+ bool have_slot = false;
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ {
+ uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
+ if(qInfo->GetQuestId()==id)
+ return false;
+
+ if(!id)
+ have_slot = true;
+ }
+
+ if(!have_slot)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DAILY_QUESTS_REMAINING );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::GiveQuestSourceItem( Quest const *pQuest )
+{
+ uint32 srcitem = pQuest->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = pQuest->GetSrcItemCount();
+ if( count <= 0 )
+ count = 1;
+
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item * item = StoreNewItem(dest, srcitem, true);
+ SendNewItem(item, count, true, false);
+ return true;
+ }
+ // player already have max amount required item, just report success
+ else if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ return true;
+ else
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ uint32 srcitem = qInfo->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = qInfo->GetSrcItemCount();
+ if( count <= 0 )
+ count = 1;
+
+ // exist one case when destroy source quest item not possible:
+ // non un-equippable item (equipped non-empty bag, for example)
+ uint8 res = CanUnequipItems(srcitem,count);
+ if(res != EQUIP_ERR_OK)
+ {
+ if(msg)
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+
+ DestroyItemCount(srcitem, count, true, true);
+ }
+ }
+ return true;
+}
+
+bool Player::GetQuestRewardStatus( uint32 quest_id ) const
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE
+ && !qInfo->IsRepeatable() )
+ return itr->second.m_rewarded;
+
+ return false;
+ }
+ return false;
+}
+
+QuestStatus Player::GetQuestStatus( uint32 quest_id ) const
+{
+ if( quest_id )
+ {
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() )
+ return itr->second.m_status;
+ }
+ return QUEST_STATUS_NONE;
+}
+
+bool Player::CanShareQuest(uint32 quest_id) const
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) )
+ {
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() )
+ return itr->second.m_status == QUEST_STATUS_NONE || itr->second.m_status == QUEST_STATUS_INCOMPLETE;
+ }
+ return false;
+}
+
+void Player::SetQuestStatus( uint32 quest_id, QuestStatus status )
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ if( status == QUEST_STATUS_NONE || status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
+ m_timedquests.erase(qInfo->GetQuestId());
+ }
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ q_status.m_status = status;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ }
+
+ UpdateForQuestsGO();
+}
+
+// not used in MaNGOS, but used in scripting code
+uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( !qInfo )
+ return 0;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ if ( qInfo->ReqCreatureOrGOId[j] == entry )
+ return mQuestStatus[quest_id].m_creatureOrGOcount[j];
+
+ return 0;
+}
+
+void Player::AdjustQuestReqItemCount( Quest const* pQuest )
+{
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ uint32 reqitemcount = pQuest->ReqItemCount[i];
+ if( reqitemcount != 0 )
+ {
+ uint32 quest_id = pQuest->GetQuestId();
+ uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i],true);
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+ q_status.m_itemcount[i] = std::min(curitemcount, reqitemcount);
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ }
+ }
+ }
+}
+
+uint16 Player::FindQuestSlot( uint32 quest_id ) const
+{
+ for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ if ( GetQuestSlotQuestId(i) == quest_id )
+ return i;
+
+ return MAX_QUEST_LOG_SIZE;
+}
+
+void Player::AreaExploredOrEventHappens( uint32 questId )
+{
+ if( questId )
+ {
+ uint16 log_slot = FindQuestSlot( questId );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ QuestStatusData& q_status = mQuestStatus[questId];
+
+ if(!q_status.m_explored)
+ {
+ q_status.m_explored = true;
+ if (q_status.uState != QUEST_NEW)
+ q_status.uState = QUEST_CHANGED;
+ }
+ }
+ if( CanCompleteQuest( questId ) )
+ CompleteQuest( questId );
+ }
+}
+
+//not used in mangosd, function for external script library
+void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject )
+{
+ if( Group *pGroup = GetGroup() )
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pGroupGuy = itr->getSource();
+
+ // for any leave or dead (with not released body) group member at appropriate distance
+ if( pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject) && !pGroupGuy->GetCorpse() )
+ pGroupGuy->AreaExploredOrEventHappens(questId);
+ }
+ }
+ else
+ AreaExploredOrEventHappens(questId);
+}
+
+void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if ( questid == 0 )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status != QUEST_STATUS_INCOMPLETE )
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( !qInfo || !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ uint32 reqitem = qInfo->ReqItemId[j];
+ if ( reqitem == entry )
+ {
+ uint32 reqitemcount = qInfo->ReqItemCount[j];
+ uint32 curitemcount = q_status.m_itemcount[j];
+ if ( curitemcount < reqitemcount )
+ {
+ uint32 additemcount = ( curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount);
+ q_status.m_itemcount[j] += additemcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddItem( qInfo, j, additemcount );
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ return;
+ }
+ }
+ }
+ UpdateForQuestsGO();
+}
+
+void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+ if( !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ uint32 reqitem = qInfo->ReqItemId[j];
+ if ( reqitem == entry )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ uint32 reqitemcount = qInfo->ReqItemCount[j];
+ uint32 curitemcount;
+ if( q_status.m_status != QUEST_STATUS_COMPLETE )
+ curitemcount = q_status.m_itemcount[j];
+ else
+ curitemcount = GetItemCount(entry,true);
+ if ( curitemcount < reqitemcount + count )
+ {
+ uint32 remitemcount = ( curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount);
+ q_status.m_itemcount[j] = curitemcount - remitemcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ IncompleteQuest( questid );
+ }
+ return;
+ }
+ }
+ }
+ UpdateForQuestsGO();
+}
+
+void Player::KilledMonster( uint32 entry, uint64 guid )
+{
+ uint32 addkillcount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( !qInfo )
+ continue;
+ // just if !ingroup || !noraidgroup || raidgroup
+ QuestStatusData& q_status = mQuestStatus[questid];
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->GetType() == QUEST_TYPE_RAID))
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip GO activate objective or none
+ if(qInfo->ReqCreatureOrGOId[j] <=0)
+ continue;
+
+ // skip Cast at creature objective
+ if(qInfo->ReqSpell[j] !=0 )
+ continue;
+
+ uint32 reqkill = qInfo->ReqCreatureOrGOId[j];
+
+ if ( reqkill == entry )
+ {
+ uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curkillcount = q_status.m_creatureOrGOcount[j];
+ if ( curkillcount < reqkillcount )
+ {
+ q_status.m_creatureOrGOcount[j] = curkillcount + addkillcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curkillcount, addkillcount);
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ continue;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
+{
+ bool isCreature = IS_CREATURE_GUID(guid);
+
+ uint32 addCastCount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST ) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip kill creature objective (0) or wrong spell casts
+ if(qInfo->ReqSpell[j] != spell_id )
+ continue;
+
+ uint32 reqTarget = 0;
+
+ if(isCreature)
+ {
+ // creature activate objectives
+ if(qInfo->ReqCreatureOrGOId[j] > 0)
+ // checked at quest_template loading
+ reqTarget = qInfo->ReqCreatureOrGOId[j];
+ }
+ else
+ {
+ // GO activate objective
+ if(qInfo->ReqCreatureOrGOId[j] < 0)
+ // checked at quest_template loading
+ reqTarget = - qInfo->ReqCreatureOrGOId[j];
+ }
+
+ // other not this creature/GO related objectives
+ if( reqTarget != entry )
+ continue;
+
+ uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curCastCount = q_status.m_creatureOrGOcount[j];
+ if ( curCastCount < reqCastCount )
+ {
+ q_status.m_creatureOrGOcount[j] = curCastCount + addCastCount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curCastCount, addCastCount);
+ }
+
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Player::TalkedToCreature( uint32 entry, uint64 guid )
+{
+ uint32 addTalkCount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO ) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip spell casts and Gameobject objectives
+ if(qInfo->ReqSpell[j] > 0 || qInfo->ReqCreatureOrGOId[j] < 0)
+ continue;
+
+ uint32 reqTarget = 0;
+
+ if(qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives
+ // checked at quest_template loading
+ reqTarget = qInfo->ReqCreatureOrGOId[j];
+ else
+ continue;
+
+ if ( reqTarget == entry )
+ {
+ uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curTalkCount = q_status.m_creatureOrGOcount[j];
+ if ( curTalkCount < reqTalkCount )
+ {
+ q_status.m_creatureOrGOcount[j] = curTalkCount + addTalkCount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curTalkCount, addTalkCount);
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ continue;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Player::MoneyChanged( uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if (!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( qInfo && qInfo->GetRewOrReqMoney() < 0 )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if(int32(count) >= -qInfo->GetRewOrReqMoney())
+ {
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ }
+ }
+ else if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ {
+ if(int32(count) < -qInfo->GetRewOrReqMoney())
+ IncompleteQuest( questid );
+ }
+ }
+ }
+}
+
+bool Player::HasQuestForItem( uint32 itemid ) const
+{
+ for( QuestStatusMap::const_iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ QuestStatusData const& q_status = i->second;
+
+ if (q_status.m_status == QUEST_STATUS_INCOMPLETE)
+ {
+ Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
+ if(!qinfo)
+ continue;
+
+ // hide quest if player is in raid-group and quest is no raid quest
+ if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
+ continue;
+
+ // There should be no mixed ReqItem/ReqSource drop
+ // This part for ReqItem drop
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ if(itemid == qinfo->ReqItemId[j] && q_status.m_itemcount[j] < qinfo->ReqItemCount[j] )
+ return true;
+ }
+ // This part - for ReqSource
+ for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++)
+ {
+ // examined item is a source item
+ if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT)
+ {
+ uint32 idx = qinfo->ReqSourceRef[j]-1;
+
+ // total count of created ReqItems and SourceItems is less than ReqItemCount
+ if(qinfo->ReqItemId[idx] != 0 &&
+ q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j])
+ return true;
+
+ // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount
+ if (qinfo->ReqCreatureOrGOId[idx] != 0)
+ {
+ if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j])
+ return true;
+ }
+ // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case
+ else if(qinfo->ReqSpell[idx] != 0)
+ {
+ // not casted and need more reagents/item for use.
+ if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void Player::SendQuestComplete( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTUPDATE_COMPLETE, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id );
+ }
+}
+
+void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGiver )
+{
+ uint32 questid = pQuest->GetQuestId();
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid );
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4+4+pQuest->GetRewItemsCount()*8) );
+ data << questid;
+ data << uint32(0x03);
+
+ if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ data << XP;
+ data << uint32(pQuest->GetRewOrReqMoney());
+ }
+ else
+ {
+ data << uint32(0);
+ data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)));
+ }
+ data << uint32(0); // new 2.3.0, HonorPoints?
+ data << uint32( pQuest->GetRewItemsCount() ); // max is 5
+
+ for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if ( pQuest->RewItemId[i] > 0 )
+ data << pQuest->RewItemId[i] << pQuest->RewItemCount[i];
+ else
+ data << uint32(0) << uint32(0);
+ }
+ GetSession()->SendPacket( &data );
+
+ if (pQuest->GetQuestCompleteScript() != 0)
+ sWorld.ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this);
+}
+
+void Player::SendQuestFailed( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
+ }
+}
+
+void Player::SendQuestTimerFailed( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTUPDATE_FAILEDTIMER, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
+ }
+}
+
+void Player::SendCanTakeQuestResponse( uint32 msg )
+{
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_INVALID, 4 );
+ data << uint32(msg);
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
+}
+
+void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
+{
+ if( pPlayer )
+ {
+ WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) );
+ data << uint64(pPlayer->GetGUID());
+ data << uint8(msg); // valid values: 0-8
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent MSG_QUEST_PUSH_RESULT");
+ }
+}
+
+void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count )
+{
+ WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, (4+4) );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" );
+ data << pQuest->ReqItemId[item_idx];
+ data << count;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count )
+{
+ assert(old_count + add_count < 256 && "mob/GO count store in 8 bits 2^8 = 256 (0..256)");
+
+ int32 entry = pQuest->ReqCreatureOrGOId[ creatureOrGO_idx ];
+ if (entry < 0)
+ // client expected gameobject template id in form (id|0x80000000)
+ entry = (-entry) | 0x80000000;
+
+ WorldPacket data( SMSG_QUESTUPDATE_ADD_KILL, (4*4+8) );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL" );
+ data << uint32(pQuest->GetQuestId());
+ data << uint32(entry);
+ data << uint32(old_count + add_count);
+ data << uint32(pQuest->ReqCreatureOrGOCount[ creatureOrGO_idx ]);
+ data << uint64(guid);
+ GetSession()->SendPacket(&data);
+
+ uint16 log_slot = FindQuestSlot( pQuest->GetQuestId() );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ SetQuestSlotCounter(log_slot,creatureOrGO_idx,GetQuestSlotCounter(log_slot,creatureOrGO_idx)+add_count);
+}
+
+/*********************************************************/
+/*** LOAD SYSTEM ***/
+/*********************************************************/
+
+bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
+{
+ bool delete_result = true;
+ if(!result)
+ {
+ // 0 1 2 3 4 5 6 7 8
+ result = CharacterDatabase.PQuery("SELECT data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login FROM characters WHERE guid = '%u'",guid);
+ if(!result) return false;
+ }
+ else delete_result = false;
+
+ Field *fields = result->Fetch();
+
+ if(!LoadValues( fields[0].GetString()))
+ {
+ sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
+ if(delete_result) delete result;
+ return false;
+ }
+
+ // overwrite possible wrong/corrupted guid
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ m_name = fields[1].GetCppString();
+
+ Relocate(fields[2].GetFloat(),fields[3].GetFloat(),fields[4].GetFloat());
+ SetMapId(fields[5].GetUInt32());
+ // the instance id is not needed at character enum
+
+ m_Played_time[0] = fields[6].GetUInt32();
+ m_Played_time[1] = fields[7].GetUInt32();
+
+ m_atLoginFlags = fields[8].GetUInt32();
+
+ // I don't see these used anywhere ..
+ /*_LoadGroup();
+
+ _LoadBoundInstances();*/
+
+ if (delete_result) delete result;
+
+ for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
+ m_items[i] = NULL;
+
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
+ m_deathState = DEAD;
+
+ return true;
+}
+
+void Player::_LoadDeclinedNames(QueryResult* result)
+{
+ if(!result)
+ return;
+
+ if(m_declinedname)
+ delete m_declinedname;
+
+ m_declinedname = new DeclinedName;
+ Field *fields = result->Fetch();
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ m_declinedname->name[i] = fields[i].GetCppString();
+
+ delete result;
+}
+
+bool Player::LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid)
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'",GUID_LOPART(guid));
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ x = fields[0].GetFloat();
+ y = fields[1].GetFloat();
+ z = fields[2].GetFloat();
+ o = fields[3].GetFloat();
+ mapid = fields[4].GetUInt32();
+ in_flight = !fields[5].GetCppString().empty();
+
+ delete result;
+ return true;
+}
+
+bool Player::LoadValuesArrayFromDB(Tokens& data, uint64 guid)
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT data FROM characters WHERE guid='%u'",GUID_LOPART(guid));
+ if( !result )
+ return false;
+
+ Field *fields = result->Fetch();
+
+ data = StrSplit(fields[0].GetCppString(), " ");
+
+ delete result;
+
+ return true;
+}
+
+uint32 Player::GetUInt32ValueFromArray(Tokens const& data, uint16 index)
+{
+ if(index >= data.size())
+ return 0;
+
+ return (uint32)atoi(data[index].c_str());
+}
+
+float Player::GetFloatValueFromArray(Tokens const& data, uint16 index)
+{
+ float result;
+ uint32 temp = Player::GetUInt32ValueFromArray(data,index);
+ memcpy(&result, &temp, sizeof(result));
+
+ return result;
+}
+
+uint32 Player::GetUInt32ValueFromDB(uint16 index, uint64 guid)
+{
+ Tokens data;
+ if(!LoadValuesArrayFromDB(data,guid))
+ return 0;
+
+ return GetUInt32ValueFromArray(data,index);
+}
+
+float Player::GetFloatValueFromDB(uint16 index, uint64 guid)
+{
+ float result;
+ uint32 temp = Player::GetUInt32ValueFromDB(index, guid);
+ memcpy(&result, &temp, sizeof(result));
+
+ return result;
+}
+
+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
+ //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, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", guid);
+ QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
+
+ if(!result)
+ {
+ sLog.outError("ERROR: Player (GUID: %u) not found in table `characters`, can't load. ",guid);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+
+ uint32 dbAccountId = fields[1].GetUInt32();
+
+ // check if the character's account in the db and the logged in account match.
+ // player should be able to load/delete character only with correct account!
+ if( dbAccountId != GetSession()->GetAccountId() )
+ {
+ sLog.outError("ERROR: Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId);
+ delete result;
+ return false;
+ }
+
+ Object::_Create( guid, 0, HIGHGUID_PLAYER );
+
+ m_name = fields[3].GetCppString();
+
+ // check name limitations
+ if(!ObjectMgr::IsValidName(m_name) || 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);
+ return false;
+ }
+
+ if(!LoadValues( fields[2].GetString()))
+ {
+ sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
+ delete result;
+ return false;
+ }
+
+ // overwrite possible wrong/corrupted guid
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
+ for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), 0 );
+ SetVisibleItemSlot(slot,NULL);
+
+ if (m_items[slot])
+ {
+ delete m_items[slot];
+ m_items[slot] = NULL;
+ }
+ }
+
+ // update money limits
+ if(GetMoney() > MAX_MONEY_AMOUNT)
+ SetMoney(MAX_MONEY_AMOUNT);
+
+ 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)
+ //Other way is to saves m_team into characters table.
+ setFactionForRace(m_race);
+ SetCharm(0);
+
+ m_class = fields[5].GetUInt8();
+
+ PlayerInfo const *info = objmgr.GetPlayerInfo(m_race, m_class);
+ if(!info)
+ {
+ sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
+ delete result;
+ return false;
+ }
+
+ InitPrimaryProffesions(); // to max set before any spell loaded
+
+ uint32 transGUID = fields[24].GetUInt32();
+ Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat());
+ SetMapId(fields[9].GetUInt32());
+ SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup
+
+ _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
+
+ // check arena teams integrity
+ for(uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
+ {
+ uint32 arena_team_id = GetArenaTeamId(arena_slot);
+ if(!arena_team_id)
+ continue;
+
+ if(ArenaTeam * at = objmgr.GetArenaTeamById(arena_team_id))
+ if(at->HaveMember(GetGUID()))
+ continue;
+
+ // arena team not exist or not member, cleanup fields
+ for(int j =0; j < 6; ++j)
+ SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arena_slot * 6 + j, 0);
+ }
+
+ _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
+
+ if(!IsPositionValid())
+ {
+ 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());
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
+
+ transGUID = 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;
+ }
+
+ // load the player's map here if it's not already loaded
+ Map *map = GetMap();
+ // since the player may not be bound to the map yet, make sure subsequent
+ // getmap calls won't create new maps
+ SetInstanceId(map->GetInstanceId());
+
+ SaveRecallPosition();
+
+ 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();
+
+ if( !MaNGOS::IsValidMapCoord(
+ GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
+ GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o) ||
+ // transport size limited
+ m_movementInfo.t_x > 50 || m_movementInfo.t_y > 50 || m_movementInfo.t_z > 50 )
+ {
+ sLog.outError("ERROR: Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
+ guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
+ GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o);
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,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;
+ }
+ }
+
+ if (transGUID != 0)
+ {
+ for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter)
+ {
+ if( (*iter)->GetGUIDLow() == transGUID)
+ {
+ m_transport = *iter;
+ m_transport->AddPassenger(this);
+ SetMapId(m_transport->GetMapId());
+ break;
+ }
+ }
+
+ if(!m_transport)
+ {
+ sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.",
+ guid,transGUID);
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,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;
+ }
+ }
+
+ time_t now = time(NULL);
+ time_t logoutTime = time_t(fields[16].GetUInt64());
+
+ // since last logout (in seconds)
+ uint64 time_diff = uint64(now - logoutTime);
+
+ // set value, including drunk invisibility detection
+ // calculate sobering. after 15 minutes logged out, the player will be sober again
+ float soberFactor;
+ if(time_diff > 15*MINUTE)
+ soberFactor = 0;
+ else
+ soberFactor = 1-time_diff/(15.0f*MINUTE);
+ uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
+ SetDrunkValue(newDrunkenValue);
+
+ m_rest_bonus = fields[15].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)
+ {
+ float bubble = fields[17].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_resetTalentsCost = fields[18].GetUInt32();
+ m_resetTalentsTime = time_t(fields[19].GetUInt64());
+
+ // reserve some flags
+ uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM );
+
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) )
+ SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags);
+
+ m_taxi.LoadTaxiMask( fields[11].GetString() ); // must be before InitTaxiNodesForLevel
+
+ uint32 gmstate = fields[25].GetUInt32();
+
+ m_stableSlots = fields[26].GetUInt32();
+ if(m_stableSlots > 2)
+ {
+ sLog.outError("Player can have not more 2 stable slots, but have in DB %u",uint32(m_stableSlots));
+ m_stableSlots = 2;
+ }
+
+ m_atLoginFlags = fields[27].GetUInt32();
+
+ // Honor system
+ // Update Honor kills data
+ m_lastHonorUpdateTime = logoutTime;
+ UpdateHonorFields();
+
+ m_deathExpireTime = (time_t)fields[30].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)
+ SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0);
+ SetUInt32Value(UNIT_CHANNEL_SPELL,0);
+
+ // clear charm/summon related fields
+ SetUInt64Value(UNIT_FIELD_CHARM,0);
+ SetUInt64Value(UNIT_FIELD_SUMMON,0);
+ SetUInt64Value(UNIT_FIELD_CHARMEDBY,0);
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY,0);
+ SetUInt64Value(UNIT_FIELD_CREATEDBY,0);
+
+ // reset some aura modifiers before aura apply
+ SetUInt64Value(PLAYER_FARSIGHT, 0);
+ SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );
+ SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 );
+
+ // reset skill modifiers and set correct unlearn flags
+ for (uint32 i = 0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // set correct unlearn bit
+ uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+ if(!id) continue;
+
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
+ if(!pSkill) continue;
+
+ // enable unlearn button for primary professions only
+ if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
+ else
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
+ }
+
+ // make sure the unit is considered out of combat for proper loading
+ ClearInCombat();
+
+ // make sure the unit is considered not in duel for proper loading
+ SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+
+ // remember loaded power/health values to restore after stats initialization and modifier applying
+ uint32 savedHealth = GetHealth();
+ uint32 savedPower[MAX_POWERS];
+ for(uint32 i = 0; i < MAX_POWERS; ++i)
+ savedPower[i] = GetPower(Powers(i));
+
+ // reset stats before loading any modifiers
+ InitStatsForLevel();
+ InitTaxiNodesForLevel();
+
+ // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
+
+ //mails are loaded only when needed ;-) - when player in game click on mailbox.
+ //_LoadMail();
+
+ _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
+
+ // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
+ m_deathState = DEAD;
+
+ _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
+
+ // after spell load
+ InitTalentForLevel();
+ learnSkillRewardedSpells();
+
+ // after spell load, learn rewarded spell if need also
+ _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
+ _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
+
+ _LoadTutorials(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTUTORIALS));
+
+ // must be before inventory (some items required reputation check)
+ _LoadReputation(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
+
+ _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
+
+ // update items with duration and realtime
+ UpdateItemDuration(time_diff, true);
+
+ _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
+
+ // unread mails and next delivery time, actual mails not loaded
+ _LoadMailInit(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));
+
+ m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow());
+
+ if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
+ return false;
+
+ // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
+ // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
+ if(uint32 curTitle = GetUInt32Value(PLAYER_CHOSEN_TITLE))
+ {
+ if(!HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << curTitle))
+ SetUInt32Value(PLAYER_CHOSEN_TITLE,0);
+ }
+
+ // Not finish taxi flight path
+ if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes))
+ {
+ // 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());
+ SetMapId(m_homebindMapId);
+ Relocate( m_homebindX, m_homebindY, m_homebindZ,0.0f);
+ 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
+ }
+
+ _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));
+
+ // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
+ // Do now before stats re-calculation cleanup for ghost state unexpected auras
+ if(!isAlive())
+ RemoveAllAurasOnDeath();
+
+ //apply all stat bonuses from items and auras
+ SetCanModifyStats(true);
+ UpdateAllStats();
+
+ // restore remembered power/health values (but not more max values)
+ SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
+ for(uint32 i = 0; i < MAX_POWERS; ++i)
+ SetPower(Powers(i),savedPower[i] > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedPower[i]);
+
+ sLog.outDebug("The value of player %s after load item and aura is: ", m_name.c_str());
+ outDebugValues();
+
+ // GM state
+ if(GetSession()->GetSecurity() > SEC_PLAYER)
+ {
+ switch(sWorld.getConfig(CONFIG_GM_LOGIN_STATE))
+ {
+ case 0: // disable
+ break;
+ case 1: // enable
+ SetGameMaster(true);
+ break;
+ case 2: // save state
+ if(gmstate)
+ SetGameMaster(true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ //Unmount Player from previous mount, so speed bug with mount is no more...
+ if(IsMounted())
+ {
+ Unmount();
+ RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ }
+
+ _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
+
+ return true;
+}
+
+bool Player::isAllowedToLoot(Creature* creature)
+{
+ if(Player* recipient = creature->GetLootRecipient())
+ {
+ if (recipient == this)
+ return true;
+ if( Group* otherGroup = recipient->GetGroup())
+ {
+ Group* thisGroup = GetGroup();
+ if(!thisGroup)
+ return false;
+ return thisGroup == otherGroup;
+ }
+ return false;
+ }
+ else
+ // prevent other players from looting if the recipient got disconnected
+ return !creature->hasLootRecipient();
+}
+
+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());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint8 button = fields[0].GetUInt8();
+
+ addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8());
+
+ m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED;
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadAuras(QueryResult *result, uint32 timediff)
+{
+ m_Auras.clear();
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ m_modAuras[i].clear();
+
+ // all aura related fields
+ for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
+ SetUInt32Value(i, 0);
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint64 caster_guid = fields[0].GetUInt64();
+ uint32 spellid = fields[1].GetUInt32();
+ uint32 effindex = fields[2].GetUInt32();
+ int32 damage = (int32)fields[3].GetUInt32();
+ int32 maxduration = (int32)fields[4].GetUInt32();
+ int32 remaintime = (int32)fields[5].GetUInt32();
+ int32 remaincharges = (int32)fields[6].GetUInt32();
+
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
+ if(!spellproto)
+ {
+ sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ if(effindex >= 3)
+ {
+ sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ // negative effects should continue counting down after logout
+ if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
+ {
+ if(remaintime <= int32(timediff))
+ continue;
+
+ remaintime -= timediff;
+ }
+
+ // prevent wrong values of remaincharges
+ if(spellproto->procCharges)
+ {
+ if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
+ remaincharges = spellproto->procCharges;
+ }
+ else
+ remaincharges = -1;
+
+ //do not load single target auras (unless they were cast by the player)
+ if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto))
+ continue;
+
+ Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
+ if(!damage)
+ damage = aura->GetModifier()->m_amount;
+ aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
+ AddAura(aura);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ if(m_class == CLASS_WARRIOR)
+ CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
+}
+
+void Player::LoadCorpse()
+{
+ if( isAlive() )
+ {
+ ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
+ }
+ else
+ {
+ if(Corpse *corpse = GetCorpse())
+ {
+ ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable() );
+ }
+ else
+ {
+ //Prevent Dead Player login without corpse
+ ResurrectPlayer(0.5f);
+ }
+ }
+}
+
+void Player::_LoadInventory(QueryResult *result, uint32 timediff)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("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", GetGUIDLow());
+ std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
+ //NOTE: the "order by `bag`" is important because it makes sure
+ //the bagMap is filled before items in the bags are loaded
+ //NOTE2: the "order by `slot`" is needed becaue mainhand weapons are (wrongly?)
+ //expected to be equipped before offhand items (TODO: fixme)
+
+ uint32 zone = GetZoneId();
+
+ if (result)
+ {
+ std::list<Item*> problematicItems;
+
+ // prevent items from being added to the queue when stored
+ m_itemUpdateQueueBlocked = true;
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 bag_guid = fields[1].GetUInt32();
+ uint8 slot = fields[2].GetUInt8();
+ uint32 item_guid = fields[3].GetUInt32();
+ uint32 item_id = fields[4].GetUInt32();
+
+ ItemPrototype const * proto = objmgr.GetItemPrototype(item_id);
+
+ if(!proto)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid);
+ sLog.outError( "Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(),item_id );
+ continue;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid, GetGUID(), result))
+ {
+ sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id );
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ // not allow have in alive state item limited to another map/zone
+ if(isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(),zone) )
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ // "Conjured items disappear if you are logged out for more than 15 minutes"
+ if ((timediff > 15*60) && (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED)))
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ bool success = true;
+
+ if (!bag_guid)
+ {
+ // the item is not in a bag
+ item->SetContainer( NULL );
+ item->SetSlot(slot);
+
+ if( IsInventoryPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false ) == EQUIP_ERR_OK )
+ item = StoreItem(dest, item, true);
+ else
+ success = false;
+ }
+ else if( IsEquipmentPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ uint16 dest;
+ if( CanEquipItem( slot, dest, item, false, false ) == EQUIP_ERR_OK )
+ QuickEquipItem(dest, item);
+ else
+ success = false;
+ }
+ else if( IsBankPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ ItemPosCountVec dest;
+ if( CanBankItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false, false ) == EQUIP_ERR_OK )
+ item = BankItem(dest, item, true);
+ else
+ success = false;
+ }
+
+ if(success)
+ {
+ // store bags that may contain items in them
+ if(item->IsBag() && IsBagPos(item->GetPos()))
+ bagMap[item_guid] = (Bag*)item;
+ }
+ }
+ else
+ {
+ item->SetSlot(NULL_SLOT);
+ // the item is in a bag, find the bag
+ std::map<uint64, Bag*>::iterator itr = bagMap.find(bag_guid);
+ if(itr != bagMap.end())
+ itr->second->StoreItem(slot, item, true );
+ else
+ success = false;
+ }
+
+ // item's state may have changed after stored
+ if (success)
+ item->SetState(ITEM_UNCHANGED, this);
+ else
+ {
+ sLog.outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(),item_guid, item_id, bag_guid, slot);
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ problematicItems.push_back(item);
+ }
+ } while (result->NextRow());
+
+ delete result;
+ m_itemUpdateQueueBlocked = false;
+
+ // send by mail problematic items
+ while(!problematicItems.empty())
+ {
+ // fill mail
+ MailItemsInfo mi; // item list prepering
+
+ for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
+ {
+ Item* item = problematicItems.front();
+ problematicItems.pop_front();
+
+ mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ }
+
+ std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
+
+ WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ }
+ }
+ //if(isAlive())
+ _ApplyAllItemMods();
+}
+
+// load mailed item which should receive current player
+void Player::_LoadMailedItems(Mail *mail)
+{
+ QueryResult* result = CharacterDatabase.PQuery("SELECT item_guid, item_template FROM mail_items WHERE mail_id='%u'", mail->messageID);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid_low = fields[0].GetUInt32();
+ uint32 item_template = fields[1].GetUInt32();
+
+ mail->AddItem(item_guid_low, item_template);
+
+ ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
+
+ 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);
+ 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;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid_low, 0))
+ {
+ sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ AddMItem(item);
+ } while (result->NextRow());
+
+ delete result;
+}
+
+void Player::_LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery)
+{
+ //set a count of unread mails
+ //QueryResult *resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(playerGuid),(uint64)cTime);
+ if (resultUnread)
+ {
+ Field *fieldMail = resultUnread->Fetch();
+ unReadMails = fieldMail[0].GetUInt8();
+ delete resultUnread;
+ }
+
+ // store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
+ //resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
+ if (resultDelivery)
+ {
+ Field *fieldMail = resultDelivery->Fetch();
+ m_nextMailDelivereTime = (time_t)fieldMail[0].GetUInt64();
+ delete resultDelivery;
+ }
+}
+
+void Player::_LoadMail()
+{
+ m_mail.clear();
+ //mails are in right order 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC",GetGUIDLow());
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ Mail *m = new Mail;
+ m->messageID = fields[0].GetUInt32();
+ m->messageType = fields[1].GetUInt8();
+ m->sender = fields[2].GetUInt32();
+ m->receiver = fields[3].GetUInt32();
+ m->subject = fields[4].GetCppString();
+ m->itemTextId = fields[5].GetUInt32();
+ bool has_items = fields[6].GetBool();
+ m->expire_time = (time_t)fields[7].GetUInt64();
+ m->deliver_time = (time_t)fields[8].GetUInt64();
+ m->money = fields[9].GetUInt32();
+ m->COD = fields[10].GetUInt32();
+ m->checked = fields[11].GetUInt32();
+ m->stationery = fields[12].GetUInt8();
+ m->mailTemplateId = fields[13].GetInt16();
+
+ if(m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
+ {
+ sLog.outError( "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
+ m->mailTemplateId = 0;
+ }
+
+ m->state = MAIL_STATE_UNCHANGED;
+
+ if (has_items)
+ _LoadMailedItems(m);
+
+ m_mail.push_back(m);
+ } while( result->NextRow() );
+ delete result;
+ }
+ m_mailsLoaded = true;
+}
+
+void Player::LoadPet()
+{
+ //fixme: the pet should still be loaded if the player is not in world
+ // just not added to the map
+ if(IsInWorld())
+ {
+ Pet *pet = new Pet;
+ if(!pet->LoadPetFromDB(this,0,0,true))
+ delete pet;
+ }
+}
+
+void Player::_LoadQuestStatus(QueryResult *result)
+{
+ mQuestStatus.clear();
+
+ uint32 slot = 0;
+
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 quest_id = fields[0].GetUInt32();
+ // used to be new, no delete?
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( pQuest )
+ {
+ // find or create
+ QuestStatusData& questStatusData = mQuestStatus[quest_id];
+
+ uint32 qstatus = fields[1].GetUInt32();
+ if(qstatus < MAX_QUEST_STATUS)
+ questStatusData.m_status = QuestStatus(qstatus);
+ else
+ {
+ questStatusData.m_status = QUEST_STATUS_NONE;
+ sLog.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).",GetName(),quest_id,qstatus);
+ }
+
+ questStatusData.m_rewarded = ( fields[2].GetUInt8() > 0 );
+ questStatusData.m_explored = ( fields[3].GetUInt8() > 0 );
+
+ time_t quest_time = time_t(fields[4].GetUInt64());
+
+ if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE )
+ {
+ AddTimedQuest( quest_id );
+
+ if (quest_time <= sWorld.GetGameTime())
+ questStatusData.m_timer = 1;
+ else
+ questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000;
+ }
+ else
+ quest_time = 0;
+
+ questStatusData.m_creatureOrGOcount[0] = fields[5].GetUInt32();
+ questStatusData.m_creatureOrGOcount[1] = fields[6].GetUInt32();
+ questStatusData.m_creatureOrGOcount[2] = fields[7].GetUInt32();
+ questStatusData.m_creatureOrGOcount[3] = fields[8].GetUInt32();
+ questStatusData.m_itemcount[0] = fields[9].GetUInt32();
+ questStatusData.m_itemcount[1] = fields[10].GetUInt32();
+ questStatusData.m_itemcount[2] = fields[11].GetUInt32();
+ questStatusData.m_itemcount[3] = fields[12].GetUInt32();
+
+ questStatusData.uState = QUEST_UNCHANGED;
+
+ // add to quest log
+ if( slot < MAX_QUEST_LOG_SIZE &&
+ ( questStatusData.m_status==QUEST_STATUS_INCOMPLETE ||
+ questStatusData.m_status==QUEST_STATUS_COMPLETE && !questStatusData.m_rewarded ) )
+ {
+ SetQuestSlot(slot,quest_id,quest_time);
+
+ if(questStatusData.m_status == QUEST_STATUS_COMPLETE)
+ SetQuestSlotState(slot,QUEST_STATE_COMPLETE);
+
+ for(uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
+ if(questStatusData.m_creatureOrGOcount[idx])
+ SetQuestSlotCounter(slot,idx,questStatusData.m_creatureOrGOcount[idx]);
+
+ ++slot;
+ }
+
+ if(questStatusData.m_rewarded)
+ {
+ // learn rewarded spell if unknown
+ learnQuestRewardedSpells(pQuest);
+
+ // set rewarded title if any
+ if(pQuest->GetCharTitleId())
+ {
+ if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
+ SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
+ }
+ }
+
+ sLog.outDebug("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow());
+ }
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ // clear quest log tail
+ for ( uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i )
+ SetQuestSlot(i,0);
+}
+
+void Player::_LoadDailyQuestStatus(QueryResult *result)
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
+
+ if(result)
+ {
+ uint32 quest_daily_idx = 0;
+
+ do
+ {
+ if(quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
+ {
+ sLog.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`",GetGUIDLow());
+ break;
+ }
+
+ Field *fields = result->Fetch();
+
+ uint32 quest_id = fields[0].GetUInt32();
+
+ // save _any_ from daily quest times (it must be after last reset anyway)
+ m_lastDailyQuestTime = (time_t)fields[1].GetUInt64();
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( !pQuest )
+ continue;
+
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
+ ++quest_daily_idx;
+
+ sLog.outDebug("Daily quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ m_DailyQuestChanged = false;
+}
+
+void Player::_LoadReputation(QueryResult *result)
+{
+ m_factions.clear();
+
+ // Set initial reputations (so everything is nifty before DB data load)
+ SetInitialFactions();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32());
+ if( factionEntry && (factionEntry->reputationListID >= 0))
+ {
+ FactionState* faction = &m_factions[factionEntry->reputationListID];
+
+ // update standing to current
+ faction->Standing = int32(fields[1].GetUInt32());
+
+ uint32 dbFactionFlags = fields[2].GetUInt32();
+
+ if( dbFactionFlags & FACTION_FLAG_VISIBLE )
+ SetFactionVisible(faction); // have internal checks for forced invisibility
+
+ if( dbFactionFlags & FACTION_FLAG_INACTIVE)
+ SetFactionInactive(faction,true); // have internal checks for visibility requirement
+
+ if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war
+ SetFactionAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED
+ else // DB not at war
+ {
+ // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
+ if( faction->Flags & FACTION_FLAG_VISIBLE )
+ SetFactionAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED
+ }
+
+ // set atWar for hostile
+ if(GetReputationRank(factionEntry) <= REP_HOSTILE)
+ SetFactionAtWar(faction,true);
+
+ // reset changed flag if values similar to saved in DB
+ if(faction->Flags==dbFactionFlags)
+ faction->Changed = false;
+ }
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadSpells(QueryResult *result)
+{
+ for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ delete itr->second;
+ m_spells.clear();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM character_spell WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ addSpell(fields[0].GetUInt16(), fields[2].GetBool(), false, true, fields[1].GetUInt16(), fields[3].GetBool());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadTutorials(QueryResult *result)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmid);
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ for (int iI=0; iI<8; iI++)
+ m_Tutorials[iI] = fields[iI].GetUInt32();
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ m_TutorialsChanged = false;
+}
+
+void Player::_LoadGroup(QueryResult *result)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
+ if(result)
+ {
+ uint64 leaderGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete result;
+ Group* group = objmgr.GetGroupByLeader(leaderGuid);
+ if(group)
+ {
+ uint8 subgroup = group->GetMemberGroup(GetGUID());
+ SetGroup(group, subgroup);
+ if(getLevel() >= LEVELREQUIREMENT_HEROIC)
+ {
+ // the group leader may change the instance difficulty while the player is offline
+ SetDifficulty(group->GetDifficulty());
+ }
+ }
+ }
+}
+
+void Player::_LoadBoundInstances(QueryResult *result)
+{
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ m_boundInstances[i].clear();
+
+ Group *group = GetGroup();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ bool perm = fields[1].GetBool();
+ uint32 mapId = fields[2].GetUInt32();
+ uint32 instanceId = fields[0].GetUInt32();
+ uint8 difficulty = fields[3].GetUInt8();
+ time_t resetTime = (time_t)fields[4].GetUInt64();
+ // the resettime for normal instances is only saved when the InstanceSave is unloaded
+ // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
+ // and in that case it is not used
+
+ if(!perm && group)
+ {
+ sLog.outError("_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d,%d,%d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetLeaderGUID()), mapId, instanceId, difficulty);
+ CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
+ continue;
+ }
+
+ // since non permanent binds are always solo bind, they can always be reset
+ InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapId, instanceId, difficulty, resetTime, !perm, true);
+ if(save) BindToInstance(save, perm, true);
+ } while(result->NextRow());
+ delete result;
+ }
+}
+
+InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, uint8 difficulty)
+{
+ // some instances only have one difficulty
+ const MapEntry* entry = sMapStore.LookupEntry(mapid);
+ if(!entry || !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL;
+
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ if(itr != m_boundInstances[difficulty].end())
+ return &itr->second;
+ else
+ return NULL;
+}
+
+void Player::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
+{
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ UnbindInstance(itr, difficulty, unload);
+}
+
+void Player::UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload)
+{
+ if(itr != m_boundInstances[difficulty].end())
+ {
+ if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId());
+ itr->second.save->RemovePlayer(this); // save can become invalid
+ m_boundInstances[difficulty].erase(itr++);
+ }
+}
+
+InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
+{
+ if(save)
+ {
+ InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
+ if(bind.save)
+ {
+ // update the save when the group kills a boss
+ if(permanent != bind.perm || save != bind.save)
+ if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId());
+ }
+ else
+ if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent);
+
+ if(bind.save != save)
+ {
+ if(bind.save) bind.save->RemovePlayer(this);
+ save->AddPlayer(this);
+ }
+
+ if(permanent) save->SetCanReset(false);
+
+ bind.save = save;
+ bind.perm = permanent;
+ if(!load) sLog.outDebug("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
+ return &bind;
+ }
+ else
+ return NULL;
+}
+
+void Player::SendRaidInfo()
+{
+ WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
+
+ uint32 counter = 0, i;
+ for(i = 0; i < TOTAL_DIFFICULTIES; i++)
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
+ if(itr->second.perm) counter++;
+
+ data << counter;
+ for(i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
+ {
+ if(itr->second.perm)
+ {
+ InstanceSave *save = itr->second.save;
+ data << (save->GetMapId());
+ data << (uint32)(save->GetResetTime() - time(NULL));
+ data << save->GetInstanceId();
+ data << uint32(counter);
+ counter--;
+ }
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+/*
+- called on every successful teleportation to a map
+*/
+void Player::SendSavedInstances()
+{
+ bool hasBeenSaved = false;
+ WorldPacket data;
+
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ {
+ if(itr->second.perm) // only permanent binds are sent
+ {
+ hasBeenSaved = true;
+ break;
+ }
+ }
+ }
+
+ //Send opcode 811. true or flase means, whether you have current raid/heroic instances
+ data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
+ data << uint32(hasBeenSaved);
+ GetSession()->SendPacket(&data);
+
+ if(!hasBeenSaved)
+ return;
+
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ {
+ if(itr->second.perm)
+ {
+ data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
+ data << uint32(itr->second.save->GetMapId());
+ GetSession()->SendPacket(&data);
+ }
+ }
+ }
+}
+
+/// convert the player's binds to the group
+void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player_guid)
+{
+ bool has_binds = false;
+ bool has_solo = false;
+
+ if(player) { player_guid = player->GetGUID(); if(!group) group = player->GetGroup(); }
+ assert(player_guid);
+
+ // copy all binds to the group, when changing leader it's assumed the character
+ // will not have any solo binds
+
+ if(player)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
+ {
+ has_binds = true;
+ if(group) group->BindToInstance(itr->second.save, itr->second.perm, true);
+ // permanent binds are not removed
+ if(!itr->second.perm)
+ {
+ player->UnbindInstance(itr, i, true); // increments itr
+ has_solo = true;
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ // if the player's not online we don't know what binds it has
+ if(!player || !group || has_binds) CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", GUID_LOPART(player_guid));
+ // the following should not get executed when changing leaders
+ if(!player || has_solo) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0", GUID_LOPART(player_guid));
+}
+
+bool Player::_LoadHomeBind(QueryResult *result)
+{
+ bool ok = false;
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
+ if (result)
+ {
+ Field *fields = result->Fetch();
+ m_homebindMapId = fields[0].GetUInt32();
+ m_homebindZoneId = fields[1].GetUInt16();
+ m_homebindX = fields[2].GetFloat();
+ m_homebindY = fields[3].GetFloat();
+ m_homebindZ = fields[4].GetFloat();
+ delete result;
+
+ // accept saved data only for valid position (and non instanceable)
+ if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
+ !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() )
+ {
+ ok = true;
+ }
+ else
+ CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
+ }
+
+ if(!ok)
+ {
+ PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
+ if(!info) return false;
+
+ m_homebindMapId = info->mapId;
+ m_homebindZoneId = info->zoneId;
+ m_homebindX = info->positionX;
+ m_homebindY = info->positionY;
+ m_homebindZ = info->positionZ;
+
+ CharacterDatabase.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId, (uint32)m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
+ }
+
+ DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f\n",
+ m_homebindMapId, m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
+
+ return true;
+}
+
+/*********************************************************/
+/*** SAVE SYSTEM ***/
+/*********************************************************/
+
+void Player::SaveToDB()
+{
+ // delay auto save at any saves (manual, in code, or autosave)
+ m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
+
+ // first save/honor gain after midnight will also update the player's honor fields
+ UpdateHonorFields();
+
+ // Must saved before enter into BattleGround
+ if(InBattleGround())
+ return;
+
+ int 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, 0); // stand state
+ SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); // stand flags?
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ SetDisplayId(GetNativeDisplayId());
+
+ bool inworld = IsInWorld();
+
+ CharacterDatabase.BeginTransaction();
+
+ CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow());
+
+ std::string sql_name = m_name;
+ CharacterDatabase.escape_string(sql_name);
+
+ std::ostringstream ss;
+ ss << "INSERT INTO characters (guid,account,name,race,class,"
+ "map, 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, "
+ "trans_x, trans_y, trans_z, trans_o, transguid, gmstate, stable_slots, at_login, zone, "
+ "death_expire_time, taxi_path) VALUES ("
+ << GetGUIDLow() << ", "
+ << GetSession()->GetAccountId() << ", '"
+ << sql_name << "', "
+ << m_race << ", "
+ << m_class << ", ";
+
+ bool save_to_dest = false;
+ if(IsBeingTeleported())
+ {
+ // don't save to battlegrounds or arenas
+ const MapEntry *entry = sMapStore.LookupEntry(GetTeleportDest().mapid);
+ if(entry && entry->map_type != MAP_BATTLEGROUND && entry->map_type != MAP_ARENA)
+ save_to_dest = true;
+ }
+
+ if(!save_to_dest)
+ {
+ ss << GetMapId() << ", "
+ << (uint32)GetDifficulty() << ", "
+ << finiteAlways(GetPositionX()) << ", "
+ << finiteAlways(GetPositionY()) << ", "
+ << finiteAlways(GetPositionZ()) << ", "
+ << finiteAlways(GetOrientation()) << ", '";
+ }
+ else
+ {
+ ss << GetTeleportDest().mapid << ", "
+ << (uint32)GetDifficulty() << ", "
+ << finiteAlways(GetTeleportDest().x) << ", "
+ << finiteAlways(GetTeleportDest().y) << ", "
+ << finiteAlways(GetTeleportDest().z) << ", "
+ << finiteAlways(GetTeleportDest().o) << ", '";
+ }
+
+ uint16 i;
+ for( i = 0; i < m_valuesCount; i++ )
+ {
+ ss << GetUInt32Value(i) << " ";
+ }
+
+ ss << "', '";
+
+ for( i = 0; i < 8; i++ )
+ ss << m_taxi.GetTaximask(i) << " ";
+
+ ss << "', ";
+ ss << (inworld ? 1 : 0);
+
+ ss << ", ";
+ ss << m_cinematic;
+
+ ss << ", ";
+ ss << m_Played_time[0];
+ ss << ", ";
+ ss << m_Played_time[1];
+
+ 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 << ", ";
+ 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 << ", ";
+ if (m_transport)
+ ss << m_transport->GetGUIDLow();
+ else
+ ss << "0";
+
+ ss << ", ";
+ ss << (isGameMaster()? 1 : 0);
+
+ ss << ", ";
+ ss << uint32(m_stableSlots); // to prevent save uint8 as char
+
+ ss << ", ";
+ ss << uint32(m_atLoginFlags);
+
+ ss << ", ";
+ ss << GetZoneId();
+
+ ss << ", ";
+ ss << (uint64)m_deathExpireTime;
+
+ ss << ", '";
+ ss << m_taxi.SaveTaxiDestinationsToString();
+ ss << "' )";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ if(m_mailsUpdated) //save mails only when needed
+ _SaveMail();
+
+ _SaveInventory();
+ _SaveQuestStatus();
+ _SaveDailyQuestStatus();
+ _SaveTutorials();
+ _SaveSpells();
+ _SaveSpellCooldowns();
+ _SaveActions();
+ _SaveAuras();
+ _SaveReputation();
+
+ 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);
+}
+
+// fast save function for item/money cheating preventing - save only inventory and money state
+void Player::SaveInventoryAndGoldToDB()
+{
+ _SaveInventory();
+ SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID());
+}
+
+void Player::_SaveActions()
+{
+ for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); )
+ {
+ 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 );
+ 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 );
+ itr->second.uState = ACTIONBUTTON_UNCHANGED;
+ ++itr;
+ break;
+ case ACTIONBUTTON_DELETED:
+ CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' and button = '%u'", GetGUIDLow(), (uint32)itr->first );
+ m_actionButtons.erase(itr++);
+ break;
+ default:
+ ++itr;
+ break;
+ };
+ }
+}
+
+void Player::_SaveAuras()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow());
+
+ AuraMap const& auras = GetAuras();
+ for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = itr->second->GetSpellProto();
+
+ //skip all auras from spells that are passive or need a shapeshift
+ if (itr->second->IsPassive() || itr->second->IsRemovedOnShapeLost())
+ continue;
+
+ //do not save single target auras (unless they were cast by the player)
+ if (itr->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))
+ continue;
+
+ uint8 i;
+ // or apply at cast SPELL_AURA_MOD_SHAPESHIFT or SPELL_AURA_MOD_STEALTH auras
+ for (i = 0; i < 3; i++)
+ if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT ||
+ spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH)
+ break;
+
+ if (i == 3)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u' and spell = '%u' and effect_index= '%u'",GetGUIDLow(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex());
+ CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
+ "VALUES ('%u', '" I64FMTD "' ,'%u', '%u', '%d', '%d', '%d', '%d')",
+ GetGUIDLow(), itr->second->GetCasterGUID(), (uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(), (*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
+ }
+ }
+}
+
+void Player::_SaveInventory()
+{
+ // force items in buyback slots to new state
+ // and remove those that aren't already
+ for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; i++)
+ {
+ Item *item = m_items[i];
+ if (!item || item->GetState() == ITEM_NEW) continue;
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item->GetGUIDLow());
+ m_items[i]->FSetState(ITEM_NEW);
+ }
+
+ // update enchantment durations
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
+ }
+
+ // if no changes
+ if (m_itemUpdateQueue.empty()) return;
+
+ // do not save if the update queue is corrupt
+ bool error = false;
+ for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
+ {
+ Item *item = m_itemUpdateQueue[i];
+ if(!item || item->GetState() == ITEM_REMOVED) continue;
+ Item *test = GetItemByPos( item->GetBagSlot(), item->GetSlot());
+
+ if (test == NULL)
+ {
+ sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - 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!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow());
+ error = true;
+ }
+ else if (test != item)
+ {
+ sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow());
+ error = true;
+ }
+ }
+
+ if (error)
+ {
+ sLog.outError("Player::_SaveInventory - one or more errors occurred save aborted!");
+ ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED);
+ return;
+ }
+
+ for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
+ {
+ Item *item = m_itemUpdateQueue[i];
+ if(!item) continue;
+
+ Bag *container = item->GetContainer();
+ uint32 bag_guid = container ? container->GetGUIDLow() : 0;
+
+ switch(item->GetState())
+ {
+ case ITEM_NEW:
+ CharacterDatabase.PExecute("INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES ('%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetGUIDLow(), item->GetEntry());
+ break;
+ case ITEM_CHANGED:
+ CharacterDatabase.PExecute("UPDATE character_inventory SET guid='%u', bag='%u', slot='%u', item_template='%u' WHERE item='%u'", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetEntry(), item->GetGUIDLow());
+ break;
+ case ITEM_REMOVED:
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
+ break;
+ case ITEM_UNCHANGED:
+ break;
+ }
+
+ item->SaveToDB(); // item have unchanged inventory record and can be save standalone
+ }
+ m_itemUpdateQueue.clear();
+}
+
+void Player::_SaveMail()
+{
+ if (!m_mailsLoaded)
+ return;
+
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
+ {
+ Mail *m = (*itr);
+ if (m->state == MAIL_STATE_CHANGED)
+ {
+ CharacterDatabase.PExecute("UPDATE mail SET itemTextId = '%u',has_items = '%u',expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',money = '%u',cod = '%u',checked = '%u' WHERE id = '%u'",
+ m->itemTextId, m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID);
+ if(m->removedItems.size())
+ {
+ for(std::vector<uint32>::iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", *itr2);
+ m->removedItems.clear();
+ }
+ m->state = MAIL_STATE_UNCHANGED;
+ }
+ else if (m->state == MAIL_STATE_DELETED)
+ {
+ if (m->HasItems())
+ for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
+ if (m->itemTextId)
+ CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID);
+ }
+ }
+
+ //deallocate deleted mails...
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); )
+ {
+ if ((*itr)->state == MAIL_STATE_DELETED)
+ {
+ Mail* m = *itr;
+ m_mail.erase(itr);
+ delete m;
+ itr = m_mail.begin();
+ }
+ else
+ ++itr;
+ }
+
+ m_mailsUpdated = false;
+}
+
+void Player::_SaveQuestStatus()
+{
+ // we don't need transactions here.
+ for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ switch (i->second.uState)
+ {
+ case QUEST_NEW :
+ CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
+ break;
+ case QUEST_CHANGED :
+ CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
+ i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
+ break;
+ case QUEST_UNCHANGED:
+ break;
+ };
+ i->second.uState = QUEST_UNCHANGED;
+ }
+}
+
+void Player::_SaveDailyQuestStatus()
+{
+ if(!m_DailyQuestChanged)
+ return;
+
+ m_DailyQuestChanged = false;
+
+ // save last daily quest time for all quests: we need only mostly reset time for reset check anyway
+
+ // we don't need transactions here.
+ CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",GetGUIDLow());
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ if(GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
+ CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest,time) VALUES ('%u', '%u','" I64FMTD "')",
+ GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime));
+}
+
+void Player::_SaveReputation()
+{
+ for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
+ {
+ if (itr->second.Changed)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", GetGUIDLow(), itr->second.ID);
+ CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
+ itr->second.Changed = false;
+ }
+ }
+}
+
+void Player::_SaveSpells()
+{
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
+ if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,slot,active,disabled) VALUES ('%u', '%u', '%u','%u','%u')", GetGUIDLow(), itr->first, itr->second->slotId,itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0);
+
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ _removeSpell(itr->first);
+ else
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ }
+}
+
+void Player::_SaveTutorials()
+{
+ if(!m_TutorialsChanged)
+ return;
+
+ uint32 Rows=0;
+ // it's better than rebuilding indexes multiple times
+ QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetSession()->GetAccountId(), realmID );
+ if(result)
+ {
+ Rows = result->Fetch()[0].GetUInt32();
+ delete result;
+ }
+
+ if (Rows)
+ {
+ CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u' AND realmid = '%u'",
+ m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetSession()->GetAccountId(), realmID );
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,realmid,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetSession()->GetAccountId(), realmID, m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
+ };
+
+ m_TutorialsChanged = false;
+}
+
+void Player::outDebugValues() const
+{
+ if(!sLog.IsOutDebug()) // optimize disabled debug output
+ return;
+
+ 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("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));
+ sLog.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u",GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
+ sLog.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f",GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
+ sLog.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
+ sLog.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
+ sLog.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u",GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
+}
+
+/*********************************************************/
+/*** FLOOD FILTER SYSTEM ***/
+/*********************************************************/
+
+void Player::UpdateSpeakTime()
+{
+ // ignore chat spam protection for GMs in any mode
+ if(GetSession()->GetSecurity() > SEC_PLAYER)
+ return;
+
+ time_t current = time (NULL);
+ if(m_speakTime > current)
+ {
+ uint32 max_count = sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
+ if(!max_count)
+ return;
+
+ ++m_speakCount;
+ if(m_speakCount >= max_count)
+ {
+ // prevent overwrite mute time, if message send just before mutes set, for example.
+ time_t new_mute = current + sWorld.getConfig(CONFIG_CHATFLOOD_MUTE_TIME);
+ if(GetSession()->m_muteTime < new_mute)
+ GetSession()->m_muteTime = new_mute;
+
+ m_speakCount = 0;
+ }
+ }
+ else
+ m_speakCount = 0;
+
+ m_speakTime = current + sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
+}
+
+bool Player::CanSpeak() const
+{
+ return GetSession()->m_muteTime <= time (NULL);
+}
+
+/*********************************************************/
+/*** LOW LEVEL FUNCTIONS:Notifiers ***/
+/*********************************************************/
+
+void Player::SendAttackSwingNotInRange()
+{
+ WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid)
+{
+ std::ostringstream ss;
+ ss << "UPDATE characters SET position_x='"<<x<<"',position_y='"<<y
+ << "',position_z='"<<z<<"',orientation='"<<o<<"',map='"<<mapid
+ << "',zone='"<<zone<<"',trans_x='0',trans_y='0',trans_z='0',"
+ << "transguid='0',taxi_path='' WHERE guid='"<< GUID_LOPART(guid) <<"'";
+ sLog.outDebug(ss.str().c_str());
+ CharacterDatabase.Execute(ss.str().c_str());
+}
+
+bool Player::SaveValuesArrayInDB(Tokens const& tokens, uint64 guid)
+{
+ std::ostringstream ss2;
+ ss2<<"UPDATE characters SET data='";
+ int i=0;
+ for (Tokens::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter, ++i)
+ {
+ ss2<<tokens[i]<<" ";
+ }
+ ss2<<"' WHERE guid='"<< GUID_LOPART(guid) <<"'";
+
+ return CharacterDatabase.Execute(ss2.str().c_str());
+}
+
+void Player::SetUInt32ValueInArray(Tokens& tokens,uint16 index, uint32 value)
+{
+ char buf[11];
+ snprintf(buf,11,"%u",value);
+
+ if(index >= tokens.size())
+ return;
+
+ tokens[index] = buf;
+}
+
+void Player::SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid)
+{
+ Tokens tokens;
+ if(!LoadValuesArrayFromDB(tokens,guid))
+ return;
+
+ if(index >= tokens.size())
+ return;
+
+ char buf[11];
+ snprintf(buf,11,"%u",value);
+ tokens[index] = buf;
+
+ SaveValuesArrayInDB(tokens,guid);
+}
+
+void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid)
+{
+ uint32 temp;
+ memcpy(&temp, &value, sizeof(value));
+ Player::SetUInt32ValueInDB(index, temp, guid);
+}
+
+void Player::SendAttackSwingNotStanding()
+{
+ WorldPacket data(SMSG_ATTACKSWING_NOTSTANDING, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingDeadTarget()
+{
+ WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingCantAttack()
+{
+ WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingCancelAttack()
+{
+ WorldPacket data(SMSG_CANCEL_COMBAT, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingBadFacingAttack()
+{
+ WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAutoRepeatCancel()
+{
+ WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::PlaySound(uint32 Sound, bool OnlySelf)
+{
+ WorldPacket data(SMSG_PLAY_SOUND, 4);
+ data << Sound;
+ if (OnlySelf)
+ GetSession()->SendPacket( &data );
+ else
+ SendMessageToSet( &data, true );
+}
+
+void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
+{
+ WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 );
+ data << Area;
+ data << Experience;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendDungeonDifficulty(bool IsInGroup)
+{
+ uint8 val = 0x00000001;
+ WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
+ data << (uint32)GetDifficulty();
+ data << uint32(val);
+ data << uint32(IsInGroup);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendResetFailedNotify(uint32 mapid)
+{
+ WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
+ data << uint32(mapid);
+ GetSession()->SendPacket(&data);
+}
+
+/// Reset all solo instances and optionally send a message on success for each
+void Player::ResetInstances(uint8 method)
+{
+ // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
+
+ // we assume that when the difficulty changes, all instances that can be reset will be
+ uint8 dif = GetDifficulty();
+
+ for (BoundInstancesMap::iterator itr = m_boundInstances[dif].begin(); itr != m_boundInstances[dif].end();)
+ {
+ InstanceSave *p = itr->second.save;
+ const MapEntry *entry = sMapStore.LookupEntry(itr->first);
+ if(!entry || !p->CanReset())
+ {
+ ++itr;
+ continue;
+ }
+
+ if(method == INSTANCE_RESET_ALL)
+ {
+ // the "reset all instances" method can only reset normal maps
+ if(dif == DIFFICULTY_HEROIC || entry->map_type == MAP_RAID)
+ {
+ ++itr;
+ continue;
+ }
+ }
+
+ // if the map is loaded, reset it
+ Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
+ if(map && map->IsDungeon())
+ ((InstanceMap*)map)->Reset(method);
+
+ // since this is a solo instance there should not be any players inside
+ if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
+ SendResetInstanceSuccess(p->GetMapId());
+
+ p->DeleteFromDB();
+ m_boundInstances[dif].erase(itr++);
+
+ // the following should remove the instance save from the manager and delete it as well
+ p->RemovePlayer(this);
+ }
+}
+
+void Player::SendResetInstanceSuccess(uint32 MapId)
+{
+ WorldPacket data(SMSG_INSTANCE_RESET, 4);
+ data << MapId;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
+{
+ // TODO: find what other fail reasons there are besides players in the instance
+ WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
+ data << reason;
+ data << MapId;
+ GetSession()->SendPacket(&data);
+}
+
+/*********************************************************/
+/*** Update timers ***/
+/*********************************************************/
+
+///checks the 15 afk reports per 5 minutes limit
+void Player::UpdateAfkReport(time_t currTime)
+{
+ if(m_bgAfkReportedTimer <= currTime)
+ {
+ m_bgAfkReportedCount = 0;
+ m_bgAfkReportedTimer = currTime+5*MINUTE;
+ }
+}
+
+void Player::UpdateContestedPvP(uint32 diff)
+{
+ if(!m_contestedPvPTimer||isInCombat())
+ return;
+ if(m_contestedPvPTimer <= diff)
+ {
+ ResetContestedPvP();
+ }
+ else
+ m_contestedPvPTimer -= diff;
+}
+
+void Player::UpdatePvPFlag(time_t currTime)
+{
+ if(!IsPvP())
+ return;
+ if(pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300))
+ return;
+
+ UpdatePvP(false);
+}
+
+void Player::UpdateDuelFlag(time_t currTime)
+{
+ if(!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
+ return;
+
+ SetUInt32Value(PLAYER_DUEL_TEAM, 1);
+ duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
+
+ duel->startTimer = 0;
+ duel->startTime = currTime;
+ duel->opponent->duel->startTimer = 0;
+ duel->opponent->duel->startTime = currTime;
+}
+
+void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
+{
+ if(!pet)
+ pet = GetPet();
+
+ if(returnreagent && (pet || m_temporaryUnsummonedPetNumber))
+ {
+ //returning of reagents only for players, so best done here
+ uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+
+ if(spellInfo)
+ {
+ for(uint32 i = 0; i < 7; ++i)
+ {
+ if(spellInfo->Reagent[i] > 0)
+ {
+ ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i] );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, spellInfo->Reagent[i], true);
+ if(IsInWorld())
+ SendNewItem(item,spellInfo->ReagentCount[i],true,false);
+ }
+ }
+ }
+ }
+ m_temporaryUnsummonedPetNumber = 0;
+ }
+
+ if(!pet || pet->GetOwnerGUID()!=GetGUID())
+ return;
+
+ // only if current pet in slot
+ switch(pet->getPetType())
+ {
+ case MINI_PET:
+ m_miniPet = 0;
+ break;
+ case GUARDIAN_PET:
+ m_guardianPets.erase(pet->GetGUID());
+ break;
+ default:
+ if(GetPetGUID()==pet->GetGUID())
+ SetPet(0);
+ break;
+ }
+
+ pet->CombatStop();
+
+ if(returnreagent)
+ {
+ switch(pet->GetEntry())
+ {
+ //warlock pets except imp are removed(?) when logging out
+ case 1860:
+ case 1863:
+ case 417:
+ case 17252:
+ mode = PET_SAVE_NOT_IN_SLOT;
+ break;
+ }
+ }
+
+ pet->SavePetToDB(mode);
+
+ pet->CleanupsBeforeDelete();
+ pet->AddObjectToRemoveList();
+ pet->m_removed = true;
+
+ if(pet->isControlled())
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ GetSession()->SendPacket(&data);
+
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_PET);
+ }
+}
+
+
+void Player::RemoveMiniPet()
+{
+ if(Pet* pet = GetMiniPet())
+ {
+ pet->Remove(PET_SAVE_AS_DELETED);
+ m_miniPet = 0;
+ }
+}
+
+Pet* Player::GetMiniPet()
+{
+ if(!m_miniPet)
+ return NULL;
+ return ObjectAccessor::GetPet(m_miniPet);
+}
+
+void Player::RemoveGuardians()
+{
+ while(!m_guardianPets.empty())
+ {
+ uint64 guid = *m_guardianPets.begin();
+ if(Pet* pet = ObjectAccessor::GetPet(guid))
+ pet->Remove(PET_SAVE_AS_DELETED);
+
+ m_guardianPets.erase(guid);
+ }
+}
+
+bool Player::HasGuardianWithEntry(uint32 entry)
+{
+ // pet guid middle part is entry (and creature also)
+ // and in guardian list must be guardians with same entry _always_
+ for(GuardianPetList::const_iterator itr = m_guardianPets.begin(); itr != m_guardianPets.end(); ++itr)
+ if(GUID_ENPART(*itr)==entry)
+ return true;
+
+ return false;
+}
+
+void Player::Uncharm()
+{
+ Unit* charm = GetCharm();
+ if(!charm)
+ return;
+
+ charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM);
+ charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS);
+}
+
+void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const
+{
+ bool pre = (msgtype==CHAT_MSG_EMOTE);
+
+ *data << (uint8)msgtype;
+ *data << (uint32)language;
+ *data << (uint64)GetGUID();
+ *data << (uint32)language; //language 2.1.0 ?
+ *data << (uint64)GetGUID();
+ *data << (uint32)(text.length()+1+(pre?3:0));
+ if(pre)
+ data->append("%s ",3);
+ *data << text;
+ *data << (uint8)chatTag();
+}
+
+void Player::Say(const std::string text, const uint32 language)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_SAY, text, language);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
+}
+
+void Player::Yell(const std::string text, const uint32 language)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_YELL, text, language);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
+}
+
+void Player::TextEmote(const std::string text)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) );
+}
+
+void Player::Whisper(std::string text, uint32 language,uint64 receiver)
+{
+ if (language != LANG_ADDON) // if not addon data
+ language = LANG_UNIVERSAL; // whispers should always be readable
+
+ Player *rPlayer = objmgr.GetPlayer(receiver);
+
+ // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
+ if(!rPlayer->isDND() || isGameMaster())
+ {
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_WHISPER, text, language);
+ rPlayer->GetSession()->SendPacket(&data);
+
+ data.Initialize(SMSG_MESSAGECHAT, 200);
+ rPlayer->BuildPlayerChat(&data, CHAT_MSG_REPLY, text, language);
+ GetSession()->SendPacket(&data);
+ }
+ else
+ {
+ // announce to player that player he is whispering to is dnd and cannot receive his message
+ ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->dndMsg.c_str());
+ }
+
+ if(!isAcceptWhispers())
+ {
+ SetAcceptWhispers(true);
+ ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
+ }
+
+ // announce to player that player he is whispering to is afk
+ if(rPlayer->isAFK())
+ ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->afkMsg.c_str());
+
+ // if player whisper someone, auto turn of dnd to be able to receive an answer
+ if(isDND() && !rPlayer->isGameMaster())
+ ToggleDND();
+}
+
+void Player::PetSpellInitialize()
+{
+ Pet* pet = GetPet();
+
+ if(pet)
+ {
+ uint8 addlist = 0;
+
+ sLog.outDebug("Pet Spells Groups");
+
+ CreatureInfo const *cinfo = pet->GetCreatureInfo();
+
+ if(pet->isControlled() && (pet->getPetType() == HUNTER_PET || cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK))
+ {
+ for(PetSpellMap::iterator itr = pet->m_spells.begin();itr != pet->m_spells.end();itr++)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+ ++addlist;
+ }
+ }
+
+ // first line + actionbar + spellcount + spells + last adds
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+
+ //16
+ data << (uint64)pet->GetGUID() << uint32(0x00000000) << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ if(addlist && pet->isControlled())
+ {
+ for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+
+ data << uint16(itr->first);
+ data << uint16(itr->second->active); // pet spell active state isn't boolean
+ }
+ }
+
+ //data << uint8(0x01) << uint32(0x6010) << uint32(0x01) << uint32(0x05) << uint16(0x00); //15
+ uint8 count = 3; //1+8+8+8=25
+
+ // if count = 0, then end of packet...
+ data << count;
+ // uint32 value is spell id...
+ // uint64 value is constant 0, unknown...
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ //data << uint32(0x5fd1) << uint64(0); // if count = 2
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::PossessSpellInitialize()
+{
+ Unit* charm = GetCharm();
+
+ if(!charm)
+ return;
+
+ CharmInfo *charmInfo = charm->GetCharmInfo();
+
+ if(!charmInfo)
+ {
+ sLog.outError("Player::PossessSpellInitialize(): charm ("I64FMTD") has no charminfo!", charm->GetGUID());
+ return;
+ }
+
+ uint8 addlist = 0;
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
+
+ //16
+ data << (uint64)charm->GetGUID() << uint32(0x00000000) << uint8(0) << uint8(0) << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ uint8 count = 3;
+ data << count;
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+}
+
+void Player::CharmSpellInitialize()
+{
+ Unit* charm = GetCharm();
+
+ if(!charm)
+ return;
+
+ CharmInfo *charmInfo = charm->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("Player::CharmSpellInitialize(): the player's charm ("I64FMTD") has no charminfo!", charm->GetGUID());
+ return;
+ }
+
+ uint8 addlist = 0;
+
+ if(charm->GetTypeId() != TYPEID_PLAYER)
+ {
+ CreatureInfo const *cinfo = ((Creature*)charm)->GetCreatureInfo();
+
+ if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
+ {
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ {
+ if(charmInfo->GetCharmSpell(i)->spellId)
+ ++addlist;
+ }
+ }
+ }
+
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
+
+ data << (uint64)charm->GetGUID() << uint32(0x00000000);
+
+ if(charm->GetTypeId() != TYPEID_PLAYER)
+ data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState());
+ else
+ data << uint8(0) << uint8(0);
+
+ data << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ if(addlist)
+ {
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ {
+ CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i);
+ if(cspell->spellId)
+ {
+ data << uint16(cspell->spellId);
+ data << uint16(cspell->active);
+ }
+ }
+ }
+
+ uint8 count = 3;
+ data << count;
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+}
+
+int32 Player::GetTotalFlatMods(uint32 spellId, SpellModOp op)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 total = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod))
+ continue;
+
+ if (mod->type == SPELLMOD_FLAT)
+ total += mod->value;
+ }
+ return total;
+}
+
+int32 Player::GetTotalPctMods(uint32 spellId, SpellModOp op)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 total = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod))
+ continue;
+
+ if (mod->type == SPELLMOD_PCT)
+ total += mod->value;
+ }
+ return total;
+}
+
+bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell)
+{
+ if (!mod || !spellInfo)
+ return false;
+
+ if(mod->charges == -1 && mod->lastAffected ) // marked as expired but locked until spell casting finish
+ {
+ // prevent apply to any spell except spell that trigger expire
+ if(spell)
+ {
+ if(mod->lastAffected != spell)
+ return false;
+ }
+ else if(mod->lastAffected != FindCurrentSpellBySpellId(spellInfo->Id))
+ return false;
+ }
+
+ return spellmgr.IsAffectedBySpell(spellInfo,mod->spellId,mod->effectId,mod->mask);
+}
+
+void Player::AddSpellMod(SpellModifier* mod, bool apply)
+{
+ uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
+
+ for(int eff=0;eff<64;++eff)
+ {
+ uint64 _mask = uint64(1) << eff;
+ if ( mod->mask & _mask)
+ {
+ int32 val = 0;
+ for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
+ {
+ if ((*itr)->type == mod->type && (*itr)->mask & _mask)
+ val += (*itr)->value;
+ }
+ val += apply ? mod->value : -(mod->value);
+ WorldPacket data(Opcode, (1+1+4));
+ data << uint8(eff);
+ data << uint8(mod->op);
+ data << int32(val);
+ SendDirectMessage(&data);
+ }
+ }
+
+ if (apply)
+ m_spellMods[mod->op].push_back(mod);
+ else
+ {
+ if (mod->charges == -1)
+ --m_SpellModRemoveCount;
+ m_spellMods[mod->op].remove(mod);
+ delete mod;
+ }
+}
+
+void Player::RemoveSpellMods(Spell const* spell)
+{
+ if(!spell || (m_SpellModRemoveCount == 0))
+ return;
+
+ for(int i=0;i<MAX_SPELLMOD;++i)
+ {
+ for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
+ {
+ SpellModifier *mod = *itr;
+ ++itr;
+
+ if (mod && mod->charges == -1 && (mod->lastAffected == spell || mod->lastAffected==NULL))
+ {
+ RemoveAurasDueToSpell(mod->spellId);
+ if (m_spellMods[i].empty())
+ break;
+ else
+ itr = m_spellMods[i].begin();
+ }
+ }
+ }
+}
+
+// send Proficiency
+void Player::SendProficiency(uint8 pr1, uint32 pr2)
+{
+ WorldPacket data(SMSG_SET_PROFICIENCY, 8);
+ data << pr1 << pr2;
+ GetSession()->SendPacket (&data);
+}
+
+void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
+{
+ QueryResult *result = NULL;
+ if(type==10)
+ result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
+ else
+ result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ if(result)
+ {
+ do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
+ { // and SendPetitionQueryOpcode reads data from the DB
+ Field *fields = result->Fetch();
+ uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ uint64 petitionguid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);
+
+ // send update if charter owner in game
+ Player* owner = objmgr.GetPlayer(ownerguid);
+ if(owner)
+ owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
+
+ } while ( result->NextRow() );
+
+ delete result;
+
+ if(type==10)
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
+ else
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if(type == 10)
+ {
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", GUID_LOPART(guid));
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid));
+ }
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ }
+ CharacterDatabase.CommitTransaction();
+}
+
+void Player::SetRestBonus (float rest_bonus_new)
+{
+ // Prevent resting on max level
+ if(getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ rest_bonus_new = 0;
+
+ if(rest_bonus_new < 0)
+ rest_bonus_new = 0;
+
+ float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5/2;
+
+ if(rest_bonus_new > rest_bonus_max)
+ m_rest_bonus = rest_bonus_max;
+ else
+ m_rest_bonus = rest_bonus_new;
+
+ // update data for client
+ if(m_rest_bonus>10)
+ SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
+ else if(m_rest_bonus<=1)
+ SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal
+
+ //RestTickUpdate
+ SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
+}
+
+void Player::HandleStealthedUnitsDetection()
+{
+ std::list<Unit*> stealthedUnits;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyStealthedCheck u_check;
+ MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck > searcher(stealthedUnits, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ for (std::list<Unit*>::iterator i = stealthedUnits.begin(); i != stealthedUnits.end();)
+ {
+ if((*i)==this)
+ {
+ i = stealthedUnits.erase(i);
+ continue;
+ }
+
+ if ((*i)->isVisibleForOrDetect(this,true))
+ {
+
+ (*i)->SendUpdateToPlayer(this);
+ m_clientGUIDs.insert((*i)->GetGUID());
+
+ #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
+
+ // target aura duration for caster show only if target exist at caster client
+ // send data at target visibility change (adding to client)
+ if((*i)!=this && (*i)->isType(TYPEMASK_UNIT))
+ SendAuraDurationsForTarget(*i);
+
+ i = stealthedUnits.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+}
+
+bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id, Creature* npc)
+{
+ if(nodes.size() < 2)
+ return false;
+
+ // not let cheating with start flight mounted
+ if(IsMounted())
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERALREADYMOUNTED);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERSHAPESHIFTED);
+ GetSession()->SendPacket(&data);
+ 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() ||
+ (!m_currentSpells[CURRENT_GENERIC_SPELL] ||
+ m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Effect[0] != SPELL_EFFECT_SEND_TAXI)&&
+ IsNonMeleeSpellCasted(false) ||
+ isInCombat())
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERBUSY);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ return false;
+
+ uint32 sourcenode = nodes[0];
+
+ // starting node too far away (cheat?)
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
+ if( !node || node->map_id != GetMapId() ||
+ (node->x - GetPositionX())*(node->x - GetPositionX())+
+ (node->y - GetPositionY())*(node->y - GetPositionY())+
+ (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
+ (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE) )
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ // Prepare to flight start now
+
+ // stop combat at start taxi flight if any
+ CombatStop();
+
+ // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
+ TradeCancel(true);
+
+ // clean not finished taxi path if any
+ m_taxi.ClearTaxiDestinations();
+
+ // 0 element current node
+ m_taxi.AddTaxiDestination(sourcenode);
+
+ // fill destinations path tail
+ uint32 sourcepath = 0;
+ uint32 totalcost = 0;
+
+ uint32 prevnode = sourcenode;
+ uint32 lastnode = 0;
+
+ for(uint32 i = 1; i < nodes.size(); ++i)
+ {
+ uint32 path, cost;
+
+ lastnode = nodes[i];
+ objmgr.GetTaxiPath(prevnode, lastnode, path, cost);
+
+ if(!path)
+ {
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ totalcost += cost;
+
+ if(prevnode == sourcenode)
+ sourcepath = path;
+
+ m_taxi.AddTaxiDestination(lastnode);
+
+ prevnode = lastnode;
+ }
+
+ if(!mount_id) // if not provide then attempt use default.
+ mount_id = objmgr.GetTaxiMount(sourcenode, GetTeam());
+
+ if (mount_id == 0 || sourcepath == 0)
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
+ GetSession()->SendPacket(&data);
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ uint32 money = GetMoney();
+
+ if(npc)
+ {
+ totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
+ }
+
+ if(money < totalcost)
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXINOTENOUGHMONEY);
+ GetSession()->SendPacket(&data);
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ //Checks and preparations done, DO FLIGHT
+ ModifyMoney(-(int32)totalcost);
+
+ // prevent stealth flight
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIOK);
+ GetSession()->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent SMSG_ACTIVATETAXIREPLY");
+
+ GetSession()->SendDoFlight(mount_id, sourcepath);
+
+ return true;
+}
+
+void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
+{
+ // last check 2.0.10
+ WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
+ data << GetGUID();
+ data << uint8(0x0);
+ time_t curTime = time(NULL);
+ for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+ uint32 unSpellId = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(unSpellId);
+ if (!spellInfo)
+ {
+ ASSERT(spellInfo);
+ continue;
+ }
+
+ // Not send cooldown for this spells
+ if (spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ continue;
+
+ if((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs )
+ {
+ data << unSpellId;
+ data << unTimeMs; // in m.secs
+ AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000);
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::InitDataForForm(bool reapplyMods)
+{
+ SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form);
+ if(ssEntry && ssEntry->attackSpeed)
+ {
+ SetAttackTime(BASE_ATTACK,ssEntry->attackSpeed);
+ SetAttackTime(OFF_ATTACK,ssEntry->attackSpeed);
+ SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
+ }
+ else
+ SetRegularAttackTime();
+
+ switch(m_form)
+ {
+ case FORM_CAT:
+ {
+ if(getPowerType()!=POWER_ENERGY)
+ setPowerType(POWER_ENERGY);
+ break;
+ }
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ {
+ if(getPowerType()!=POWER_RAGE)
+ setPowerType(POWER_RAGE);
+ break;
+ }
+ default: // 0, for example
+ {
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
+ if(cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
+ setPowerType(Powers(cEntry->powerType));
+ break;
+ }
+ }
+
+ // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
+ if (!reapplyMods)
+ UpdateEquipSpellsAtFormChange();
+
+ UpdateAttackPowerAndDamage();
+ UpdateAttackPowerAndDamage(true);
+}
+
+// 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)
+{
+ // cheating attempt
+ if(count < 1) count = 1;
+
+ if(!isAlive())
+ return false;
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( !pProto )
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
+ return false;
+ }
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*this, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ SendBuyError( BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
+ return false;
+ }
+
+ // load vendor items if not yet
+ pCreature->LoadGoods();
+
+ CreatureItem* crItem = pCreature->FindItem(item);
+ if(!crItem)
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
+ return false;
+ }
+
+ if( crItem->maxcount != 0 && crItem->count < count )
+ {
+ SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
+ return false;
+ }
+
+ if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
+ {
+ SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
+ return false;
+ }
+
+ if(crItem->ExtendedCost)
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ 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))
+ {
+ SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
+ return false;
+ }
+
+ // arena points price
+ if(GetArenaPoints() < (iece->reqarenapoints * count))
+ {
+ SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL);
+ return false;
+ }
+
+ // item base price
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i] && !HasItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count)))
+ {
+ SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
+ return false;
+ }
+ }
+
+ // check for personal arena rating requirement
+ if( GetMaxPersonalArenaRatingRequirement() < iece->reqpersonalarenarating )
+ {
+ // probably not the proper equip err
+ SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK,NULL,NULL);
+ return false;
+ }
+ }
+
+ uint32 price = pProto->BuyPrice * count;
+
+ // reputation discount
+ price = uint32(floor(price * GetReputationPriceDiscount(pCreature)));
+
+ 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)
+ {
+ Bag *pBag;
+ if( bagguid == GetGUID() )
+ {
+ bag = INVENTORY_SLOT_BAG_0;
+ }
+ else
+ {
+ for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;i++)
+ {
+ pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0,i);
+ if( pBag )
+ {
+ if( bagguid == pBag->GetGUID() )
+ {
+ bag = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if( IsInventoryPos( bag, slot ) || (bagguid == NULL_BAG && slot == NULL_SLOT) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ ModifyMoney( -(int32)price );
+ if(crItem->ExtendedCost) // case for new honor system
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ if(iece->reqhonorpoints)
+ ModifyHonorPoints( - int32(iece->reqhonorpoints * count));
+ if(iece->reqarenapoints)
+ ModifyArenaPoints( - int32(iece->reqarenapoints * count));
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i])
+ DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
+ }
+ }
+
+ if(Item *it = StoreNewItem( dest, item, true ))
+ {
+ if( crItem->maxcount != 0 )
+ crItem->count -= pProto->BuyCount * count;
+
+ WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
+ data << pCreature->GetGUID();
+ data << (uint32)crItem->id; // entry
+ data << (uint32)crItem->count;
+ data << (uint32)count;
+ GetSession()->SendPacket(&data);
+
+ SendNewItem(it, count, true, false, false);
+ }
+ }
+ else if( IsEquipmentPos( bag, slot ) )
+ {
+ uint16 dest;
+ uint8 msg = CanEquipNewItem( slot, dest, item, pProto->BuyCount * count, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ ModifyMoney( -(int32)price );
+ if(crItem->ExtendedCost) // case for new honor system
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ if(iece->reqhonorpoints)
+ ModifyHonorPoints( - int32(iece->reqhonorpoints));
+ if(iece->reqarenapoints)
+ ModifyArenaPoints( - int32(iece->reqarenapoints));
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i])
+ DestroyItemCount(iece->reqitem[i], iece->reqitemcount[i], true);
+ }
+ }
+
+ if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true ))
+ {
+ if( crItem->maxcount != 0 )
+ crItem->count -= pProto->BuyCount * count;
+
+ WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
+ data << pCreature->GetGUID();
+ data << (uint32)crItem->id; // entry
+ data << (uint32)crItem->count;
+ data << (uint32)count;
+ GetSession()->SendPacket(&data);
+
+ SendNewItem(it, count, true, false, false);
+
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else
+ {
+ SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
+ return false;
+ }
+
+ return crItem->maxcount!=0?true:false;
+}
+
+uint32 Player::GetMaxPersonalArenaRatingRequirement()
+{
+ // returns the maximal personal arena rating that can be used to purchase items requiring this condition
+ // the personal rating of the arena team must match the required limit as well
+ // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
+ uint32 max_personal_rating = 0;
+ for(int i = 0; i < MAX_ARENA_SLOT; ++i)
+ {
+ if(ArenaTeam * at = objmgr.GetArenaTeamById(GetArenaTeamId(i)))
+ {
+ uint32 p_rating = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (i * 6) + 5);
+ uint32 t_rating = at->GetRating();
+ p_rating = p_rating<t_rating? p_rating : t_rating;
+ if(max_personal_rating < p_rating)
+ max_personal_rating = p_rating;
+ }
+ }
+ return max_personal_rating;
+}
+
+void Player::UpdateHomebindTime(uint32 time)
+{
+ // GMs never get homebind timer online
+ if (m_InstanceValid || isGameMaster())
+ {
+ if(m_HomebindTimer) // instance valid, but timer not reset
+ {
+ // hide reminder
+ WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
+ data << uint32(0);
+ data << uint32(0);
+ GetSession()->SendPacket(&data);
+ }
+ // instance is valid, reset homebind timer
+ m_HomebindTimer = 0;
+ }
+ else if (m_HomebindTimer > 0)
+ {
+ if (time >= m_HomebindTimer)
+ {
+ // teleport to homebind location
+ TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
+ }
+ else
+ m_HomebindTimer -= time;
+ }
+ else
+ {
+ // instance is invalid, start homebind timer
+ m_HomebindTimer = 60000;
+ // send message to player
+ WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
+ data << m_HomebindTimer;
+ data << uint32(1);
+ GetSession()->SendPacket(&data);
+ sLog.outDebug("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(),GetGUIDLow());
+ }
+}
+
+void Player::UpdatePvP(bool state, bool ovrride)
+{
+ if(!state || ovrride)
+ {
+ SetPvP(state);
+ if(Pet* pet = GetPet())
+ pet->SetPvP(state);
+ if(Unit* charmed = GetCharm())
+ charmed->SetPvP(state);
+
+ pvpInfo.endTimer = 0;
+ }
+ else
+ {
+ if(pvpInfo.endTimer != 0)
+ pvpInfo.endTimer = time(NULL);
+ else
+ {
+ SetPvP(state);
+
+ if(Pet* pet = GetPet())
+ pet->SetPvP(state);
+ if(Unit* charmed = GetCharm())
+ charmed->SetPvP(state);
+ }
+ }
+}
+
+void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
+{
+ SpellCooldown sc;
+ sc.end = end_time;
+ sc.itemid = itemid;
+ m_spellCooldowns[spellid] = sc;
+}
+
+void Player::SendCooldownEvent(SpellEntry const *spellInfo)
+{
+ if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) )
+ return;
+
+ // Get spell cooldwn
+ int32 cooldown = GetSpellRecoveryTime(spellInfo);
+ // Apply spellmods
+ ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown);
+ if (cooldown < 0)
+ cooldown = 0;
+ // Add cooldown
+ AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000);
+ // Send activate
+ WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
+ data << spellInfo->Id;
+ data << GetGUID();
+ SendDirectMessage(&data);
+}
+ //slot to be excluded while counting
+bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
+{
+ if(!enchantmentcondition)
+ return true;
+
+ SpellItemEnchantmentConditionEntry const *Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
+
+ if(!Condition)
+ return true;
+
+ uint8 curcount[4] = {0, 0, 0, 0};
+
+ //counting current equipped gem colors
+ for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
+ {
+ if(i == slot)
+ continue;
+ Item *pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if(pItem2 && pItem2->GetProto()->Socket[0].Color)
+ {
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ uint32 gemid = enchantEntry->GemID;
+ if(!gemid)
+ continue;
+
+ ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
+ if(!gemProto)
+ continue;
+
+ GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
+ if(!gemProperty)
+ continue;
+
+ uint8 GemColor = gemProperty->color;
+
+ for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
+ {
+ if(tmpcolormask & GemColor)
+ ++curcount[b];
+ }
+ }
+ }
+ }
+
+ bool activate = true;
+
+ for(int i = 0; i < 5; i++)
+ {
+ if(!Condition->Color[i])
+ continue;
+
+ uint32 _cur_gem = curcount[Condition->Color[i] - 1];
+
+ // if have <CompareColor> use them as count, else use <value> from Condition
+ uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
+
+ switch(Condition->Comparator[i])
+ {
+ case 2: // requires less <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem < _cmp_gem) ? true : false;
+ break;
+ case 3: // requires more <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem > _cmp_gem) ? true : false;
+ break;
+ case 5: // requires at least <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem >= _cmp_gem) ? true : false;
+ break;
+ }
+ }
+
+ sLog.outDebug("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no");
+
+ return activate;
+}
+
+void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
+{
+ //cycle all equipped items
+ for(uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ //enchants for the slot being socketed are handled by Player::ApplyItemMods
+ if(slot == exceptslot)
+ continue;
+
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+
+ if(!pItem || !pItem->GetProto()->Socket[0].Color)
+ continue;
+
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ uint32 condition = enchantEntry->EnchantmentCondition;
+ if(condition)
+ {
+ //was enchant active with/without item?
+ bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
+ //should it now be?
+ if(wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
+ {
+ // ignore item gem conditions
+ //if state changed, (dis)apply enchant
+ ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot),!wasactive,true,true);
+ }
+ }
+ }
+ }
+}
+
+ //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
+void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
+{
+ //cycle all equipped items
+ for(int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
+ if(slot == exceptslot)
+ continue;
+
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+
+ if(!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
+ continue;
+
+ //cycle all (gem)enchants
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id) //if no enchant go to next enchant(slot)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ //only metagems to be (de)activated, so only enchants with condition
+ uint32 condition = enchantEntry->EnchantmentCondition;
+ if(condition)
+ ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot), apply);
+ }
+ }
+}
+
+void Player::LeaveBattleground(bool teleportToEntryPoint)
+{
+ if(BattleGround *bg = GetBattleGround())
+ {
+ bool need_debuf = bg->isBattleGround() && (bg->GetStatus() == STATUS_IN_PROGRESS) && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER);
+
+ bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
+
+ // call after remove to be sure that player resurrected for correct cast
+ if(need_debuf)
+ CastSpell(this, 26013, true); // Deserter
+ }
+}
+
+bool Player::CanJoinToBattleground() const
+{
+ // check Deserter debuff
+ if(GetDummyAura(26013))
+ return false;
+
+ return true;
+}
+
+bool Player::CanReportAfkDueToLimit()
+{
+ // a player can complain about 15 people per 5 minutes
+ if(m_bgAfkReportedCount >= 15)
+ return false;
+ ++m_bgAfkReportedCount;
+ return true;
+}
+
+///This player has been blamed to be inactive in a battleground
+void Player::ReportedAfkBy(Player* reporter)
+{
+ BattleGround *bg = GetBattleGround();
+ if(!bg || bg != reporter->GetBattleGround() || GetTeam() != reporter->GetTeam())
+ return;
+
+ // check if player has 'Idle' or 'Inactive' debuff
+ if(m_bgAfkReporter.find(reporter->GetGUIDLow())==m_bgAfkReporter.end() && !HasAura(43680,0) && !HasAura(43681,0) && reporter->CanReportAfkDueToLimit())
+ {
+ m_bgAfkReporter.insert(reporter->GetGUIDLow());
+ // 3 players have to complain to apply debuff
+ if(m_bgAfkReporter.size() >= 3)
+ {
+ // cast 'Idle' spell
+ CastSpell(this, 43680, true);
+ m_bgAfkReporter.clear();
+ }
+ }
+}
+
+bool Player::IsVisibleInGridForPlayer( Player* pl ) const
+{
+ // gamemaster in GM mode see all, including ghosts
+ if(pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity())
+ return true;
+
+ // It seems in battleground everyone sees everyone, except the enemy-faction ghosts
+ if (InBattleGround())
+ {
+ if (!(isAlive() || m_deathTimer > 0) && !IsFriendlyTo(pl) )
+ return false;
+ return true;
+ }
+
+ // Live player see live player or dead player with not realized corpse
+ if(pl->isAlive() || pl->m_deathTimer > 0)
+ {
+ return isAlive() || m_deathTimer > 0;
+ }
+
+ // Ghost see other friendly ghosts, that's for sure
+ if(!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl))
+ return true;
+
+ // Dead player see live players near own corpse
+ if(isAlive())
+ {
+ Corpse *corpse = pl->GetCorpse();
+ if(corpse)
+ {
+ // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
+ if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
+ return true;
+ }
+ }
+
+ // and not see any other
+ return false;
+}
+
+bool Player::IsVisibleGloballyFor( Player* u ) const
+{
+ if(!u)
+ return false;
+
+ // Always can see self
+ if (u==this)
+ return true;
+
+ // Visible units, always are visible for all players
+ if (GetVisibility() == VISIBILITY_ON)
+ return true;
+
+ // GMs are visible for higher gms (or players are visible for gms)
+ if (u->GetSession()->GetSecurity() > SEC_PLAYER)
+ return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
+
+ // non faction visibility non-breakable for non-GMs
+ if (GetVisibility() == VISIBILITY_OFF)
+ return false;
+
+ // non-gm stealth/invisibility not hide from global player lists
+ return true;
+}
+
+void Player::UpdateVisibilityOf(WorldObject* target)
+{
+ if(HaveAtClient(target))
+ {
+ if(!target->isVisibleForInState(this,true))
+ {
+ target->DestroyForPlayer(this);
+ m_clientGUIDs.erase(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+ else
+ {
+ if(target->isVisibleForInState(this,false))
+ {
+ target->SendUpdateToPlayer(this);
+ if(target->GetTypeId()!=TYPEID_GAMEOBJECT||!((GameObject*)target)->IsTransport())
+ m_clientGUIDs.insert(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
+ #endif
+
+ // target aura duration for caster show only if target exist at caster client
+ // send data at target visibility change (adding to client)
+ if(target!=this && target->isType(TYPEMASK_UNIT))
+ SendAuraDurationsForTarget((Unit*)target);
+
+ if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isAlive())
+ ((Creature*)target)->SendMonsterMoveWithSpeedToCurrentDestination(this);
+ }
+ }
+}
+
+template<class T>
+inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target)
+{
+ s64.insert(target->GetGUID());
+}
+
+template<>
+inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target)
+{
+ if(!target->IsTransport())
+ s64.insert(target->GetGUID());
+}
+
+template<class T>
+void Player::UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow)
+{
+ if(HaveAtClient(target))
+ {
+ if(!target->isVisibleForInState(this,true))
+ {
+ target->BuildOutOfRangeUpdateBlock(&data);
+ m_clientGUIDs.erase(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u, Entry: %u) is out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+ else
+ {
+ if(target->isVisibleForInState(this,false))
+ {
+ visibleNow.insert(target);
+ target->BuildUpdate(data_updates);
+ target->BuildCreateUpdateBlockForPlayer(&data, this);
+ UpdateVisibilityOf_helper(m_clientGUIDs,target);
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+}
+
+template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+
+void Player::InitPrimaryProffesions()
+{
+ SetFreePrimaryProffesions(sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
+}
+
+void Player::SendComboPoints()
+{
+ Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
+ if (combotarget)
+ {
+ WorldPacket data(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
+ data.append(combotarget->GetPackGUID());
+ data << uint8(m_comboPoints);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::AddComboPoints(Unit* target, int8 count)
+{
+ if(!count)
+ return;
+
+ // without combo points lost (duration checked in aura)
+ RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
+
+ if(target->GetGUID() == m_comboTarget)
+ {
+ m_comboPoints += count;
+ }
+ else
+ {
+ if(m_comboTarget)
+ if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
+ target->RemoveComboPointHolder(GetGUIDLow());
+
+ m_comboTarget = target->GetGUID();
+ m_comboPoints = count;
+
+ target->AddComboPointHolder(GetGUIDLow());
+ }
+
+ if (m_comboPoints > 5) m_comboPoints = 5;
+ if (m_comboPoints < 0) m_comboPoints = 0;
+
+ SendComboPoints();
+}
+
+void Player::ClearComboPoints()
+{
+ if(!m_comboTarget)
+ return;
+
+ // without combopoints lost (duration checked in aura)
+ RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
+
+ m_comboPoints = 0;
+
+ SendComboPoints();
+
+ if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
+ target->RemoveComboPointHolder(GetGUIDLow());
+
+ m_comboTarget = 0;
+}
+
+void Player::SetGroup(Group *group, int8 subgroup)
+{
+ if(group == NULL) m_group.unlink();
+ else
+ {
+ // never use SetGroup without a subgroup unless you specify NULL for group
+ assert(subgroup >= 0);
+ m_group.link(group, this);
+ m_group.setSubGroup((uint8)subgroup);
+ }
+}
+
+void Player::SendInitialPacketsBeforeAddToMap()
+{
+ WorldPacket data(SMSG_SET_REST_START, 4);
+ data << uint32(0); // unknown, may be rest state time or expirience
+ GetSession()->SendPacket(&data);
+
+ // Homebind
+ data.Initialize(SMSG_BINDPOINTUPDATE, 5*4);
+ data << m_homebindX << m_homebindY << m_homebindZ;
+ data << (uint32) m_homebindMapId;
+ data << (uint32) m_homebindZoneId;
+ GetSession()->SendPacket(&data);
+
+ // SMSG_SET_PROFICIENCY
+ // SMSG_UPDATE_AURA_DURATION
+
+ // tutorial stuff
+ data.Initialize(SMSG_TUTORIAL_FLAGS, 8*4);
+ for (int i = 0; i < 8; ++i)
+ data << uint32( GetTutorialInt(i) );
+ GetSession()->SendPacket(&data);
+
+ SendInitialSpells();
+
+ data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
+ data << uint32(0); // count, for(count) uint32;
+ GetSession()->SendPacket(&data);
+
+ SendInitialActionButtons();
+ SendInitialReputations();
+ UpdateZone(GetZoneId());
+ SendInitWorldStates();
+
+ // SMSG_SET_AURA_SINGLE
+
+ data.Initialize(SMSG_LOGIN_SETTIMESPEED, 8);
+ data << uint32(secsToTimeBitFields(sWorld.GetGameTime()));
+ data << (float)0.01666667f; // game speed
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendInitialPacketsAfterAddToMap()
+{
+ CastSpell(this, 836, true); // LOGINEFFECT
+
+ // set some aura effects that send packet to player client after add player to map
+ // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
+ // same auras state lost at far teleport, send it one more time in this case also
+ static const AuraType auratypes[] =
+ {
+ SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
+ SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
+ SPELL_AURA_FLY, SPELL_AURA_NONE
+ };
+ for(AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
+ {
+ Unit::AuraList const& auraList = GetAurasByType(*itr);
+ if(!auraList.empty())
+ auraList.front()->ApplyModifier(true,true);
+ }
+
+ if(HasAuraType(SPELL_AURA_MOD_STUN))
+ SetMovement(MOVE_ROOT);
+
+ // manual send package (have code in ApplyModifier(true,true); that don't must be re-applied.
+ if(HasAuraType(SPELL_AURA_MOD_ROOT))
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
+ data.append(GetPackGUID());
+ data << (uint32)2;
+ SendMessageToSet(&data,true);
+ }
+
+ SendEnchantmentDurations(); // must be after add to map
+ SendItemDurations(); // must be after add to map
+}
+
+void Player::SendUpdateToOutOfRangeGroupMembers()
+{
+ if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
+ return;
+ if(Group* group = GetGroup())
+ group->UpdatePlayerOutOfRange(this);
+
+ m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
+ m_auraUpdateMask = 0;
+ if(Pet *pet = GetPet())
+ pet->ResetAuraUpdateMask();
+}
+
+void Player::SendTransferAborted(uint32 mapid, uint16 reason)
+{
+ WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
+ data << uint32(mapid);
+ data << uint16(reason); // transfer abort reason
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendInstanceResetWarning(uint32 mapid, uint32 time)
+{
+ // type of warning, based on the time remaining until reset
+ uint32 type;
+ if(time > 3600)
+ type = RAID_INSTANCE_WELCOME;
+ else if(time > 900 && time <= 3600)
+ type = RAID_INSTANCE_WARNING_HOURS;
+ else if(time > 300 && time <= 900)
+ type = RAID_INSTANCE_WARNING_MIN;
+ else
+ type = RAID_INSTANCE_WARNING_MIN_SOON;
+ WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4);
+ data << uint32(type);
+ data << uint32(mapid);
+ data << uint32(time);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ApplyEquipCooldown( Item * pItem )
+{
+ for(int i = 0; i <5; ++i)
+ {
+ _Spell const& spellData = pItem->GetProto()->Spells[i];
+
+ // no spell
+ if( !spellData.SpellId )
+ continue;
+
+ // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
+ if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE )
+ continue;
+
+ AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
+
+ WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
+ data << pItem->GetGUID();
+ data << uint32(spellData.SpellId);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+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());
+ }
+
+ // 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
+
+ learnDefaultSpells();
+ learnQuestRewardedSpells();
+}
+
+void Player::learnDefaultSpells(bool loading)
+{
+ // learn default race/class spells
+ PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(),getClass());
+ std::list<CreateSpellPair>::const_iterator spell_itr;
+ for (spell_itr = info->spell.begin(); spell_itr!=info->spell.end(); ++spell_itr)
+ {
+ uint16 tspell = spell_itr->first;
+ if (tspell)
+ {
+ sLog.outDebug("PLAYER: Adding initial spell, id = %u",tspell);
+ if(loading || !spell_itr->second) // not care about passive spells or loading case
+ addSpell(tspell,spell_itr->second);
+ else // but send in normal spell in game learn case
+ learnSpell(tspell);
+ }
+ }
+}
+
+void Player::learnQuestRewardedSpells(Quest const* quest)
+{
+ uint32 spell_id = quest->GetRewSpellCast();
+
+ // skip quests without rewarded spell
+ if( !spell_id )
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(!spellInfo)
+ return;
+
+ // check learned spells state
+ bool found = false;
+ for(int i=0; i < 3; ++i)
+ {
+ if(spellInfo->Effect[i] == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->EffectTriggerSpell[i]))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ // skip quests with not teaching spell or already known spell
+ if(!found)
+ return;
+
+ // prevent learn non first rank unknown profession and second specialization for same profession)
+ uint32 learned_0 = spellInfo->EffectTriggerSpell[0];
+ if( spellmgr.GetSpellRank(learned_0) > 1 && !HasSpell(learned_0) )
+ {
+ // not have first rank learned (unlearned prof?)
+ uint32 first_spell = spellmgr.GetFirstSpellInChain(learned_0);
+ if( !HasSpell(first_spell) )
+ return;
+
+ SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
+ if(!learnedInfo)
+ return;
+
+ // specialization
+ if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0)
+ {
+ // search other specialization for same prof
+ for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED || itr->first==learned_0)
+ continue;
+
+ SpellEntry const *itrInfo = sSpellStore.LookupEntry(itr->first);
+ if(!itrInfo)
+ return;
+
+ // compare only specializations
+ if(itrInfo->Effect[0]!=SPELL_EFFECT_TRADE_SKILL || itrInfo->Effect[1]!=0)
+ 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))
+ return;
+ }
+ }
+ }
+
+ CastSpell( this, spell_id, true);
+}
+
+void Player::learnQuestRewardedSpells()
+{
+ // learn spells received from quest completing
+ for(QuestStatusMap::const_iterator itr = mQuestStatus.begin(); itr != mQuestStatus.end(); ++itr)
+ {
+ // skip no rewarded quests
+ if(!itr->second.m_rewarded)
+ continue;
+
+ Quest const* quest = objmgr.GetQuestTemplate(itr->first);
+ if( !quest )
+ continue;
+
+ learnQuestRewardedSpells(quest);
+ }
+}
+
+void Player::learnSkillRewardedSpells(uint32 skill_id )
+{
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j);
+ if (!pAbility || pAbility->skillId!=skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
+ continue;
+ // Check race if set
+ if (pAbility->racemask && !(pAbility->racemask & raceMask))
+ continue;
+ // Check class if set
+ if (pAbility->classmask && !(pAbility->classmask & classMask))
+ continue;
+
+ if (SpellEntry const* spellentry = sSpellStore.LookupEntry(pAbility->spellId))
+ {
+ // Ok need learn spell
+ learnSpell(pAbility->spellId);
+ }
+ }
+}
+
+void Player::learnSkillRewardedSpells()
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if(!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ continue;
+
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+
+ learnSkillRewardedSpells(pskill);
+ }
+}
+
+void Player::SendAuraDurationsForTarget(Unit* target)
+{
+ for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr)
+ {
+ Aura* aura = itr->second;
+ if(aura->GetAuraSlot() >= MAX_AURAS || aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
+ continue;
+
+ aura->SendAuraDurationForCaster(this);
+ }
+}
+
+void Player::SetDailyQuestStatus( uint32 quest_id )
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ {
+ if(!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
+ {
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
+ m_lastDailyQuestTime = time(NULL); // last daily quest time
+ m_DailyQuestChanged = true;
+ break;
+ }
+ }
+}
+
+void Player::ResetDailyQuestStatus()
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
+
+ // DB data deleted in caller
+ m_DailyQuestChanged = false;
+ m_lastDailyQuestTime = 0;
+}
+
+BattleGround* Player::GetBattleGround() const
+{
+ if(GetBattleGroundId()==0)
+ return NULL;
+
+ return sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
+}
+
+bool Player::InArena() const
+{
+ BattleGround *bg = GetBattleGround();
+ if(!bg || !bg->isArena())
+ return false;
+
+ return true;
+}
+
+bool Player::GetBGAccessByLevel(uint32 bgTypeId) const
+{
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId);
+ if(!bg)
+ return false;
+
+ if(getLevel() < bg->GetMinLevel() || getLevel() > bg->GetMaxLevel())
+ return false;
+
+ return true;
+}
+
+uint32 Player::GetMinLevelForBattleGroundQueueId(uint32 queue_id)
+{
+ if(queue_id < 1)
+ return 0;
+
+ if(queue_id >=6)
+ queue_id = 6;
+
+ return 10*(queue_id+1);
+}
+
+uint32 Player::GetMaxLevelForBattleGroundQueueId(uint32 queue_id)
+{
+ if(queue_id >=6)
+ return 255; // hardcoded max level
+
+ return 10*(queue_id+2)-1;
+}
+
+uint32 Player::GetBattleGroundQueueIdFromLevel() const
+{
+ uint32 level = getLevel();
+ if(level <= 19)
+ return 0;
+ else if (level > 69)
+ return 6;
+ else
+ return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ...
+}
+
+float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
+{
+ FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
+ if(!vendor_faction)
+ return 1.0f;
+
+ ReputationRank rank = GetReputationRank(vendor_faction->faction);
+ if(rank <= REP_NEUTRAL)
+ return 1.0f;
+
+ return 1.0f - 0.05f* (rank - REP_NEUTRAL);
+}
+
+bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const
+{
+ uint32 racemask = getRaceMask();
+ uint32 classmask = getClassMask();
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ // skip wrong race skills
+ if( _spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
+ return false;
+
+ // skip wrong class skills
+ if( _spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
+ return false;
+ }
+ return true;
+}
+
+bool Player::HasQuestForGO(int32 GOId)
+{
+ for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ QuestStatusData qs=i->second;
+ if (qs.m_status == QUEST_STATUS_INCOMPLETE)
+ {
+ Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
+ if(!qinfo)
+ continue;
+
+ if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ if (qinfo->ReqCreatureOrGOId[j]>=0) //skip non GO case
+ continue;
+
+ if((-1)*GOId == qinfo->ReqCreatureOrGOId[j] && qs.m_creatureOrGOcount[j] < qinfo->ReqCreatureOrGOCount[j])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Player::UpdateForQuestsGO()
+{
+ if(m_clientGUIDs.empty())
+ return;
+
+ UpdateData udata;
+ WorldPacket packet;
+ for(ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr!=m_clientGUIDs.end(); ++itr)
+ {
+ if(IS_GAMEOBJECT_GUID(*itr))
+ {
+ GameObject *obj = HashMapHolder<GameObject>::Find(*itr);
+ if(obj)
+ obj->BuildValuesUpdateBlockForPlayer(&udata,this);
+ }
+ }
+ udata.BuildPacket(&packet);
+ GetSession()->SendPacket(&packet);
+}
+
+void Player::SummonIfPossible(bool agree)
+{
+ if(!agree)
+ {
+ m_summon_expire = 0;
+ return;
+ }
+
+ // expire and auto declined
+ if(m_summon_expire < time(NULL))
+ return;
+
+ // stop taxi flight at summon
+ if(isInFlight())
+ {
+ GetMotionMaster()->MovementExpired();
+ m_taxi.ClearTaxiDestinations();
+ }
+
+ // drop flag at summon
+ if(BattleGround *bg = GetBattleGround())
+ bg->EventPlayerDroppedFlag(this);
+
+ m_summon_expire = 0;
+
+ TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,GetOrientation());
+}
+
+void Player::RemoveItemDurations( Item *item )
+{
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); ++itr)
+ {
+ if(*itr==item)
+ {
+ m_itemDuration.erase(itr);
+ break;
+ }
+ }
+}
+
+void Player::AddItemDurations( Item *item )
+{
+ if(item->GetUInt32Value(ITEM_FIELD_DURATION))
+ {
+ m_itemDuration.push_back(item);
+ item->SendTimeUpdate(this);
+ }
+}
+
+void Player::AutoUnequipOffhandIfNeed()
+{
+ Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
+ if(!offItem)
+ return;
+
+ Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+
+ if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
+ return;
+
+ ItemPosCountVec off_dest;
+ uint8 off_msg = CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false );
+ if( off_msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
+ StoreItem( off_dest, offItem, true );
+ }
+ else
+ {
+ sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow());
+ }
+}
+
+bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem)
+{
+ if(spellInfo->EquippedItemClass < 0)
+ return true;
+
+ // scan other equipped items for same requirements (mostly 2 daggers/etc)
+ // for optimize check 2 used cases only
+ switch(spellInfo->EquippedItemClass)
+ {
+ case ITEM_CLASS_WEAPON:
+ {
+ for(int i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+ break;
+ }
+ case ITEM_CLASS_ARMOR:
+ {
+ // tabard not have dependent spells
+ for(int i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i)
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ // shields can be equipped to offhand slot
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ // ranged slot can have some armor subclasses
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ break;
+ }
+ default:
+ sLog.outError("HasItemFitToSpellReqirements: Not handeled spell reqirement for item class %u",spellInfo->EquippedItemClass);
+ break;
+ }
+
+ return false;
+}
+
+void Player::RemoveItemDependentAurasAndCasts( Item * pItem )
+{
+ AuraMap& auras = GetAuras();
+ for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
+ {
+ Aura* aura = itr->second;
+
+ // skip passive (passive item dependent spells work in another way) and not self applied auras
+ SpellEntry const* spellInfo = aura->GetSpellProto();
+ if(aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
+ {
+ ++itr;
+ continue;
+ }
+
+ // skip if not item dependent or have alternative item
+ if(HasItemFitToSpellReqirements(spellInfo,pItem))
+ {
+ ++itr;
+ continue;
+ }
+
+ // no alt item, remove aura, restart check
+ RemoveAurasDueToSpell(aura->GetId());
+ itr = auras.begin();
+ }
+
+ // currently casted spells can be dependent from item
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ if( m_currentSpells[i] && m_currentSpells[i]->getState()!=SPELL_STATE_DELAYED &&
+ !HasItemFitToSpellReqirements(m_currentSpells[i]->m_spellInfo,pItem) )
+ InterruptSpell(i);
+ }
+}
+
+uint32 Player::GetResurrectionSpellId()
+{
+ // search priceless resurrection possabilities
+ uint32 prio = 0;
+ uint32 spell_id = 0;
+ AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
+ {
+ // Soulstone Resurrection // prio: 3 (max, non death persistent)
+ if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 )
+ {
+ switch((*itr)->GetId())
+ {
+ case 20707: spell_id = 3026; break; // rank 1
+ case 20762: spell_id = 20758; break; // rank 2
+ case 20763: spell_id = 20759; break; // rank 3
+ case 20764: spell_id = 20760; break; // rank 4
+ case 20765: spell_id = 20761; break; // rank 5
+ case 27239: spell_id = 27240; break; // rank 6
+ default:
+ sLog.outError("Unhandled spell %%u: S.Resurrection",(*itr)->GetId());
+ continue;
+ }
+
+ prio = 3;
+ }
+ // Twisting Nether // prio: 2 (max)
+ else if((*itr)->GetId()==23701 && roll_chance_i(10))
+ {
+ prio = 2;
+ spell_id = 23700;
+ }
+ }
+
+ // Reincarnation (passive spell) // prio: 1
+ if(prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && HasItemCount(17030,1))
+ spell_id = 21169;
+
+ return spell_id;
+}
+
+bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
+{
+ bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
+
+ // prepare data for near group iteration (PvP and !PvP cases)
+ uint32 xp = 0;
+ bool honored_kill = false;
+
+ if(Group *pGroup = GetGroup())
+ {
+ uint32 count = 0;
+ uint32 sum_level = 0;
+ Player* member_with_max_level = NULL;
+
+ pGroup->GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level);
+
+ if(member_with_max_level)
+ {
+ xp = PvP ? 0 : MaNGOS::XP::Gain(member_with_max_level, pVictim);
+
+ // skip in check PvP case (for speed, not used)
+ bool is_raid = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsRaid() && pGroup->isRaidGroup();
+ bool is_dungeon = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsDungeon();
+ float group_rate = MaNGOS::XP::xp_in_group_rate(count,is_raid);
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* pGroupGuy = itr->getSource();
+ if(!pGroupGuy)
+ continue;
+
+ if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
+ continue; // member (alive or dead) or his corpse at req. distance
+
+ // honor can be in PvP and !PvP (racial leader) cases (for alive)
+ if(pGroupGuy->isAlive() && pGroupGuy->RewardHonor(pVictim,count) && pGroupGuy==this)
+ honored_kill = true;
+
+ // xp and reputation only in !PvP case
+ if(!PvP)
+ {
+ float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
+
+ // if is in dungeon then all receive full reputation at kill
+ // rewarded any alive/dead/near_corpse group member
+ pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
+
+ // XP updated only for alive group member
+ if(pGroupGuy->isAlive())
+ {
+ uint32 itr_xp = uint32(xp*rate);
+
+ pGroupGuy->GiveXP(itr_xp, pVictim);
+ if(Pet* pet = pGroupGuy->GetPet())
+ pet->GivePetXP(itr_xp/2);
+ }
+
+ // quest objectives updated only for alive group member or dead but with not released body
+ if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse())
+ {
+ // normal creature (not pet/etc) can be only in !PvP case
+ if(pVictim->GetTypeId()==TYPEID_UNIT)
+ pGroupGuy->KilledMonster(pVictim->GetEntry(), pVictim->GetGUID());
+ }
+ }
+ }
+ }
+ }
+ else // if (!pGroup)
+ {
+ xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
+
+ // honor can be in PvP and !PvP (racial leader) cases
+ if(RewardHonor(pVictim,1))
+ honored_kill = true;
+
+ // xp and reputation only in !PvP case
+ if(!PvP)
+ {
+ RewardReputation(pVictim,1);
+ GiveXP(xp, pVictim);
+
+ if(Pet* pet = GetPet())
+ pet->GivePetXP(xp);
+
+ // normal creature (not pet/etc) can be only in !PvP case
+ if(pVictim->GetTypeId()==TYPEID_UNIT)
+ KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
+ }
+ }
+ return xp || honored_kill;
+}
+
+bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
+{
+ if(pRewardSource->GetDistance(this) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ return true;
+
+ if(isAlive())
+ return false;
+
+ Corpse* corpse = GetCorpse();
+ if(!corpse)
+ return false;
+
+ return pRewardSource->GetDistance(corpse) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE);
+}
+
+uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
+{
+ Item* item = GetWeaponForAttack(attType,true);
+
+ // unarmmed only with base attack
+ if(attType != BASE_ATTACK && !item)
+ return 0;
+
+ // weapon skill or (unarmed for base attack)
+ uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
+ return GetBaseSkillValue(skill);
+}
+
+void Player::ResurectUsingRequestData()
+{
+ 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();
+
+ TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
+}
+
+void Player::SetClientControl(Unit* target, uint8 allowMove)
+{
+ WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
+ data.append(target->GetPackGUID());
+ data << uint8(allowMove);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::UpdateZoneDependentAuras( uint32 newZone )
+{
+ // remove new continent flight forms
+ if( !isGameMaster() &&
+ GetVirtualMapForMapAndZone(GetMapId(),newZone) != 530)
+ {
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
+ RemoveSpellsCausingAura(SPELL_AURA_FLY);
+ }
+
+ // Some spells applied at enter into zone (with subzones)
+ // Human Illusion
+ // NOTE: these are removed by RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
+ if ( newZone == 2367 ) // Old Hillsbrad Foothills
+ {
+ uint32 spellid = 0;
+ // all horde races
+ if( GetTeam() == HORDE )
+ spellid = getGender() == GENDER_FEMALE ? 35481 : 35480;
+ // and some alliance races
+ else if( getRace() == RACE_NIGHTELF || getRace() == RACE_DRAENEI )
+ spellid = getGender() == GENDER_FEMALE ? 35483 : 35482;
+
+ if(spellid && !HasAura(spellid,0) )
+ CastSpell(this,spellid,true);
+ }
+}
+
+void Player::UpdateAreaDependentAuras( uint32 newArea )
+{
+ // remove auras from spells with area limitations
+ for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
+ {
+ // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
+ if(!IsSpellAllowedInLocation(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea))
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+
+ // unmount if enter in this subzone
+ if( newArea == 35)
+ RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ // Dragonmaw Illusion
+ else if( newArea == 3759 || newArea == 3966 || newArea == 3939 )
+ {
+ if( GetDummyAura(40214) )
+ {
+ if( !HasAura(40216,0) )
+ CastSpell(this,40216,true);
+ if( !HasAura(42016,0) )
+ CastSpell(this,42016,true);
+ }
+ }
+}
+
+uint32 Player::GetCorpseReclaimDelay(bool pvp) const
+{
+ if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ {
+ return copseReclaimDelay[0];
+ }
+
+ time_t now = time(NULL);
+ // 0..2 full period
+ uint32 count = (now < m_deathExpireTime) ? (m_deathExpireTime - now)/DEATH_EXPIRE_STEP : 0;
+ return copseReclaimDelay[count];
+}
+
+void Player::UpdateCorpseReclaimDelay()
+{
+ bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;
+
+ if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ return;
+
+ time_t now = time(NULL);
+ if(now < m_deathExpireTime)
+ {
+ // full and partly periods 1..3
+ uint32 count = (m_deathExpireTime - now)/DEATH_EXPIRE_STEP +1;
+ if(count < MAX_DEATH_COUNT)
+ m_deathExpireTime = now+(count+1)*DEATH_EXPIRE_STEP;
+ else
+ m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
+ }
+ else
+ m_deathExpireTime = now+DEATH_EXPIRE_STEP;
+}
+
+void Player::SendCorpseReclaimDelay(bool load)
+{
+ Corpse* corpse = GetCorpse();
+ if(!corpse)
+ return;
+
+ uint32 delay;
+ if(load)
+ {
+ if(corpse->GetGhostTime() > m_deathExpireTime)
+ return;
+
+ bool pvp = corpse->GetType()==CORPSE_RESURRECTABLE_PVP;
+
+ uint32 count;
+ if( pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ {
+ count = (m_deathExpireTime-corpse->GetGhostTime())/DEATH_EXPIRE_STEP;
+ if(count>=MAX_DEATH_COUNT)
+ count = MAX_DEATH_COUNT-1;
+ }
+ else
+ count=0;
+
+ time_t expected_time = corpse->GetGhostTime()+copseReclaimDelay[count];
+
+ time_t now = time(NULL);
+ if(now >= expected_time)
+ return;
+
+ delay = expected_time-now;
+ }
+ else
+ delay = GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP);
+
+ //! corpse reclaim delay 30 * 1000ms or longer at often deaths
+ WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
+ data << uint32(delay*1000);
+ GetSession()->SendPacket( &data );
+}
+
+Player* Player::GetNextRandomRaidMember(float radius)
+{
+ Group *pGroup = GetGroup();
+ if(!pGroup)
+ return NULL;
+
+ std::vector<Player*> nearMembers;
+ nearMembers.reserve(pGroup->GetMembersCount());
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && Target != this && IsWithinDistInMap(Target, radius) &&
+ !Target->HasInvisibilityAura() && !IsHostileTo(Target) )
+ nearMembers.push_back(Target);
+ }
+
+ if (nearMembers.empty())
+ return NULL;
+
+ uint32 randTarget = urand(0,nearMembers.size()-1);
+ return nearMembers[randTarget];
+}
+
+void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
+{
+ float water_z = m->GetWaterLevel(x,y);
+ float height_z = m->GetHeight(x,y,z, false); // use .map base surface height
+ uint8 flag1 = m->GetTerrainType(x,y);
+
+ //!Underwater check, not in water if underground or above water level
+ if (height_z <= INVALID_HEIGHT || z < (height_z-2) || z > (water_z - 2) )
+ m_isunderwater &= 0x7A;
+ else if ((z < (water_z - 2)) && (flag1 & 0x01))
+ m_isunderwater |= 0x01;
+
+ //!in lava check, anywhere under lava level
+ if ((height_z <= INVALID_HEIGHT || z < (height_z - 0)) && (flag1 == 0x00) && IsInWater())
+ m_isunderwater |= 0x80;
+}
+
+bool ItemPosCount::isContainedIn(ItemPosCountVec &vec)
+{
+ for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
+ {
+ if(itr->pos == this->pos/* && itr->count == this.count*/)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/src/game/Player.h b/src/game/Player.h
new file mode 100644
index 00000000000..d30d8e56622
--- /dev/null
+++ b/src/game/Player.h
@@ -0,0 +1,2310 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _PLAYER_H
+#define _PLAYER_H
+
+#include "Common.h"
+#include "ItemPrototype.h"
+#include "Unit.h"
+#include "Item.h"
+
+#include "Database/DatabaseEnv.h"
+#include "NPCHandler.h"
+#include "QuestDef.h"
+#include "Group.h"
+#include "Bag.h"
+#include "WorldSession.h"
+#include "Pet.h"
+#include "Util.h" // for Tokens typedef
+
+#include<string>
+#include<vector>
+
+struct Mail;
+class Channel;
+class DynamicObject;
+class Creature;
+class Pet;
+class PlayerMenu;
+class Transport;
+class UpdateMask;
+class PlayerSocial;
+
+typedef std::deque<Mail*> PlayerMails;
+
+#define PLAYER_MAX_SKILLS 127
+#define PLAYER_MAX_DAILY_QUESTS 25
+
+// Note: SPELLMOD_* values is aura types in fact
+enum SpellModType
+{
+ SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER
+ SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER
+};
+
+enum PlayerSpellState
+{
+ PLAYERSPELL_UNCHANGED = 0,
+ PLAYERSPELL_CHANGED = 1,
+ PLAYERSPELL_NEW = 2,
+ PLAYERSPELL_REMOVED = 3
+};
+
+struct PlayerSpell
+{
+ uint16 slotId : 16;
+ PlayerSpellState state : 8;
+ bool active : 1;
+ bool disabled : 1;
+};
+
+#define SPELL_WITHOUT_SLOT_ID uint16(-1)
+
+struct SpellModifier
+{
+ SpellModOp op : 8;
+ SpellModType type : 8;
+ int16 charges : 16;
+ int32 value;
+ uint64 mask;
+ uint32 spellId;
+ uint32 effectId;
+ Spell const* lastAffected;
+};
+
+typedef HM_NAMESPACE::hash_map<uint16, PlayerSpell*> PlayerSpellMap;
+typedef std::list<SpellModifier*> SpellModList;
+
+struct SpellCooldown
+{
+ time_t end;
+ uint16 itemid;
+};
+
+typedef std::map<uint32, SpellCooldown> SpellCooldowns;
+
+enum TrainerSpellState
+{
+ TRAINER_SPELL_GREEN = 0,
+ TRAINER_SPELL_RED = 1,
+ TRAINER_SPELL_GRAY = 2
+};
+
+enum ActionButtonUpdateState
+{
+ ACTIONBUTTON_UNCHANGED = 0,
+ ACTIONBUTTON_CHANGED = 1,
+ ACTIONBUTTON_NEW = 2,
+ ACTIONBUTTON_DELETED = 3
+};
+
+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 ) {}
+
+ uint16 action;
+ uint8 type;
+ uint8 misc;
+ ActionButtonUpdateState uState;
+};
+
+enum ActionButtonType
+{
+ ACTION_BUTTON_SPELL = 0,
+ ACTION_BUTTON_MACRO = 64,
+ ACTION_BUTTON_CMACRO= 65,
+ ACTION_BUTTON_ITEM = 128
+};
+
+#define MAX_ACTION_BUTTONS 132 //checked in 2.3.0
+
+typedef std::map<uint8,ActionButton> ActionButtonList;
+
+typedef std::pair<uint16, uint8> CreateSpellPair;
+
+struct PlayerCreateInfoItem
+{
+ PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {}
+
+ uint32 item_id;
+ uint32 item_amount;
+};
+
+typedef std::list<PlayerCreateInfoItem> PlayerCreateInfoItems;
+
+struct PlayerClassLevelInfo
+{
+ PlayerClassLevelInfo() : basehealth(0), basemana(0) {}
+ uint16 basehealth;
+ uint16 basemana;
+};
+
+struct PlayerClassInfo
+{
+ PlayerClassInfo() : levelInfo(NULL) { }
+
+ PlayerClassLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
+};
+
+struct PlayerLevelInfo
+{
+ PlayerLevelInfo() { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
+ uint8 stats[MAX_STATS];
+};
+
+struct PlayerInfo
+{
+ // existence checked by displayId != 0 // existence checked by displayId != 0
+ PlayerInfo() : displayId_m(0),displayId_f(0),levelInfo(NULL)
+ {
+ }
+
+ uint32 mapId;
+ uint32 zoneId;
+ float positionX;
+ float positionY;
+ float positionZ;
+ uint16 displayId_m;
+ uint16 displayId_f;
+ PlayerCreateInfoItems item;
+ std::list<CreateSpellPair> spell;
+ std::list<uint16> action[4];
+
+ PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
+};
+
+struct PvPInfo
+{
+ PvPInfo() : inHostileArea(false), endTimer(0) {}
+
+ bool inHostileArea;
+ time_t endTimer;
+};
+
+struct DuelInfo
+{
+ DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0) {}
+
+ Player *initiator;
+ Player *opponent;
+ time_t startTimer;
+ time_t startTime;
+ time_t outOfBound;
+};
+
+struct Areas
+{
+ uint32 areaID;
+ uint32 areaFlag;
+ float x1;
+ float x2;
+ float y1;
+ float y2;
+};
+
+enum FactionFlags
+{
+ FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction)
+ FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation
+ FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client)
+ FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions
+ FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions
+ FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE )
+ FACTION_FLAG_RIVAL = 0x40 // flag for the two competing outland factions
+};
+
+typedef uint32 RepListID;
+struct FactionState
+{
+ uint32 ID;
+ RepListID ReputationListID;
+ uint32 Flags;
+ int32 Standing;
+ bool Changed;
+};
+
+typedef std::map<RepListID,FactionState> FactionStateList;
+
+typedef std::map<uint32,ReputationRank> ForcedReactions;
+
+typedef std::set<uint64> GuardianPetList;
+
+struct EnchantDuration
+{
+ EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) {};
+ EnchantDuration(Item * _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), leftduration(_leftduration) { assert(item); };
+
+ Item * item;
+ EnchantmentSlot slot;
+ uint32 leftduration;
+};
+
+typedef std::list<EnchantDuration> EnchantDurationList;
+typedef std::list<Item*> ItemDurationList;
+
+struct LookingForGroupSlot
+{
+ LookingForGroupSlot() : entry(0), type(0) {}
+ bool Empty() const { return !entry && !type; }
+ void Clear() { entry = 0; type = 0; }
+ void Set(uint32 _entry, uint32 _type ) { entry = _entry; type = _type; }
+ bool Is(uint32 _entry, uint32 _type) const { return entry==_entry && type==_type; }
+ bool canAutoJoin() const { return entry && (type == 1 || type == 5); }
+
+ uint32 entry;
+ uint32 type;
+};
+
+#define MAX_LOOKING_FOR_GROUP_SLOT 3
+
+struct LookingForGroup
+{
+ LookingForGroup() {}
+ bool HaveInSlot(LookingForGroupSlot const& slot) const { return HaveInSlot(slot.entry,slot.type); }
+ bool HaveInSlot(uint32 _entry, uint32 _type) const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(slots[i].Is(_entry,_type))
+ return true;
+ return false;
+ }
+
+ bool canAutoJoin() const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(slots[i].canAutoJoin())
+ return true;
+ return false;
+ }
+
+ bool Empty() const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(!slots[i].Empty())
+ return false;
+ return more.Empty();
+ }
+
+ LookingForGroupSlot slots[MAX_LOOKING_FOR_GROUP_SLOT];
+ LookingForGroupSlot more;
+ std::string comment;
+};
+
+enum PlayerMovementType
+{
+ MOVE_ROOT = 1,
+ MOVE_UNROOT = 2,
+ MOVE_WATER_WALK = 3,
+ MOVE_LAND_WALK = 4
+};
+
+enum DrunkenState
+{
+ DRUNKEN_SOBER = 0,
+ DRUNKEN_TIPSY = 1,
+ DRUNKEN_DRUNK = 2,
+ DRUNKEN_SMASHED = 3
+};
+
+enum PlayerStateType
+{
+ /*
+ PLAYER_STATE_DANCE
+ PLAYER_STATE_SLEEP
+ PLAYER_STATE_SIT
+ PLAYER_STATE_STAND
+ PLAYER_STATE_READYUNARMED
+ PLAYER_STATE_WORK
+ PLAYER_STATE_POINT(DNR)
+ PLAYER_STATE_NONE // not used or just no state, just standing there?
+ PLAYER_STATE_STUN
+ PLAYER_STATE_DEAD
+ PLAYER_STATE_KNEEL
+ PLAYER_STATE_USESTANDING
+ PLAYER_STATE_STUN_NOSHEATHE
+ PLAYER_STATE_USESTANDING_NOSHEATHE
+ PLAYER_STATE_WORK_NOSHEATHE
+ PLAYER_STATE_SPELLPRECAST
+ PLAYER_STATE_READYRIFLE
+ PLAYER_STATE_WORK_NOSHEATHE_MINING
+ PLAYER_STATE_WORK_NOSHEATHE_CHOPWOOD
+ PLAYER_STATE_AT_EASE
+ PLAYER_STATE_READY1H
+ PLAYER_STATE_SPELLKNEELSTART
+ PLAYER_STATE_SUBMERGED
+ */
+
+ PLAYER_STATE_NONE = 0,
+ PLAYER_STATE_SIT = 1,
+ PLAYER_STATE_SIT_CHAIR = 2,
+ PLAYER_STATE_SLEEP = 3,
+ PLAYER_STATE_SIT_LOW_CHAIR = 4,
+ PLAYER_STATE_SIT_MEDIUM_CHAIR = 5,
+ PLAYER_STATE_SIT_HIGH_CHAIR = 6,
+ PLAYER_STATE_DEAD = 7,
+ PLAYER_STATE_KNEEL = 8,
+
+ PLAYER_STATE_FORM_ALL = 0x00FF0000,
+
+ PLAYER_STATE_FLAG_ALWAYS_STAND = 0x01, // byte 4
+ PLAYER_STATE_FLAG_CREEP = 0x02000000,
+ PLAYER_STATE_FLAG_UNTRACKABLE = 0x04000000,
+ PLAYER_STATE_FLAG_ALL = 0xFF000000,
+};
+
+enum PlayerFlags
+{
+ PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
+ PLAYER_FLAGS_AFK = 0x00000002,
+ PLAYER_FLAGS_DND = 0x00000004,
+ PLAYER_FLAGS_GM = 0x00000008,
+ PLAYER_FLAGS_GHOST = 0x00000010,
+ PLAYER_FLAGS_RESTING = 0x00000020,
+ PLAYER_FLAGS_FFA_PVP = 0x00000080,
+ PLAYER_FLAGS_CONTESTED_PVP = 0x00000100, // Player has been involved in a PvP combat and will be attacked by contested guards
+ PLAYER_FLAGS_IN_PVP = 0x00000200,
+ PLAYER_FLAGS_HIDE_HELM = 0x00000400,
+ PLAYER_FLAGS_HIDE_CLOAK = 0x00000800,
+ PLAYER_FLAGS_UNK1 = 0x00001000, // played long time
+ PLAYER_FLAGS_UNK2 = 0x00002000, // played too long time
+ PLAYER_FLAGS_UNK3 = 0x00008000, // strange visual effect (2.0.1), looks like PLAYER_FLAGS_GHOST flag
+ PLAYER_FLAGS_SANCTUARY = 0x00010000, // player entered sanctuary
+ PLAYER_FLAGS_UNK4 = 0x00020000, // taxi benchmark mode (on/off) (2.0.1)
+ PLAYER_UNK = 0x00040000, // 2.0.8...
+};
+
+// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
+// can't use enum for uint64 values
+#define PLAYER_TITLE_DISABLED 0x0000000000000000LL
+#define PLAYER_TITLE_NONE 0x0000000000000001LL
+#define PLAYER_TITLE_PRIVATE 0x0000000000000002LL // 1
+#define PLAYER_TITLE_CORPORAL 0x0000000000000004LL // 2
+#define PLAYER_TITLE_SERGEANT_A 0x0000000000000008LL // 3
+#define PLAYER_TITLE_MASTER_SERGEANT 0x0000000000000010LL // 4
+#define PLAYER_TITLE_SERGEANT_MAJOR 0x0000000000000020LL // 5
+#define PLAYER_TITLE_KNIGHT 0x0000000000000040LL // 6
+#define PLAYER_TITLE_KNIGHT_LIEUTENANT 0x0000000000000080LL // 7
+#define PLAYER_TITLE_KNIGHT_CAPTAIN 0x0000000000000100LL // 8
+#define PLAYER_TITLE_KNIGHT_CHAMPION 0x0000000000000200LL // 9
+#define PLAYER_TITLE_LIEUTENANT_COMMANDER 0x0000000000000400LL // 10
+#define PLAYER_TITLE_COMMANDER 0x0000000000000800LL // 11
+#define PLAYER_TITLE_MARSHAL 0x0000000000001000LL // 12
+#define PLAYER_TITLE_FIELD_MARSHAL 0x0000000000002000LL // 13
+#define PLAYER_TITLE_GRAND_MARSHAL 0x0000000000004000LL // 14
+#define PLAYER_TITLE_SCOUT 0x0000000000008000LL // 15
+#define PLAYER_TITLE_GRUNT 0x0000000000010000LL // 16
+#define PLAYER_TITLE_SERGEANT_H 0x0000000000020000LL // 17
+#define PLAYER_TITLE_SENIOR_SERGEANT 0x0000000000040000LL // 18
+#define PLAYER_TITLE_FIRST_SERGEANT 0x0000000000080000LL // 19
+#define PLAYER_TITLE_STONE_GUARD 0x0000000000100000LL // 20
+#define PLAYER_TITLE_BLOOD_GUARD 0x0000000000200000LL // 21
+#define PLAYER_TITLE_LEGIONNAIRE 0x0000000000400000LL // 22
+#define PLAYER_TITLE_CENTURION 0x0000000000800000LL // 23
+#define PLAYER_TITLE_CHAMPION 0x0000000001000000LL // 24
+#define PLAYER_TITLE_LIEUTENANT_GENERAL 0x0000000002000000LL // 25
+#define PLAYER_TITLE_GENERAL 0x0000000004000000LL // 26
+#define PLAYER_TITLE_WARLORD 0x0000000008000000LL // 27
+#define PLAYER_TITLE_HIGH_WARLORD 0x0000000010000000LL // 28
+#define PLAYER_TITLE_GLADIATOR 0x0000000020000000LL // 29
+#define PLAYER_TITLE_DUELIST 0x0000000040000000LL // 30
+#define PLAYER_TITLE_RIVAL 0x0000000080000000LL // 31
+#define PLAYER_TITLE_CHALLENGER 0x0000000100000000LL // 32
+#define PLAYER_TITLE_SCARAB_LORD 0x0000000200000000LL // 33
+#define PLAYER_TITLE_CONQUEROR 0x0000000400000000LL // 34
+#define PLAYER_TITLE_JUSTICAR 0x0000000800000000LL // 35
+#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU 0x0000001000000000LL // 36
+#define PLAYER_TITLE_MERCILESS_GLADIATOR 0x0000002000000000LL // 37
+#define PLAYER_TITLE_OF_THE_SHATTERED_SUN 0x0000004000000000LL // 38
+#define PLAYER_TITLE_HAND_OF_ADAL 0x0000008000000000LL // 39
+#define PLAYER_TITLE_VENGEFUL_GLADIATOR 0x0000010000000000LL // 40
+
+// used in PLAYER_FIELD_BYTES values
+enum PlayerFieldByteFlags
+{
+ PLAYER_FIELD_BYTE_TRACK_STEALTHED = 0x00000002,
+ PLAYER_FIELD_BYTE_RELEASE_TIMER = 0x00000008, // Display time till auto release spirit
+ PLAYER_FIELD_BYTE_NO_RELEASE_WINDOW = 0x00000010 // Display no "release spirit" window at all
+};
+
+// used in PLAYER_FIELD_BYTES2 values
+enum PlayerFieldByte2Flags
+{
+ PLAYER_FIELD_BYTE2_NONE = 0x0000,
+ PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW = 0x4000
+};
+
+enum ActivateTaxiReplies
+{
+ ERR_TAXIOK = 0,
+ ERR_TAXIUNSPECIFIEDSERVERERROR = 1,
+ ERR_TAXINOSUCHPATH = 2,
+ ERR_TAXINOTENOUGHMONEY = 3,
+ ERR_TAXITOOFARAWAY = 4,
+ ERR_TAXINOVENDORNEARBY = 5,
+ ERR_TAXINOTVISITED = 6,
+ ERR_TAXIPLAYERBUSY = 7,
+ ERR_TAXIPLAYERALREADYMOUNTED = 8,
+ ERR_TAXIPLAYERSHAPESHIFTED = 9,
+ ERR_TAXIPLAYERMOVING = 10,
+ ERR_TAXISAMENODE = 11,
+ ERR_TAXINOTSTANDING = 12
+};
+
+enum LootType
+{
+ LOOT_CORPSE = 1,
+ LOOT_SKINNING = 2,
+ LOOT_FISHING = 3,
+ LOOT_PICKPOCKETING = 4, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_DISENCHANTING = 5, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_PROSPECTING = 6, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_INSIGNIA = 7, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_FISHINGHOLE = 8 // unsupported by client, sending LOOT_FISHING instead
+};
+
+enum MirrorTimerType
+{
+ FATIGUE_TIMER = 0,
+ BREATH_TIMER = 1,
+ FIRE_TIMER = 2
+};
+
+// 2^n values
+enum PlayerExtraFlags
+{
+ // gm abilities
+ PLAYER_EXTRA_GM_ON = 0x0001,
+ PLAYER_EXTRA_GM_ACCEPT_TICKETS = 0x0002,
+ PLAYER_EXTRA_ACCEPT_WHISPERS = 0x0004,
+ PLAYER_EXTRA_TAXICHEAT = 0x0008,
+ PLAYER_EXTRA_GM_INVISIBLE = 0x0010,
+
+ // other states
+ PLAYER_EXTRA_PVP_DEATH = 0x0100 // store PvP death status until corpse creating.
+};
+
+// 2^n values
+enum AtLoginFlags
+{
+ AT_LOGIN_NONE = 0,
+ AT_LOGIN_RENAME = 1,
+ AT_LOGIN_RESET_SPELLS = 2,
+ AT_LOGIN_RESET_TALENTS = 4
+};
+
+typedef std::map<uint32, QuestStatusData> QuestStatusMap;
+
+enum QuestSlotOffsets
+{
+ QUEST_ID_OFFSET = 0,
+ QUEST_STATE_OFFSET = 1,
+ QUEST_COUNTS_OFFSET = 2,
+ QUEST_TIME_OFFSET = 3
+};
+
+#define MAX_QUEST_OFFSET 4
+
+enum QuestSlotStateMask
+{
+ QUEST_STATE_NONE = 0x0000,
+ QUEST_STATE_COMPLETE = 0x0001,
+ QUEST_STATE_FAIL = 0x0002
+};
+
+class Quest;
+class Spell;
+class Item;
+class WorldSession;
+
+enum PlayerSlots
+{
+ // first slot for item stored (in any way in player m_items data)
+ PLAYER_SLOT_START = 0,
+ // last+1 slot for item stored (in any way in player m_items data)
+ PLAYER_SLOT_END = 118,
+ PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START)
+};
+
+enum EquipmentSlots
+{
+ EQUIPMENT_SLOT_START = 0,
+ EQUIPMENT_SLOT_HEAD = 0,
+ EQUIPMENT_SLOT_NECK = 1,
+ EQUIPMENT_SLOT_SHOULDERS = 2,
+ EQUIPMENT_SLOT_BODY = 3,
+ EQUIPMENT_SLOT_CHEST = 4,
+ EQUIPMENT_SLOT_WAIST = 5,
+ EQUIPMENT_SLOT_LEGS = 6,
+ EQUIPMENT_SLOT_FEET = 7,
+ EQUIPMENT_SLOT_WRISTS = 8,
+ EQUIPMENT_SLOT_HANDS = 9,
+ EQUIPMENT_SLOT_FINGER1 = 10,
+ EQUIPMENT_SLOT_FINGER2 = 11,
+ EQUIPMENT_SLOT_TRINKET1 = 12,
+ EQUIPMENT_SLOT_TRINKET2 = 13,
+ EQUIPMENT_SLOT_BACK = 14,
+ EQUIPMENT_SLOT_MAINHAND = 15,
+ EQUIPMENT_SLOT_OFFHAND = 16,
+ EQUIPMENT_SLOT_RANGED = 17,
+ EQUIPMENT_SLOT_TABARD = 18,
+ EQUIPMENT_SLOT_END = 19
+};
+
+enum InventorySlots
+{
+ INVENTORY_SLOT_BAG_0 = 255,
+ INVENTORY_SLOT_BAG_START = 19,
+ INVENTORY_SLOT_BAG_1 = 19,
+ INVENTORY_SLOT_BAG_2 = 20,
+ INVENTORY_SLOT_BAG_3 = 21,
+ INVENTORY_SLOT_BAG_4 = 22,
+ INVENTORY_SLOT_BAG_END = 23,
+
+ INVENTORY_SLOT_ITEM_START = 23,
+ INVENTORY_SLOT_ITEM_1 = 23,
+ INVENTORY_SLOT_ITEM_2 = 24,
+ INVENTORY_SLOT_ITEM_3 = 25,
+ INVENTORY_SLOT_ITEM_4 = 26,
+ INVENTORY_SLOT_ITEM_5 = 27,
+ INVENTORY_SLOT_ITEM_6 = 28,
+ INVENTORY_SLOT_ITEM_7 = 29,
+ INVENTORY_SLOT_ITEM_8 = 30,
+ INVENTORY_SLOT_ITEM_9 = 31,
+ INVENTORY_SLOT_ITEM_10 = 32,
+ INVENTORY_SLOT_ITEM_11 = 33,
+ INVENTORY_SLOT_ITEM_12 = 34,
+ INVENTORY_SLOT_ITEM_13 = 35,
+ INVENTORY_SLOT_ITEM_14 = 36,
+ INVENTORY_SLOT_ITEM_15 = 37,
+ INVENTORY_SLOT_ITEM_16 = 38,
+ INVENTORY_SLOT_ITEM_END = 39
+};
+
+enum BankSlots
+{
+ BANK_SLOT_ITEM_START = 39,
+ BANK_SLOT_ITEM_1 = 39,
+ BANK_SLOT_ITEM_2 = 40,
+ BANK_SLOT_ITEM_3 = 41,
+ BANK_SLOT_ITEM_4 = 42,
+ BANK_SLOT_ITEM_5 = 43,
+ BANK_SLOT_ITEM_6 = 44,
+ BANK_SLOT_ITEM_7 = 45,
+ BANK_SLOT_ITEM_8 = 46,
+ BANK_SLOT_ITEM_9 = 47,
+ BANK_SLOT_ITEM_10 = 48,
+ BANK_SLOT_ITEM_11 = 49,
+ BANK_SLOT_ITEM_12 = 50,
+ BANK_SLOT_ITEM_13 = 51,
+ BANK_SLOT_ITEM_14 = 52,
+ BANK_SLOT_ITEM_15 = 53,
+ BANK_SLOT_ITEM_16 = 54,
+ BANK_SLOT_ITEM_17 = 55,
+ BANK_SLOT_ITEM_18 = 56,
+ BANK_SLOT_ITEM_19 = 57,
+ BANK_SLOT_ITEM_20 = 58,
+ BANK_SLOT_ITEM_21 = 59,
+ BANK_SLOT_ITEM_22 = 60,
+ BANK_SLOT_ITEM_23 = 61,
+ BANK_SLOT_ITEM_24 = 62,
+ BANK_SLOT_ITEM_25 = 63,
+ BANK_SLOT_ITEM_26 = 64,
+ BANK_SLOT_ITEM_27 = 65,
+ BANK_SLOT_ITEM_28 = 66,
+ BANK_SLOT_ITEM_END = 67,
+
+ BANK_SLOT_BAG_START = 67,
+ BANK_SLOT_BAG_1 = 67,
+ BANK_SLOT_BAG_2 = 68,
+ BANK_SLOT_BAG_3 = 69,
+ BANK_SLOT_BAG_4 = 70,
+ BANK_SLOT_BAG_5 = 71,
+ BANK_SLOT_BAG_6 = 72,
+ BANK_SLOT_BAG_7 = 73,
+ BANK_SLOT_BAG_END = 74
+};
+
+enum BuyBackSlots
+{
+ // stored in m_buybackitems
+ BUYBACK_SLOT_START = 74,
+ BUYBACK_SLOT_1 = 74,
+ BUYBACK_SLOT_2 = 75,
+ BUYBACK_SLOT_3 = 76,
+ BUYBACK_SLOT_4 = 77,
+ BUYBACK_SLOT_5 = 78,
+ BUYBACK_SLOT_6 = 79,
+ BUYBACK_SLOT_7 = 80,
+ BUYBACK_SLOT_8 = 81,
+ BUYBACK_SLOT_9 = 82,
+ BUYBACK_SLOT_10 = 83,
+ BUYBACK_SLOT_11 = 84,
+ BUYBACK_SLOT_12 = 85,
+ BUYBACK_SLOT_END = 86
+};
+
+enum KeyRingSlots
+{
+ KEYRING_SLOT_START = 86,
+ KEYRING_SLOT_END = 118
+};
+
+struct ItemPosCount
+{
+ ItemPosCount(uint16 _pos, uint8 _count) : pos(_pos), count(_count) {}
+ bool isContainedIn(std::vector<ItemPosCount>&);
+ uint16 pos;
+ uint8 count;
+};
+typedef std::vector<ItemPosCount> ItemPosCountVec;
+
+enum SwitchWeapon
+{
+ DEFAULT_SWITCH_WEAPON = 1500, //cooldown in ms
+ ROGUE_SWITCH_WEAPON = 1000
+};
+
+enum TradeSlots
+{
+ TRADE_SLOT_COUNT = 7,
+ TRADE_SLOT_TRADED_COUNT = 6,
+ TRADE_SLOT_NONTRADED = 6
+};
+
+enum TransferAbortReason
+{
+ TRANSFER_ABORT_MAX_PLAYERS = 0x0001, // Transfer Aborted: instance is full
+ TRANSFER_ABORT_NOT_FOUND = 0x0002, // Transfer Aborted: instance not found
+ TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x0003, // You have entered too many instances recently.
+ TRANSFER_ABORT_ZONE_IN_COMBAT = 0x0005, // Unable to zone in while an encounter is in progress.
+ TRANSFER_ABORT_INSUF_EXPAN_LVL1 = 0x0106, // You must have TBC expansion installed to access this area.
+ TRANSFER_ABORT_DIFFICULTY1 = 0x0007, // Normal difficulty mode is not available for %s.
+ TRANSFER_ABORT_DIFFICULTY2 = 0x0107, // Heroic difficulty mode is not available for %s.
+ TRANSFER_ABORT_DIFFICULTY3 = 0x0207 // Epic difficulty mode is not available for %s.
+};
+
+enum InstanceResetWarningType
+{
+ RAID_INSTANCE_WARNING_HOURS = 1, // WARNING! %s is scheduled to reset in %d hour(s).
+ RAID_INSTANCE_WARNING_MIN = 2, // WARNING! %s is scheduled to reset in %d minute(s)!
+ RAID_INSTANCE_WARNING_MIN_SOON = 3, // WARNING! %s is scheduled to reset in %d minute(s). Please exit the zone or you will be returned to your bind location!
+ RAID_INSTANCE_WELCOME = 4 // Welcome to %s. This raid instance is scheduled to reset in %s.
+};
+
+struct MovementInfo
+{
+ // common
+ //uint32 flags;
+ uint8 unk1;
+ uint32 time;
+ float x, y, z, o;
+ // transport
+ uint64 t_guid;
+ float t_x, t_y, t_z, t_o;
+ uint32 t_time;
+ // swimming and unk
+ float s_pitch;
+ // last fall time
+ uint32 fallTime;
+ // jumping
+ float j_unk, j_sinAngle, j_cosAngle, j_xyspeed;
+ // spline
+ float u_unk1;
+
+ MovementInfo()
+ {
+ //flags =
+ time = t_time = fallTime = 0;
+ unk1 = 0;
+ x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_unk = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
+ t_guid = 0;
+ }
+
+ /*void SetMovementFlags(uint32 _flags)
+ {
+ flags = _flags;
+ }*/
+};
+
+// flags that use in movement check for example at spell casting
+MovementFlags const movementFlagsMask = MovementFlags(
+ MOVEMENTFLAG_FORWARD |MOVEMENTFLAG_BACKWARD |MOVEMENTFLAG_STRAFE_LEFT|MOVEMENTFLAG_STRAFE_RIGHT|
+ MOVEMENTFLAG_PITCH_UP|MOVEMENTFLAG_PITCH_DOWN|MOVEMENTFLAG_FLY_UNK1 |
+ MOVEMENTFLAG_JUMPING |MOVEMENTFLAG_FALLING |MOVEMENTFLAG_FLY_UP |
+ MOVEMENTFLAG_FLYING |MOVEMENTFLAG_SPLINE
+);
+
+MovementFlags const movementOrTurningFlagsMask = MovementFlags(
+ movementFlagsMask | MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT
+);
+class InstanceSave;
+
+enum RestType
+{
+ REST_TYPE_NO = 0,
+ REST_TYPE_IN_TAVERN = 1,
+ REST_TYPE_IN_CITY = 2
+};
+
+enum DuelCompleteType
+{
+ DUEL_INTERUPTED = 0,
+ DUEL_WON = 1,
+ DUEL_FLED = 2
+};
+
+enum TeleportToOptions
+{
+ TELE_TO_GM_MODE = 0x01,
+ TELE_TO_NOT_LEAVE_TRANSPORT = 0x02,
+ TELE_TO_NOT_LEAVE_COMBAT = 0x04,
+ TELE_TO_NOT_UNSUMMON_PET = 0x08,
+ TELE_TO_SPELL = 0x10,
+};
+
+/// Type of environmental damages
+enum EnviromentalDamage
+{
+ DAMAGE_EXHAUSTED = 0,
+ DAMAGE_DROWNING = 1,
+ DAMAGE_FALL = 2,
+ DAMAGE_LAVA = 3,
+ DAMAGE_SLIME = 4,
+ DAMAGE_FIRE = 5,
+ DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss
+};
+
+// used at player loading query list preparing, and later result selection
+enum PlayerLoginQueryIndex
+{
+ PLAYER_LOGIN_QUERY_LOADFROM = 0,
+ PLAYER_LOGIN_QUERY_LOADGROUP = 1,
+ PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES = 2,
+ PLAYER_LOGIN_QUERY_LOADAURAS = 3,
+ PLAYER_LOGIN_QUERY_LOADSPELLS = 4,
+ PLAYER_LOGIN_QUERY_LOADQUESTSTATUS = 5,
+ PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS = 6,
+ PLAYER_LOGIN_QUERY_LOADTUTORIALS = 7, // common for all characters for some account at specific realm
+ PLAYER_LOGIN_QUERY_LOADREPUTATION = 8,
+ PLAYER_LOGIN_QUERY_LOADINVENTORY = 9,
+ PLAYER_LOGIN_QUERY_LOADACTIONS = 10,
+ PLAYER_LOGIN_QUERY_LOADMAILCOUNT = 11,
+ PLAYER_LOGIN_QUERY_LOADMAILDATE = 12,
+ PLAYER_LOGIN_QUERY_LOADSOCIALLIST = 13,
+ PLAYER_LOGIN_QUERY_LOADHOMEBIND = 14,
+ PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 15,
+ PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16,
+ PLAYER_LOGIN_QUERY_LOADGUILD = 17,
+};
+
+#define MAX_PLAYER_LOGIN_QUERY 18
+
+// Player summoning auto-decline time (in secs)
+#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
+#define MAX_MONEY_AMOUNT (0x7FFFFFFF-1)
+
+struct InstancePlayerBind
+{
+ InstanceSave *save;
+ bool perm;
+ /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players
+ that aren't already permanently bound when they are inside when a boss is killed
+ or when they enter an instance that the group leader is permanently bound to. */
+ InstancePlayerBind() : save(NULL), perm(false) {}
+};
+
+class MANGOS_DLL_SPEC PlayerTaxi
+{
+ public:
+ PlayerTaxi();
+ ~PlayerTaxi() {}
+ // Nodes
+ void InitTaxiNodesForLevel(uint32 race, uint32 level);
+ void LoadTaxiMask(const char* data);
+ void SaveTaxiMask(const char* data);
+
+ uint32 GetTaximask( uint8 index ) const { return m_taximask[index]; }
+ bool IsTaximaskNodeKnown(uint32 nodeidx) const
+ {
+ uint8 field = uint8((nodeidx - 1) / 32);
+ uint32 submask = 1<<((nodeidx-1)%32);
+ return (m_taximask[field] & submask) == submask;
+ }
+ bool SetTaximaskNode(uint32 nodeidx)
+ {
+ uint8 field = uint8((nodeidx - 1) / 32);
+ uint32 submask = 1<<((nodeidx-1)%32);
+ if ((m_taximask[field] & submask) != submask )
+ {
+ m_taximask[field] |= submask;
+ return true;
+ }
+ else
+ return false;
+ }
+ void AppendTaximaskTo(ByteBuffer& data,bool all);
+
+ // Destinations
+ bool LoadTaxiDestinationsFromString(std::string values);
+ std::string SaveTaxiDestinationsToString();
+
+ void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
+ void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
+ uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
+ uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
+ uint32 GetCurrentTaxiPath() const;
+ uint32 NextTaxiDestination()
+ {
+ m_TaxiDestinations.pop_front();
+ return GetTaxiDestination();
+ }
+ bool empty() const { return m_TaxiDestinations.empty(); }
+ private:
+ TaxiMask m_taximask;
+ std::deque<uint32> m_TaxiDestinations;
+};
+
+class MANGOS_DLL_SPEC Player : public Unit
+{
+ friend class WorldSession;
+ friend void Item::AddToUpdateQueueOf(Player *player);
+ friend void Item::RemoveFromUpdateQueueOf(Player *player);
+ public:
+ explicit Player (WorldSession *session);
+ ~Player ( );
+
+ void CleanupsBeforeDelete();
+
+ static UpdateMask updateVisualBits;
+ static void InitVisibleBits();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0);
+
+ bool TeleportTo(WorldLocation const &loc, uint32 options = 0)
+ {
+ return TeleportTo(loc.mapid, loc.x, loc.y, loc.z, options);
+ }
+
+ void SetSummonPoint(uint32 mapid, float x, float y, float z)
+ {
+ m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY;
+ m_summon_mapid = mapid;
+ m_summon_x = x;
+ m_summon_y = y;
+ m_summon_z = z;
+ }
+ void SummonIfPossible(bool agree);
+
+ bool Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId );
+
+ void Update( uint32 time );
+
+ void BuildEnumData( QueryResult * result, WorldPacket * p_data );
+
+ void SetInWater(bool apply);
+
+ bool IsInWater() const { return m_isInWater; }
+ bool IsUnderWater() const;
+
+ void SendInitialPacketsBeforeAddToMap();
+ void SendInitialPacketsAfterAddToMap();
+ void SendTransferAborted(uint32 mapid, uint16 reason);
+ void SendInstanceResetWarning(uint32 mapid, uint32 time);
+
+ bool CanInteractWithNPCs(bool alive = true) const;
+
+ bool ToggleAFK();
+ bool ToggleDND();
+ bool isAFK() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_AFK); };
+ bool isDND() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_DND); };
+ uint8 chatTag() const;
+ std::string afkMsg;
+ std::string dndMsg;
+
+ PlayerSocial *GetSocial() { return m_social; }
+
+ PlayerTaxi m_taxi;
+ void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(),getLevel()); }
+ bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id = 0 , Creature* npc = NULL);
+ // mount_id can be used in scripting calls
+ bool isAcceptTickets() const { return GetSession()->GetSecurity() >= SEC_GAMEMASTER && (m_ExtraFlags & PLAYER_EXTRA_GM_ACCEPT_TICKETS); }
+ void SetAcceptTicket(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_GM_ACCEPT_TICKETS; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_ACCEPT_TICKETS; }
+ 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; }
+ bool isGameMaster() const { return m_ExtraFlags & PLAYER_EXTRA_GM_ON; }
+ void SetGameMaster(bool on);
+ bool isTaxiCheater() const { return m_ExtraFlags & PLAYER_EXTRA_TAXICHEAT; }
+ void SetTaxiCheater(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_TAXICHEAT; else m_ExtraFlags &= ~PLAYER_EXTRA_TAXICHEAT; }
+ bool isGMVisible() const { return !(m_ExtraFlags & PLAYER_EXTRA_GM_INVISIBLE); }
+ void SetGMVisible(bool on);
+ void SetPvPDeath(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; }
+
+ void GiveXP(uint32 xp, Unit* victim);
+ void GiveLevel(uint32 level);
+ void InitStatsForLevel(bool reapplyMods = false);
+
+ // 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]; };
+
+ void setDeathState(DeathState s); // overwrite Unit::setDeathState
+
+ void InnEnter (int time,uint32 mapid, float x,float y,float z)
+ {
+ inn_pos_mapid = mapid;
+ inn_pos_x = x;
+ inn_pos_y = y;
+ inn_pos_z = z;
+ time_inn_enter = time;
+ };
+
+ float GetRestBonus() const { return m_rest_bonus; };
+ void SetRestBonus(float rest_bonus_new);
+
+ RestType GetRestType() const { return rest_type; };
+ void SetRestType(RestType n_r_type) { rest_type = n_r_type; };
+
+ uint32 GetInnPosMapId() const { return inn_pos_mapid; };
+ float GetInnPosX() const { return inn_pos_x; };
+ float GetInnPosY() const { return inn_pos_y; };
+ float GetInnPosZ() const { return inn_pos_z; };
+
+ int GetTimeInnEnter() const { return time_inn_enter; };
+ void UpdateInnerTime (int time) { time_inn_enter = time; };
+
+ void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
+ void RemoveMiniPet();
+ Pet* GetMiniPet();
+ void SetMiniPet(Pet* pet) { m_miniPet = pet->GetGUID(); }
+ void RemoveGuardians();
+ bool HasGuardianWithEntry(uint32 entry);
+ void AddGuardian(Pet* pet) { m_guardianPets.insert(pet->GetGUID()); }
+ GuardianPetList const& GetGuardians() const { return m_guardianPets; }
+ void Uncharm();
+
+ void Say(std::string text, const uint32 language);
+ void Yell(std::string text, const uint32 language);
+ void TextEmote(std::string text);
+ void Whisper(std::string text, const uint32 language,uint64 receiver);
+ void BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const;
+
+ /*********************************************************/
+ /*** STORAGE SYSTEM ***/
+ /*********************************************************/
+
+ void SetVirtualItemSlot( uint8 i, Item* item);
+ void SetSheath( uint32 sheathed );
+ uint8 FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const;
+ uint32 GetItemCount( uint32 item, bool inBankAlso = false, Item* skipItem = NULL ) const;
+ Item* GetItemByGuid( uint64 guid ) const;
+ Item* GetItemByPos( uint16 pos ) const;
+ Item* GetItemByPos( uint8 bag, uint8 slot ) const;
+ Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
+ Item* GetShield(bool useable = false) const;
+ static uint32 GetAttackBySlot( uint8 slot ); // MAX_ATTACK if not weapon slot
+ std::vector<Item *> &GetItemUpdateQueue() { return m_itemUpdateQueue; }
+ static bool IsInventoryPos( uint16 pos ) { return IsInventoryPos(pos >> 8,pos & 255); }
+ static bool IsInventoryPos( uint8 bag, uint8 slot );
+ static bool IsEquipmentPos( uint16 pos ) { return IsEquipmentPos(pos >> 8,pos & 255); }
+ static bool IsEquipmentPos( uint8 bag, uint8 slot );
+ static bool IsBagPos( uint16 pos );
+ static bool IsBankPos( uint16 pos ) { return IsBankPos(pos >> 8,pos & 255); }
+ static bool IsBankPos( uint8 bag, uint8 slot );
+ bool HasBankBagSlot( uint8 slot ) const;
+ bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false ) const;
+ bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL);
+ Item* GetItemOrItemWithGemEquipped( uint32 item ) const;
+ uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(),pItem->GetCount(),pItem); }
+ uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry,count,NULL); }
+ uint8 CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const
+ {
+ return _CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count );
+ }
+ uint8 CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap = false ) const
+ {
+ if(!pItem)
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+ uint32 count = pItem->GetCount();
+ return _CanStoreItem( bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL );
+
+ }
+ uint8 CanStoreItems( Item **pItem,int count) const;
+ uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const;
+ uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const;
+ uint8 CanUnequipItems( uint32 item, uint32 count ) const;
+ uint8 CanUnequipItem( uint16 src, bool swap ) const;
+ uint8 CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const;
+ uint8 CanUseItem( Item *pItem, bool not_loading = true ) const;
+ bool HasItemTotemCategory( uint32 TotemCategory ) const;
+ bool CanUseItem( ItemPrototype const *pItem );
+ uint8 CanUseAmmo( uint32 item ) const;
+ Item* StoreNewItem( ItemPosCountVec const& pos, uint32 item, bool update,int32 randomPropertyId = 0 );
+ Item* StoreItem( ItemPosCountVec const& pos, Item *pItem, bool update );
+ Item* EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update );
+ Item* EquipItem( uint16 pos, Item *pItem, bool update );
+ void AutoUnequipOffhandIfNeed();
+
+ uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const;
+ uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const;
+
+ void ApplyEquipCooldown( Item * pItem );
+ void SetAmmo( uint32 item );
+ void RemoveAmmo();
+ float GetAmmoDPS() const { return m_ammoDPS; }
+ bool CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const;
+ void QuickEquipItem( uint16 pos, Item *pItem);
+ void VisualizeItem( uint8 slot, Item *pItem);
+ void SetVisibleItemSlot(uint8 slot, Item *pItem);
+ Item* BankItem( ItemPosCountVec const& dest, Item *pItem, bool update )
+ {
+ return StoreItem( dest, pItem, update);
+ }
+ Item* BankItem( uint16 pos, Item *pItem, bool update );
+ void RemoveItem( uint8 bag, uint8 slot, bool update );
+ void MoveItemFromInventory(uint8 bag, uint8 slot, bool update);
+ // in trade, auction, guild bank, mail....
+ void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false);
+ // in trade, guild bank, mail....
+ void RemoveItemDependentAurasAndCasts( Item * pItem );
+ void DestroyItem( uint8 bag, uint8 slot, bool update );
+ void DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check = false);
+ void DestroyItemCount( Item* item, uint32& count, bool update );
+ void DestroyConjuredItems( bool update );
+ void DestroyZoneLimitedItem( bool update, uint32 new_zone );
+ void SplitItem( uint16 src, uint16 dst, uint32 count );
+ void SwapItem( uint16 src, uint16 dst );
+ void AddItemToBuyBackSlot( Item *pItem );
+ Item* GetItemFromBuyBackSlot( uint32 slot );
+ void RemoveItemFromBuyBackSlot( uint32 slot, bool del );
+ uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; }
+ void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 );
+ void SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param );
+ void SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param );
+ void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; }
+ void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; }
+ uint32 GetWeaponProficiency() const { return m_WeaponProficiency; }
+ uint32 GetArmorProficiency() const { return m_ArmorProficiency; }
+ bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; }
+ bool IsUseEquipedWeapon( bool mainhand ) const
+ {
+ // disarm applied only to mainhand weapon
+ return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISARMED) );
+ }
+ 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);
+
+ float GetReputationPriceDiscount( Creature const* pCreature ) const;
+ Player* GetTrader() const { return pTrader; }
+ void ClearTrade();
+ void TradeCancel(bool sendback);
+ uint16 GetItemPosByTradeSlot(uint32 slot) const { return tradeItems[slot]; }
+
+ void UpdateEnchantTime(uint32 time);
+ void UpdateItemDuration(uint32 time, bool realtimeonly=false);
+ void AddEnchantmentDurations(Item *item);
+ void RemoveEnchantmentDurations(Item *item);
+ void RemoveAllEnchantments(EnchantmentSlot slot);
+ void AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration);
+ void ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur = true, bool ignore_condition = false);
+ void ApplyEnchantment(Item *item,bool apply);
+ void SendEnchantmentDurations();
+ void AddItemDurations(Item *item);
+ void RemoveItemDurations(Item *item);
+ void SendItemDurations();
+ void LoadCorpse();
+ void LoadPet();
+
+ uint32 m_stableSlots;
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+
+ void PrepareQuestMenu( uint64 guid );
+ void SendPreparedQuest( uint64 guid );
+ bool IsActiveQuest( uint32 quest_id ) const;
+ Quest const *GetNextQuest( uint64 guid, Quest const *pQuest );
+ bool CanSeeStartQuest( Quest const *pQuest );
+ bool CanTakeQuest( Quest const *pQuest, bool msg );
+ bool CanAddQuest( Quest const *pQuest, bool msg );
+ bool CanCompleteQuest( uint32 quest_id );
+ bool CanCompleteRepeatableQuest(Quest const *pQuest);
+ bool CanRewardQuest( Quest const *pQuest, bool msg );
+ bool CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg );
+ void AddQuest( Quest const *pQuest, Object *questGiver );
+ void CompleteQuest( uint32 quest_id );
+ void IncompleteQuest( uint32 quest_id );
+ void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true );
+ void FailQuest( uint32 quest_id );
+ void FailTimedQuest( uint32 quest_id );
+ bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg );
+ bool SatisfyQuestLevel( Quest const* qInfo, bool msg );
+ bool SatisfyQuestLog( bool msg );
+ bool SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg );
+ bool SatisfyQuestRace( Quest const* qInfo, bool msg );
+ bool SatisfyQuestReputation( Quest const* qInfo, bool msg );
+ bool SatisfyQuestStatus( Quest const* qInfo, bool msg );
+ bool SatisfyQuestTimed( Quest const* qInfo, bool msg );
+ bool SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg );
+ bool SatisfyQuestNextChain( Quest const* qInfo, bool msg );
+ bool SatisfyQuestPrevChain( Quest const* qInfo, bool msg );
+ bool SatisfyQuestDay( Quest const* qInfo, bool msg );
+ bool GiveQuestSourceItem( Quest const *pQuest );
+ bool TakeQuestSourceItem( uint32 quest_id, bool msg );
+ bool GetQuestRewardStatus( uint32 quest_id ) const;
+ QuestStatus GetQuestStatus( uint32 quest_id ) const;
+ void SetQuestStatus( uint32 quest_id, QuestStatus status );
+
+ void SetDailyQuestStatus( uint32 quest_id );
+ void ResetDailyQuestStatus();
+
+ uint16 FindQuestSlot( uint32 quest_id ) const;
+ uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET); }
+ uint32 GetQuestSlotState(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET); }
+ uint32 GetQuestSlotCounters(uint16 slot)const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET); }
+ uint8 GetQuestSlotCounter(uint16 slot,uint8 counter) const { return GetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter); }
+ uint32 GetQuestSlotTime(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET); }
+ void SetQuestSlot(uint16 slot,uint32 quest_id, uint32 timer = 0)
+ {
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET,quest_id);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,0);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,0);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer);
+ }
+ void SetQuestSlotCounter(uint16 slot,uint8 counter,uint8 count) { SetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter,count); }
+ void SetQuestSlotState(uint16 slot,uint32 state) { SetFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
+ void RemoveQuestSlotState(uint16 slot,uint32 state) { RemoveFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
+ void SetQuestSlotTimer(uint16 slot,uint32 timer) { SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer); }
+ void SwapQuestSlot(uint16 slot1,uint16 slot2)
+ {
+ for (int i = 0; i < MAX_QUEST_OFFSET ; ++i )
+ {
+ uint32 temp1 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i);
+ uint32 temp2 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i);
+
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i, temp2);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i, temp1);
+ }
+ }
+ uint32 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry);
+ void AdjustQuestReqItemCount( Quest const* pQuest );
+ void AreaExploredOrEventHappens( uint32 questId );
+ 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 CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id );
+ void TalkedToCreature( uint32 entry, uint64 guid );
+ void MoneyChanged( uint32 value );
+ bool HasQuestForItem( uint32 itemid ) const;
+ bool HasQuestForGO(int32 GOId);
+ void UpdateForQuestsGO();
+ bool CanShareQuest(uint32 quest_id) const;
+
+ void SendQuestComplete( uint32 quest_id );
+ void SendQuestReward( Quest const *pQuest, uint32 XP, Object* questGiver );
+ void SendQuestFailed( uint32 quest_id );
+ void SendQuestTimerFailed( uint32 quest_id );
+ void SendCanTakeQuestResponse( uint32 msg );
+ void SendPushToPartyResponse( Player *pPlayer, uint32 msg );
+ void SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count );
+ void SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count );
+
+ uint64 GetDivider() { return m_divider; };
+ void SetDivider( uint64 guid ) { m_divider = guid; };
+
+ uint32 GetInGameTime() { return m_ingametime; };
+
+ void SetInGameTime( uint32 time ) { m_ingametime = time; };
+
+ void AddTimedQuest( uint32 quest_id ) { m_timedquests.insert(quest_id); }
+
+ /*********************************************************/
+ /*** LOAD SYSTEM ***/
+ /*********************************************************/
+
+ bool LoadFromDB(uint32 guid, SqlQueryHolder *holder);
+ bool MinimalLoadFromDB(QueryResult *result, uint32 guid);
+ static bool LoadValuesArrayFromDB(Tokens& data,uint64 guid);
+ static uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index);
+ static float GetFloatValueFromArray(Tokens const& data, uint16 index);
+ static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid);
+ static float GetFloatValueFromDB(uint16 index, uint64 guid);
+ static uint32 GetZoneIdFromDB(uint64 guid);
+ static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid);
+
+ /*********************************************************/
+ /*** SAVE SYSTEM ***/
+ /*********************************************************/
+
+ void SaveToDB();
+ void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing
+ void SaveGoldToDB() { SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID()); }
+ static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid);
+ static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value);
+ static void SetFloatValueInArray(Tokens& data,uint16 index, float value);
+ static void SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid);
+ static void SetFloatValueInDB(uint16 index, float value, uint64 guid);
+ static void SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid);
+
+ bool m_mailsLoaded;
+ bool m_mailsUpdated;
+
+ 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();
+ void Regenerate(Powers power);
+ void RegenerateHealth();
+ void setRegenTimer(uint32 time) {m_regenTimer = time;}
+ void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
+
+ uint32 GetMoney() { return GetUInt32Value (PLAYER_FIELD_COINAGE); }
+ void ModifyMoney( int32 d )
+ {
+ if(d < 0)
+ SetMoney (GetMoney() > uint32(-d) ? GetMoney() + d : 0);
+ else
+ SetMoney (GetMoney() < MAX_MONEY_AMOUNT - d ? GetMoney() + d : MAX_MONEY_AMOUNT);
+
+ // "At Gold Limit"
+ if(GetMoney() >= MAX_MONEY_AMOUNT)
+ SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD,NULL,NULL);
+ }
+ void SetMoney( uint32 value )
+ {
+ SetUInt32Value (PLAYER_FIELD_COINAGE, value);
+ MoneyChanged( value );
+ }
+
+ uint32 GetTutorialInt(uint32 intId )
+ {
+ ASSERT( (intId < 8) );
+ return m_Tutorials[intId];
+ }
+
+ void SetTutorialInt(uint32 intId, uint32 value)
+ {
+ ASSERT( (intId < 8) );
+ if(m_Tutorials[intId]!=value)
+ {
+ m_Tutorials[intId] = value;
+ m_TutorialsChanged = true;
+ }
+ }
+
+ QuestStatusMap& getQuestStatusMap() { return mQuestStatus; };
+
+ const uint64& GetSelection( ) const { return m_curSelection; }
+ void SetSelection(const uint64 &guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); }
+
+ uint8 GetComboPoints() { return m_comboPoints; }
+ uint64 GetComboTarget() { return m_comboTarget; }
+
+ void AddComboPoints(Unit* target, int8 count);
+ void ClearComboPoints();
+ void SendComboPoints();
+
+ void SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0);
+ void SendNewMail();
+ void UpdateNextMailTimeAndUnreads();
+ void AddNewMailDeliverTime(time_t deliver_time);
+ bool IsMailsLoaded() const { return m_mailsLoaded; }
+
+ //void SetMail(Mail *m);
+ void RemoveMail(uint32 id);
+
+ void AddMail(Mail* mail) { m_mail.push_front(mail);}// for call from WorldSession::SendMailTo
+ uint32 GetMailSize() { return m_mail.size();};
+ Mail* GetMail(uint32 id);
+
+ PlayerMails::iterator GetmailBegin() { return m_mail.begin();};
+ PlayerMails::iterator GetmailEnd() { return m_mail.end();};
+
+ /*********************************************************/
+ /*** MAILED ITEMS SYSTEM ***/
+ /*********************************************************/
+
+ uint8 unReadMails;
+ time_t m_nextMailDelivereTime;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
+
+ ItemMap mMitems; //template defined in objectmgr.cpp
+
+ Item* GetMItem(uint32 id)
+ {
+ ItemMap::const_iterator itr = mMitems.find(id);
+ if (itr != mMitems.end())
+ return itr->second;
+
+ return NULL;
+ }
+
+ void AddMItem(Item* it)
+ {
+ ASSERT( it );
+ //assert deleted, because items can be added before loading
+ mMitems[it->GetGUIDLow()] = it;
+ }
+
+ bool RemoveMItem(uint32 id)
+ {
+ ItemMap::iterator i = mMitems.find(id);
+ if (i == mMitems.end())
+ return false;
+
+ mMitems.erase(i);
+ return true;
+ }
+
+ void PetSpellInitialize();
+ void CharmSpellInitialize();
+ void PossessSpellInitialize();
+ bool HasSpell(uint32 spell) const;
+ TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
+ bool IsSpellFitByClassAndRace( uint32 spell_id ) const;
+
+ void SendProficiency(uint8 pr1, uint32 pr2);
+ void SendInitialSpells();
+ bool addSpell(uint32 spell_id, bool active, bool learning = true, bool loading = false, uint16 slot_id=SPELL_WITHOUT_SLOT_ID, bool disabled = false);
+ void learnSpell(uint32 spell_id);
+ void removeSpell(uint32 spell_id, bool disabled = false);
+ void resetSpells();
+ void learnDefaultSpells(bool loading = false);
+ void learnQuestRewardedSpells();
+ void learnQuestRewardedSpells(Quest const* quest);
+
+ uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); }
+ void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1,points); }
+ bool resetTalents(bool no_cost = false);
+ uint32 resetTalentsCost() const;
+ void InitTalentForLevel();
+
+ uint32 GetFreePrimaryProffesionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); }
+ void SetFreePrimaryProffesions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2,profs); }
+ void InitPrimaryProffesions();
+
+ PlayerSpellMap const& GetSpellMap() const { return m_spells; }
+ PlayerSpellMap & GetSpellMap() { return m_spells; }
+
+ void AddSpellMod(SpellModifier* mod, bool apply);
+ int32 GetTotalFlatMods(uint32 spellId, SpellModOp op);
+ int32 GetTotalPctMods(uint32 spellId, SpellModOp op);
+ bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell = NULL);
+ template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell = NULL);
+ void RemoveSpellMods(Spell const* spell);
+
+ bool HasSpellCooldown(uint32 spell_id) const
+ {
+ SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
+ return itr != m_spellCooldowns.end() && itr->second.end > time(NULL);
+ }
+ uint32 GetSpellCooldownDelay(uint32 spell_id) const
+ {
+ SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
+ time_t t = time(NULL);
+ return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0;
+ }
+ void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time);
+ void SendCooldownEvent(SpellEntry const *spellInfo);
+ void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs );
+ void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); }
+ void RemoveArenaSpellCooldowns();
+ void RemoveAllSpellCooldown();
+ void _LoadSpellCooldowns(QueryResult *result);
+ void _SaveSpellCooldowns();
+
+ void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
+ {
+ m_resurrectGUID = guid;
+ m_resurrectMap = mapId;
+ m_resurrectX = X;
+ m_resurrectY = Y;
+ m_resurrectZ = Z;
+ m_resurrectHealth = health;
+ m_resurrectMana = mana;
+ };
+ void clearResurrectRequestData() { setResurrectRequestData(0,0,0.0f,0.0f,0.0f,0,0); }
+ bool isRessurectRequestedBy(uint64 guid) const { return m_resurrectGUID == guid; }
+ bool isRessurectRequested() const { return m_resurrectGUID != 0; }
+ void ResurectUsingRequestData();
+
+ int getCinematic()
+ {
+ return m_cinematic;
+ }
+ void setCinematic(int cine)
+ {
+ m_cinematic = cine;
+ }
+
+ void addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc);
+ void removeActionButton(uint8 button);
+ void SendInitialActionButtons();
+
+ PvPInfo pvpInfo;
+ void UpdatePvP(bool state, bool ovrride=false);
+ void UpdateZone(uint32 newZone);
+ void UpdateArea(uint32 newArea);
+
+ void UpdateZoneDependentAuras( uint32 zone_id ); // zones
+ void UpdateAreaDependentAuras( uint32 area_id ); // subzones
+
+ void UpdateAfkReport(time_t currTime);
+ void UpdatePvPFlag(time_t currTime);
+ void UpdateContestedPvP(uint32 currTime);
+ void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;}
+ void ResetContestedPvP()
+ {
+ clearUnitState(UNIT_STAT_ATTACK_PLAYER);
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
+ m_contestedPvPTimer = 0;
+ }
+
+ /** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/
+ DuelInfo *duel;
+ void UpdateDuelFlag(time_t currTime);
+ void CheckDuelDistance(time_t currTime);
+ void DuelComplete(DuelCompleteType type);
+
+ bool IsGroupVisibleFor(Player* p) const;
+ bool IsInSameGroupWith(Player const* p) const;
+ bool IsInSameRaidWith(Player const* p) const { return p==this || (GetGroup() != NULL && GetGroup() == p->GetGroup()); }
+ void UninviteFromGroup();
+ static void RemoveFromGroup(Group* group, uint64 guid);
+ void RemoveFromGroup() { RemoveFromGroup(GetGroup(),GetGUID()); }
+ void SendUpdateToOutOfRangeGroupMembers();
+
+ void SetInGuild(uint32 GuildId) { SetUInt32Value(PLAYER_GUILDID, GuildId); Player::SetUInt32ValueInDB(PLAYER_GUILDID, GuildId, this->GetGUID()); }
+ void SetRank(uint32 rankId){ SetUInt32Value(PLAYER_GUILDRANK, rankId); Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, rankId, this->GetGUID()); }
+ void SetGuildIdInvited(uint32 GuildId) { m_GuildIdInvited = GuildId; }
+ uint32 GetGuildId() { return GetUInt32Value(PLAYER_GUILDID); }
+ static uint32 GetGuildIdFromDB(uint64 guid);
+ uint32 GetRank(){ return GetUInt32Value(PLAYER_GUILDRANK); }
+ static uint32 GetRankFromDB(uint64 guid);
+ int GetGuildIdInvited() { return m_GuildIdInvited; }
+ static void RemovePetitionsAndSigns(uint64 guid, uint32 type);
+
+ // Arena Team
+ void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot)
+ {
+ SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId);
+ SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId, this->GetGUID());
+ }
+ uint32 GetArenaTeamId(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6)); }
+ static uint32 GetArenaTeamIdFromDB(uint64 guid, uint8 slot);
+ void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
+ uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
+
+ void SetDifficulty(uint32 dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; }
+ uint8 GetDifficulty() { return m_dungeonDifficulty; }
+
+ bool UpdateSkill(uint32 skill_id, uint32 step);
+ bool UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step);
+
+ bool UpdateCraftSkill(uint32 spellid);
+ bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1);
+ bool UpdateFishingSkill();
+
+ uint32 GetBaseDefenseSkillValue() const { return GetBaseSkillValue(SKILL_DEFENSE); }
+ uint32 GetBaseWeaponSkillValue(WeaponAttackType attType) const;
+
+ uint32 GetSpellByProto(ItemPrototype *proto);
+
+ float GetHealthBonusFromStamina();
+ float GetManaBonusFromIntellect();
+
+ bool UpdateStats(Stats stat);
+ bool UpdateAllStats();
+ void UpdateResistances(uint32 school);
+ void UpdateArmor();
+ void UpdateMaxHealth();
+ void UpdateMaxPower(Powers power);
+ void UpdateAttackPowerAndDamage(bool ranged = false);
+ void UpdateShieldBlockValue();
+ void UpdateDamagePhysical(WeaponAttackType attType);
+ void UpdateSpellDamageAndHealingBonus();
+
+ void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage);
+
+ void UpdateDefenseBonusesMod();
+ void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
+ float GetMeleeCritFromAgility();
+ float GetDodgeFromAgility();
+ float GetSpellCritFromIntellect();
+ float OCTRegenHPPerSpirit();
+ float OCTRegenMPPerSpirit();
+ float GetRatingCoefficient(CombatRating cr) const;
+ float GetRatingBonusValue(CombatRating cr) const;
+ uint32 GetMeleeCritDamageReduction(uint32 damage) const;
+ uint32 GetRangedCritDamageReduction(uint32 damage) const;
+ uint32 GetSpellCritDamageReduction(uint32 damage) const;
+ uint32 GetDotDamageReduction(uint32 damage) const;
+
+ float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
+ void UpdateBlockPercentage();
+ void UpdateCritPercentage(WeaponAttackType attType);
+ void UpdateAllCritPercentages();
+ void UpdateParryPercentage();
+ void UpdateDodgePercentage();
+ void UpdateAllSpellCritChances();
+ void UpdateSpellCritChance(uint32 school);
+ void UpdateExpertise(WeaponAttackType attType);
+ void UpdateManaRegen();
+
+ const uint64& GetLootGUID() const { return m_lootGuid; }
+ void SetLootGUID(const uint64 &guid) { m_lootGuid = guid; }
+
+ void RemovedInsignia(Player* looterPlr);
+
+ WorldSession* GetSession() const { return m_session; }
+ void SetSession(WorldSession *s) { m_session = s; }
+
+ void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const;
+ void DestroyForPlayer( Player *target ) const;
+ void SendDelayResponse(const uint32);
+ void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP);
+
+ //Low Level Packets
+ void PlaySound(uint32 Sound, bool OnlySelf);
+ //notifiers
+ void SendAttackSwingCantAttack();
+ void SendAttackSwingCancelAttack();
+ void SendAttackSwingDeadTarget();
+ void SendAttackSwingNotStanding();
+ void SendAttackSwingNotInRange();
+ void SendAttackSwingBadFacingAttack();
+ void SendAutoRepeatCancel();
+ void SendExplorationExperience(uint32 Area, uint32 Experience);
+
+ void SendDungeonDifficulty(bool IsInGroup);
+ void ResetInstances(uint8 method);
+ void SendResetInstanceSuccess(uint32 MapId);
+ void SendResetInstanceFailed(uint32 reason, uint32 MapId);
+ void SendResetFailedNotify(uint32 mapid);
+
+ bool SetPosition(float x, float y, float z, float orientation, bool teleport = false);
+ void UpdateUnderwaterState( Map * m, float x, float y, float z );
+
+ void SendMessageToSet(WorldPacket *data, bool self);// overwrite Object::SendMessageToSet
+ void SendMessageToSetInRange(WorldPacket *data, float fist, bool self);
+ // overwrite Object::SendMessageToSetInRange
+ void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only);
+
+ static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true);
+
+ Corpse *GetCorpse() const;
+ void SpawnCorpseBones();
+ void CreateCorpse();
+ void KillPlayer();
+ uint32 GetResurrectionSpellId();
+ void ResurrectPlayer(float restore_percent, bool updateToWorld = true, bool applySickness = false);
+ void BuildPlayerRepop();
+ void RepopAtGraveyard();
+
+ void DurabilityLossAll(double percent, bool inventory);
+ void DurabilityLoss(Item* item, double percent);
+ void DurabilityPointsLossAll(int32 points, bool inventory);
+ void DurabilityPointsLoss(Item* item, int32 points);
+ void DurabilityPointLossForEquipSlot(EquipmentSlots slot);
+ uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank);
+ uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank);
+
+ void StopMirrorTimers()
+ {
+ StopMirrorTimer(FATIGUE_TIMER);
+ StopMirrorTimer(BREATH_TIMER);
+ StopMirrorTimer(FIRE_TIMER);
+ }
+
+ void SetMovement(PlayerMovementType pType);
+
+ void JoinedChannel(Channel *c);
+ void LeftChannel(Channel *c);
+ void CleanupChannels();
+ void UpdateLocalChannels( uint32 newZone );
+ void LeaveLFGChannel();
+
+ void UpdateDefense();
+ void UpdateWeaponSkill (WeaponAttackType attType);
+ void UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence);
+
+ void SetSkill(uint32 id, uint16 currVal, uint16 maxVal);
+ uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus
+ uint16 GetPureMaxSkillValue(uint32 skill) const; // max
+ uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus
+ uint16 GetBaseSkillValue(uint32 skill) const; // skill value + perm. bonus
+ uint16 GetPureSkillValue(uint32 skill) const; // skill value
+ int16 GetSkillTempBonusValue(uint32 skill) const;
+ bool HasSkill(uint32 skill) const;
+ void learnSkillRewardedSpells( uint32 id );
+ void learnSkillRewardedSpells();
+
+ void SetDontMove(bool dontMove);
+ bool GetDontMove() const { return m_dontMove; }
+
+ void CheckExploreSystem(void);
+
+ static uint32 TeamForRace(uint8 race);
+ uint32 GetTeam() const { return m_team; }
+ static uint32 getFactionForRace(uint8 race);
+ void setFactionForRace(uint8 race);
+
+ bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
+ bool RewardPlayerAndGroupAtKill(Unit* pVictim);
+
+ FactionStateList m_factions;
+ ForcedReactions m_forcedReactions;
+ uint32 GetDefaultReputationFlags(const FactionEntry *factionEntry) const;
+ int32 GetBaseReputation(const FactionEntry *factionEntry) const;
+ int32 GetReputation(uint32 faction_id) const;
+ int32 GetReputation(const FactionEntry *factionEntry) const;
+ ReputationRank GetReputationRank(uint32 faction) const;
+ ReputationRank GetReputationRank(const FactionEntry *factionEntry) const;
+ ReputationRank GetBaseReputationRank(const FactionEntry *factionEntry) const;
+ ReputationRank ReputationToRank(int32 standing) const;
+ const static int32 ReputationRank_Length[MAX_REPUTATION_RANK];
+ const static int32 Reputation_Cap = 42999;
+ const static int32 Reputation_Bottom = -42000;
+ bool ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation);
+ bool ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool SetFactionReputation(uint32 FactionTemplateId, int32 standing);
+ bool SetFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest);
+ void RewardReputation(Unit *pVictim, float rate);
+ void RewardReputation(Quest const *pQuest);
+ void SetInitialFactions();
+ void UpdateReputation() const;
+ void SendFactionState(FactionState const* faction) const;
+ void SendInitialReputations();
+ FactionState const* GetFactionState( FactionEntry const* factionEntry) const;
+ void SetFactionAtWar(FactionState* faction, bool atWar);
+ void SetFactionInactive(FactionState* faction, bool inactive);
+ void SetFactionVisible(FactionState* faction);
+ void SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId);
+ void SetFactionVisibleForFactionId(uint32 FactionId);
+ void UpdateMaxSkills();
+ void UpdateSkillsToMaxSkillsForLevel(); // for .levelup
+ void ModifySkillBonus(uint32 skillid,int32 val, bool talent);
+
+ /*********************************************************/
+ /*** PVP SYSTEM ***/
+ /*********************************************************/
+ void UpdateArenaFields();
+ void UpdateHonorFields();
+ bool RewardHonor(Unit *pVictim, uint32 groupsize, float honor = -1);
+ uint32 GetHonorPoints() { return GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY); }
+ uint32 GetArenaPoints() { return GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); }
+ void ModifyHonorPoints( int32 value );
+ void ModifyArenaPoints( int32 value );
+ uint32 GetMaxPersonalArenaRatingRequirement();
+
+ //End of PvP System
+
+ void SetDrunkValue(uint16 newDrunkValue, uint32 itemid=0);
+ uint16 GetDrunkValue() const { return m_drunk; }
+ static DrunkenState GetDrunkenstateByValue(uint16 value);
+
+ uint32 GetDeathTimer() const { return m_deathTimer; }
+ uint32 GetCorpseReclaimDelay(bool pvp) const;
+ void UpdateCorpseReclaimDelay();
+ void SendCorpseReclaimDelay(bool load = false);
+
+ uint32 GetShieldBlockValue() const; // overwrite Unit version (virtual)
+ bool CanParry() const { return m_canParry; }
+ void SetCanParry(bool value) { m_canParry = value; }
+ bool CanDualWield() const { return m_canDualWield; }
+ void SetCanDualWield(bool value) { m_canDualWield = value; }
+
+ void SetRegularAttackTime();
+ void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; }
+ void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats = true);
+ float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const;
+ float GetTotalBaseModValue(BaseModGroup modGroup) const;
+ float GetTotalPercentageModValue(BaseModGroup modGroup) const { return m_auraBaseMod[modGroup][FLAT_MOD] + m_auraBaseMod[modGroup][PCT_MOD]; }
+ void _ApplyAllStatBonuses();
+ void _RemoveAllStatBonuses();
+
+ void _ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply);
+ void _ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
+ void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
+
+ void _ApplyItemMods(Item *item,uint8 slot,bool apply);
+ void _RemoveAllItemMods();
+ void _ApplyAllItemMods();
+ void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply);
+ void _ApplyAmmoBonuses();
+ bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot);
+ void ToggleMetaGemsActive(uint8 exceptslot, bool apply);
+ void CorrectMetaGemEnchants(uint8 slot, bool apply);
+ void InitDataForForm(bool reapplyMods = false);
+
+ 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,Unit* Target, WeaponAttackType attType);
+
+ void SendInitWorldStates();
+ void SendUpdateWorldState(uint32 Field, uint32 Value);
+ void SendDirectMessage(WorldPacket *data);
+
+ void SendAuraDurationsForTarget(Unit* target);
+
+ PlayerMenu* PlayerTalkClass;
+ std::vector<ItemSetEffect *> ItemSetEff;
+
+ void SendLoot(uint64 guid, LootType loot_type);
+ void SendLootRelease( uint64 guid );
+ void SendNotifyLootItemRemoved(uint8 lootSlot);
+ void SendNotifyLootMoneyRemoved();
+
+ /*********************************************************/
+ /*** BATTLEGROUND SYSTEM ***/
+ /*********************************************************/
+
+ bool InBattleGround() const { return m_bgBattleGroundID != 0; }
+ uint32 GetBattleGroundId() const { return m_bgBattleGroundID; }
+ BattleGround* GetBattleGround() const;
+ bool InArena() const;
+
+ static uint32 GetMinLevelForBattleGroundQueueId(uint32 queue_id);
+ static uint32 GetMaxLevelForBattleGroundQueueId(uint32 queue_id);
+ uint32 GetBattleGroundQueueIdFromLevel() const;
+
+ uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgType; }
+ uint32 GetBattleGroundQueueIndex(uint32 bgType) const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgType == bgType)
+ return i;
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ bool IsInvitedForBattleGroundType(uint32 bgType) const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgType == bgType)
+ return m_bgBattleGroundQueueID[i].invited;
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ bool InBattleGroundQueueForBattleGroundType(uint32 bgType) const
+ {
+ return GetBattleGroundQueueIndex(bgType) < PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+
+ void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; }
+ uint32 AddBattleGroundQueueId(uint32 val)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if (m_bgBattleGroundQueueID[i].bgType == 0 || m_bgBattleGroundQueueID[i].bgType == val)
+ {
+ m_bgBattleGroundQueueID[i].bgType = val;
+ m_bgBattleGroundQueueID[i].invited = false;
+ return i;
+ }
+ }
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ void RemoveBattleGroundQueueId(uint32 val)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if (m_bgBattleGroundQueueID[i].bgType == val)
+ {
+ m_bgBattleGroundQueueID[i].bgType = 0;
+ m_bgBattleGroundQueueID[i].invited = false;
+ return;
+ }
+ }
+ }
+ void SetInviteForBattleGroundType(uint32 bgType)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgType == bgType)
+ m_bgBattleGroundQueueID[i].invited = true;
+ }
+
+ uint32 GetBattleGroundEntryPointMap() const { return m_bgEntryPointMap; }
+ float GetBattleGroundEntryPointX() const { return m_bgEntryPointX; }
+ float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; }
+ float GetBattleGroundEntryPointZ() const { return m_bgEntryPointZ; }
+ float GetBattleGroundEntryPointO() const { return m_bgEntryPointO; }
+ void SetBattleGroundEntryPoint(uint32 Map, float PosX, float PosY, float PosZ, float PosO )
+ {
+ m_bgEntryPointMap = Map;
+ m_bgEntryPointX = PosX;
+ m_bgEntryPointY = PosY;
+ m_bgEntryPointZ = PosZ;
+ m_bgEntryPointO = PosO;
+ }
+
+ void SetBGTeam(uint32 team) { m_bgTeam = team; }
+ uint32 GetBGTeam() const { return m_bgTeam ? m_bgTeam : GetTeam(); }
+
+ void LeaveBattleground(bool teleportToEntryPoint = true);
+ bool CanJoinToBattleground() const;
+ bool CanReportAfkDueToLimit();
+ void ReportedAfkBy(Player* reporter);
+ void ClearAfkReports() { m_bgAfkReporter.clear(); }
+
+ bool GetBGAccessByLevel(uint32 bgTypeId) const;
+
+ /*********************************************************/
+ /*** REST SYSTEM ***/
+ /*********************************************************/
+
+ bool isRested() const { return GetRestTime() >= 10000; }
+ uint32 GetXPRestBonus(uint32 xp);
+ uint32 GetRestTime() const { return m_restTime;};
+ void SetRestTime(uint32 v) { m_restTime = v;};
+
+ /*********************************************************/
+ /*** ENVIROMENTAL SYSTEM ***/
+ /*********************************************************/
+
+ void EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage);
+
+ /*********************************************************/
+ /*** FLOOD FILTER SYSTEM ***/
+ /*********************************************************/
+
+ void UpdateSpeakTime();
+ bool CanSpeak() const;
+ void ChangeSpeakTime(int utime);
+
+ /*********************************************************/
+ /*** VARIOUS SYSTEMS ***/
+ /*********************************************************/
+ MovementInfo m_movementInfo;
+ bool isMoving() const { return HasUnitMovementFlag(movementFlagsMask); }
+ bool isMovingOrTurning() const { return HasUnitMovementFlag(movementOrTurningFlagsMask); }
+
+ bool CanFly() const { return HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); }
+ bool IsFlying() const { return HasUnitMovementFlag(MOVEMENTFLAG_FLYING); }
+
+ void HandleDrowning();
+
+ void SetClientControl(Unit* target, uint8 allowMove);
+
+ // Transports
+ Transport * GetTransport() const { return m_transport; }
+ void SetTransport(Transport * t) { m_transport = t; }
+
+ float GetTransOffsetX() const { return m_movementInfo.t_x; }
+ float GetTransOffsetY() const { return m_movementInfo.t_y; }
+ float GetTransOffsetZ() const { return m_movementInfo.t_z; }
+ float GetTransOffsetO() const { return m_movementInfo.t_o; }
+ uint32 GetTransTime() const { return m_movementInfo.t_time; }
+
+ uint32 GetSaveTimer() const { return m_nextSave; }
+ void SetSaveTimer(uint32 timer) { m_nextSave = timer; }
+
+ // Recall position
+ uint32 m_recallMap;
+ float m_recallX;
+ float m_recallY;
+ float m_recallZ;
+ float m_recallO;
+ void SaveRecallPosition();
+
+ // Homebind coordinates
+ uint32 m_homebindMapId;
+ uint16 m_homebindZoneId;
+ float m_homebindX;
+ float m_homebindY;
+ float m_homebindZ;
+
+ // currently visible objects at player client
+ typedef std::set<uint64> ClientGUIDs;
+ ClientGUIDs m_clientGUIDs;
+
+ bool HaveAtClient(WorldObject const* u) { return u==this || m_clientGUIDs.find(u->GetGUID())!=m_clientGUIDs.end(); }
+
+ bool IsVisibleInGridForPlayer(Player* pl) const;
+ bool IsVisibleGloballyFor(Player* pl) const;
+
+ void UpdateVisibilityOf(WorldObject* target);
+
+ template<class T>
+ void UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, 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; }
+
+ LookingForGroup m_lookingForGroup;
+
+ // Temporarily removed pet cache
+ uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; }
+ void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; }
+ uint32 GetOldPetSpell() const { return m_oldpetspell; }
+ void SetOldPetSpell(uint32 petspell) { m_oldpetspell = petspell; }
+
+ /*********************************************************/
+ /*** INSTANCE SYSTEM ***/
+ /*********************************************************/
+
+ typedef HM_NAMESPACE::hash_map< uint32 /*mapId*/, InstancePlayerBind > BoundInstancesMap;
+
+ void UpdateHomebindTime(uint32 time);
+
+ uint32 m_HomebindTimer;
+ bool m_InstanceValid;
+ // permanent binds and solo binds by difficulty
+ BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES];
+ InstancePlayerBind* GetBoundInstance(uint32 mapid, uint8 difficulty);
+ BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; }
+ 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);
+ void SendRaidInfo();
+ void SendSavedInstances();
+ static void ConvertInstancesToGroup(Player *player, Group *group = NULL, uint64 player_guid = 0);
+
+ /*********************************************************/
+ /*** GROUP SYSTEM ***/
+ /*********************************************************/
+
+ Group * GetGroupInvite() { return m_groupInvite; }
+ void SetGroupInvite(Group *group) { m_groupInvite = group; }
+ Group * GetGroup() { return m_group.getTarget(); }
+ const Group * GetGroup() const { return (const Group*)m_group.getTarget(); }
+ GroupReference& GetGroupRef() { return m_group; }
+ void SetGroup(Group *group, int8 subgroup = -1);
+ uint8 GetSubGroup() const { return m_group.getSubGroup(); }
+ uint32 GetGroupUpdateFlag() { return m_groupUpdateMask; }
+ void SetGroupUpdateFlag(uint32 flag) { m_groupUpdateMask |= flag; }
+ uint64 GetAuraUpdateMask() { return m_auraUpdateMask; }
+ void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); }
+ Player* GetNextRandomRaidMember(float radius);
+
+ GridReference<Player> &GetGridRef() { return m_gridRef; }
+ bool isAllowedToLoot(Creature* creature);
+
+ WorldLocation& GetTeleportDest() { return m_teleport_dest; }
+
+ DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
+
+ protected:
+
+ /*********************************************************/
+ /*** BATTLEGROUND SYSTEM ***/
+ /*********************************************************/
+
+ /* this variable is set to bg->m_InstanceID, when player is teleported to BG - (it is battleground's GUID)*/
+ uint32 m_bgBattleGroundID;
+ /*
+ this is an array of BG queues (BgTypeIDs) in which is player
+ */
+ struct BgBattleGroundQueueID_Rec
+ {
+ uint32 bgType;
+ bool invited;
+ };
+ BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES];
+ uint32 m_bgEntryPointMap;
+ float m_bgEntryPointX;
+ float m_bgEntryPointY;
+ float m_bgEntryPointZ;
+ float m_bgEntryPointO;
+
+ std::set<uint32> m_bgAfkReporter;
+ uint8 m_bgAfkReportedCount;
+ time_t m_bgAfkReportedTimer;
+ uint32 m_contestedPvPTimer;
+
+ uint32 m_bgTeam; // what side the player will be added to
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+
+ std::set<uint32> m_timedquests;
+
+ uint64 m_divider;
+ uint32 m_ingametime;
+
+ /*********************************************************/
+ /*** LOAD SYSTEM ***/
+ /*********************************************************/
+
+ void _LoadActions(QueryResult *result);
+ void _LoadAuras(QueryResult *result, uint32 timediff);
+ void _LoadBoundInstances(QueryResult *result);
+ void _LoadInventory(QueryResult *result, uint32 timediff);
+ void _LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery);
+ void _LoadMail();
+ void _LoadMailedItems(Mail *mail);
+ void _LoadQuestStatus(QueryResult *result);
+ void _LoadDailyQuestStatus(QueryResult *result);
+ void _LoadGroup(QueryResult *result);
+ void _LoadReputation(QueryResult *result);
+ void _LoadSpells(QueryResult *result);
+ void _LoadTutorials(QueryResult *result);
+ void _LoadFriendList(QueryResult *result);
+ bool _LoadHomeBind(QueryResult *result);
+ void _LoadDeclinedNames(QueryResult *result);
+
+ /*********************************************************/
+ /*** SAVE SYSTEM ***/
+ /*********************************************************/
+
+ void _SaveActions();
+ void _SaveAuras();
+ void _SaveInventory();
+ void _SaveMail();
+ void _SaveQuestStatus();
+ void _SaveDailyQuestStatus();
+ void _SaveReputation();
+ void _SaveSpells();
+ void _SaveTutorials();
+
+ void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
+ void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
+
+ /*********************************************************/
+ /*** ENVIRONMENTAL SYSTEM ***/
+ /*********************************************************/
+ void HandleLava();
+ void HandleSobering();
+ void StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue);
+ void ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen);
+ void StopMirrorTimer(MirrorTimerType Type);
+ uint8 m_isunderwater;
+ bool m_isInWater;
+
+ /*********************************************************/
+ /*** HONOR SYSTEM ***/
+ /*********************************************************/
+ time_t m_lastHonorUpdateTime;
+
+ void outDebugValues() const;
+ bool _removeSpell(uint16 spell_id);
+ uint64 m_lootGuid;
+
+ uint32 m_race;
+ uint32 m_class;
+ uint32 m_team;
+ uint32 m_nextSave;
+ time_t m_speakTime;
+ uint32 m_speakCount;
+ uint32 m_dungeonDifficulty;
+
+ uint32 m_atLoginFlags;
+
+ Item* m_items[PLAYER_SLOTS_COUNT];
+ uint32 m_currentBuybackSlot;
+
+ std::vector<Item*> m_itemUpdateQueue;
+ bool m_itemUpdateQueueBlocked;
+
+ uint32 m_ExtraFlags;
+ uint64 m_curSelection;
+
+ uint64 m_comboTarget;
+ int8 m_comboPoints;
+
+ QuestStatusMap mQuestStatus;
+
+ uint32 m_GuildIdInvited;
+ uint32 m_ArenaTeamIdInvited;
+
+ PlayerMails m_mail;
+ PlayerSpellMap m_spells;
+ SpellCooldowns m_spellCooldowns;
+
+ ActionButtonList m_actionButtons;
+
+ float m_auraBaseMod[BASEMOD_END][MOD_END];
+
+ SpellModList m_spellMods[MAX_SPELLMOD];
+ int32 m_SpellModRemoveCount;
+ EnchantDurationList m_enchantDuration;
+ ItemDurationList m_itemDuration;
+
+ uint64 m_resurrectGUID;
+ uint32 m_resurrectMap;
+ float m_resurrectX, m_resurrectY, m_resurrectZ;
+ uint32 m_resurrectHealth, m_resurrectMana;
+
+ WorldSession *m_session;
+
+ typedef std::list<Channel*> JoinedChannelsList;
+ JoinedChannelsList m_channels;
+
+ bool m_dontMove;
+
+ int m_cinematic;
+
+ Player *pTrader;
+ bool acceptTrade;
+ uint16 tradeItems[TRADE_SLOT_COUNT];
+ uint32 tradeGold;
+
+ time_t m_nextThinkTime;
+
+ uint32 m_Tutorials[8];
+ bool m_TutorialsChanged;
+
+ bool m_DailyQuestChanged;
+ time_t m_lastDailyQuestTime;
+
+ uint32 m_regenTimer;
+ uint32 m_breathTimer;
+ uint32 m_drunkTimer;
+ uint16 m_drunk;
+ uint32 m_weaponChangeTimer;
+
+ uint32 m_zoneUpdateId;
+ uint32 m_zoneUpdateTimer;
+ uint32 m_areaUpdateId;
+
+ uint32 m_deathTimer;
+ time_t m_deathExpireTime;
+
+ uint32 m_restTime;
+
+ uint32 m_WeaponProficiency;
+ uint32 m_ArmorProficiency;
+ bool m_canParry;
+ bool m_canDualWield;
+ uint8 m_swingErrorMsg;
+ float m_ammoDPS;
+ ////////////////////Rest System/////////////////////
+ int time_inn_enter;
+ uint32 inn_pos_mapid;
+ float inn_pos_x;
+ float inn_pos_y;
+ float inn_pos_z;
+ float m_rest_bonus;
+ RestType rest_type;
+ ////////////////////Rest System/////////////////////
+
+ // Transports
+ Transport * m_transport;
+
+ uint32 m_resetTalentsCost;
+ time_t m_resetTalentsTime;
+ uint32 m_usedTalentCount;
+
+ // Social
+ PlayerSocial *m_social;
+
+ // Groups
+ GroupReference m_group;
+ Group *m_groupInvite;
+ uint32 m_groupUpdateMask;
+ uint64 m_auraUpdateMask;
+
+ // Temporarily removed pet cache
+ uint32 m_temporaryUnsummonedPetNumber;
+ uint32 m_oldpetspell;
+
+ uint64 m_miniPet;
+ GuardianPetList m_guardianPets;
+
+ // Player summoning
+ time_t m_summon_expire;
+ uint32 m_summon_mapid;
+ float m_summon_x;
+ float m_summon_y;
+ float m_summon_z;
+
+ // Far Teleport
+ WorldLocation m_teleport_dest;
+
+ DeclinedName *m_declinedname;
+ private:
+ // internal common parts for CanStore/StoreItem functions
+ uint8 _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const;
+ uint8 _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;
+ uint8 _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const;
+ Item* _StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update );
+
+ GridReference<Player> m_gridRef;
+};
+
+void AddItemsSetItem(Player*player,Item *item);
+void RemoveItemsSetItem(Player*player,ItemPrototype const *proto);
+
+// "the bodies of template functions must be made available in a header file"
+template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 totalpct = 0;
+ int32 totalflat = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod,spell))
+ continue;
+ if (mod->type == SPELLMOD_FLAT)
+ totalflat += mod->value;
+ else if (mod->type == SPELLMOD_PCT)
+ {
+ // skip percent mods for null basevalue (most important for spell mods with charges )
+ if(basevalue == T(0))
+ continue;
+
+ // special case (skip >10sec spell casts for instant cast setting)
+ if( mod->op==SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
+ continue;
+
+ totalpct += mod->value;
+ }
+
+ if (mod->charges > 0 )
+ {
+ --mod->charges;
+ if (mod->charges == 0)
+ {
+ mod->charges = -1;
+ mod->lastAffected = spell;
+ if(!mod->lastAffected)
+ mod->lastAffected = FindCurrentSpellBySpellId(spellId);
+ ++m_SpellModRemoveCount;
+ }
+ }
+ }
+
+ float diff = (float)basevalue*(float)totalpct/100.0f + (float)totalflat;
+ basevalue = T((float)basevalue + diff);
+ return T(diff);
+}
+#endif
diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp
new file mode 100644
index 00000000000..413f006b501
--- /dev/null
+++ b/src/game/PlayerDump.cpp
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "PlayerDump.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/SQLStorage.h"
+#include "UpdateFields.h"
+#include "ObjectMgr.h"
+
+// Character Dump tables
+#define DUMP_TABLE_COUNT 20
+
+struct DumpTable
+{
+ char const* name;
+ DumpTableType type;
+};
+
+static DumpTable dumpTables[DUMP_TABLE_COUNT] =
+{
+ { "characters", DTT_CHARACTER },
+ { "character_queststatus", DTT_CHAR_TABLE },
+ { "character_reputation", DTT_CHAR_TABLE },
+ { "character_spell", DTT_CHAR_TABLE },
+ { "character_spell_cooldown", DTT_CHAR_TABLE },
+ { "character_action", DTT_CHAR_TABLE },
+ { "character_aura", DTT_CHAR_TABLE },
+ { "character_homebind", DTT_CHAR_TABLE },
+ { "character_ticket", DTT_CHAR_TABLE },
+ { "character_inventory", DTT_INVENTORY },
+ { "mail", DTT_MAIL },
+ { "mail_items", DTT_MAIL_ITEM },
+ { "item_instance", DTT_ITEM },
+ { "character_gifts", DTT_ITEM_GIFT },
+ { "item_text", DTT_ITEM_TEXT },
+ { "character_pet", DTT_PET },
+ { "pet_aura", DTT_PET_TABLE },
+ { "pet_spell", DTT_PET_TABLE },
+ { "pet_spell_cooldown", DTT_PET_TABLE },
+};
+
+// Low level functions
+static bool findtoknth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e)
+{
+ int i; s = e = 0;
+ std::string::size_type size = str.size();
+ for(i = 1; s < size && i < n; s++) if(str[s] == ' ') ++i;
+ if (i < n)
+ return false;
+
+ e = str.find(' ', s);
+
+ return e != std::string::npos;
+}
+
+std::string gettoknth(std::string &str, int n)
+{
+ std::string::size_type s = 0, e = 0;
+ if(!findtoknth(str, n, s, e))
+ return "";
+
+ return str.substr(s, e-s);
+}
+
+bool findnth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e)
+{
+ s = str.find("VALUES ('")+9;
+ if (s == std::string::npos) return false;
+
+ do
+ {
+ e = str.find("'",s);
+ if (e == std::string::npos) return false;
+ } while(str[e-1] == '\\');
+
+ for(int i = 1; i < n; i++)
+ {
+ do
+ {
+ s = e+4;
+ e = str.find("'",s);
+ if (e == std::string::npos) return false;
+ } while (str[e-1] == '\\');
+ }
+ return true;
+}
+
+std::string gettablename(std::string &str)
+{
+ std::string::size_type s = 13;
+ std::string::size_type e = str.find(_TABLE_SIM_, s);
+ if (e == std::string::npos)
+ return "";
+
+ return str.substr(s, e-s);
+}
+
+bool changenth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false)
+{
+ std::string::size_type s, e;
+ if(!findnth(str,n,s,e))
+ return false;
+
+ if(nonzero && str.substr(s,e-s) == "0")
+ return true; // not an error
+ if(!insert)
+ str.replace(s,e-s, with);
+ else
+ str.insert(s, with);
+
+ return true;
+}
+
+std::string getnth(std::string &str, int n)
+{
+ std::string::size_type s, e;
+ if(!findnth(str,n,s,e))
+ return "";
+
+ return str.substr(s, e-s);
+}
+
+bool changetoknth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false)
+{
+ std::string::size_type s = 0, e = 0;
+ if(!findtoknth(str, n, s, e))
+ return false;
+ if(nonzero && str.substr(s,e-s) == "0")
+ return true; // not an error
+ if(!insert)
+ str.replace(s, e-s, with);
+ else
+ str.insert(s, with);
+
+ return true;
+}
+
+uint32 registerNewGuid(uint32 oldGuid, std::map<uint32, uint32> &guidMap, uint32 hiGuid)
+{
+ std::map<uint32, uint32>::iterator itr = guidMap.find(oldGuid);
+ if(itr != guidMap.end())
+ return itr->second;
+
+ uint32 newguid = hiGuid + guidMap.size();
+ guidMap[oldGuid] = newguid;
+ return newguid;
+}
+
+bool changeGuid(std::string &str, int n, std::map<uint32, uint32> &guidMap, uint32 hiGuid, bool nonzero = false)
+{
+ char chritem[20];
+ uint32 oldGuid = atoi(getnth(str, n).c_str());
+ if (nonzero && oldGuid == 0)
+ return true; // not an error
+
+ uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid);
+ snprintf(chritem, 20, "%d", newGuid);
+
+ return changenth(str, n, chritem, false, nonzero);
+}
+
+bool changetokGuid(std::string &str, int n, std::map<uint32, uint32> &guidMap, uint32 hiGuid, bool nonzero = false)
+{
+ char chritem[20];
+ uint32 oldGuid = atoi(gettoknth(str, n).c_str());
+ if (nonzero && oldGuid == 0)
+ return true; // not an error
+
+ uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid);
+ snprintf(chritem, 20, "%d", newGuid);
+
+ return changetoknth(str, n, chritem, false, nonzero);
+}
+
+std::string CreateDumpString(char const* tableName, QueryResult *result)
+{
+ if(!tableName || !result) return "";
+ std::ostringstream ss;
+ ss << "INSERT INTO "<< _TABLE_SIM_ << tableName << _TABLE_SIM_ << " VALUES (";
+ Field *fields = result->Fetch();
+ for(uint32 i = 0; i < result->GetFieldCount(); i++)
+ {
+ if (i == 0) ss << "'";
+ else ss << ", '";
+
+ std::string s = fields[i].GetCppString();
+ CharacterDatabase.escape_string(s);
+ ss << s;
+
+ ss << "'";
+ }
+ ss << ");";
+ return ss.str();
+}
+
+std::string PlayerDumpWriter::GenerateWhereStr(char const* field, uint32 guid)
+{
+ std::ostringstream wherestr;
+ wherestr << field << " = '" << guid << "'";
+ return wherestr.str();
+}
+
+std::string PlayerDumpWriter::GenerateWhereStr(char const* field, GUIDs const& guids, GUIDs::const_iterator& itr)
+{
+ std::ostringstream wherestr;
+ wherestr << field << " IN ('";
+ for(; itr != guids.end(); ++itr)
+ {
+ wherestr << *itr;
+
+ if(wherestr.str().size() > MAX_QUERY_LEN - 50) // near to max query
+ {
+ ++itr;
+ break;
+ }
+
+ GUIDs::const_iterator itr2 = itr;
+ if(++itr2 != guids.end())
+ wherestr << "','";
+ }
+ wherestr << "')";
+ return wherestr.str();
+}
+
+void StoreGUID(QueryResult *result,uint32 field,std::set<uint32>& guids)
+{
+ Field* fields = result->Fetch();
+ uint32 guid = fields[field].GetUInt32();
+ if(guid)
+ guids.insert(guid);
+}
+
+void StoreGUID(QueryResult *result,uint32 data,uint32 field, std::set<uint32>& guids)
+{
+ Field* fields = result->Fetch();
+ std::string dataStr = fields[data].GetCppString();
+ uint32 guid = atoi(gettoknth(dataStr, field).c_str());
+ if(guid)
+ guids.insert(guid);
+}
+
+// Writing - High-level functions
+bool PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type)
+{
+ if (!tableFrom || !tableTo)
+ return false;
+
+ GUIDs const* guids = NULL;
+ char const* fieldname = NULL;
+
+ switch ( type )
+ {
+ case DTT_ITEM: fieldname = "guid"; guids = &items; break;
+ case DTT_ITEM_GIFT: fieldname = "item_guid"; guids = &items; break;
+ case DTT_PET: fieldname = "owner"; break;
+ case DTT_PET_TABLE: fieldname = "guid"; guids = &pets; break;
+ case DTT_MAIL: fieldname = "receiver"; break;
+ case DTT_MAIL_ITEM: fieldname = "mail_id"; guids = &mails; break;
+ case DTT_ITEM_TEXT: fieldname = "id"; guids = &texts; break;
+ default: fieldname = "guid"; break;
+ }
+
+ // for guid set stop if set is empty
+ if(guids && guids->empty())
+ return true; // nothing to do
+
+ // setup for guids case start position
+ GUIDs::const_iterator guids_itr;
+ if(guids)
+ guids_itr = guids->begin();
+
+ do
+ {
+ std::string wherestr;
+
+ if(guids) // set case, get next guids string
+ wherestr = GenerateWhereStr(fieldname,*guids,guids_itr);
+ else // not set case, get single guid string
+ wherestr = GenerateWhereStr(fieldname,guid);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT * FROM %s WHERE %s", tableFrom, wherestr.c_str());
+ if(!result)
+ return false;
+
+ do
+ {
+ // 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;
+ }
+
+ dump += CreateDumpString(tableTo, result);
+ dump += "\n";
+ }
+ while (result->NextRow());
+
+ delete result;
+ }
+ while(guids && guids_itr != guids->end()); // not set case iterate single time, set case iterate for all guids
+
+ return true;
+}
+
+std::string PlayerDumpWriter::GetDump(uint32 guid)
+{
+ std::string dump;
+ for(int i = 0; i < DUMP_TABLE_COUNT; i++)
+ DumpTable(dump, guid, dumpTables[i].name, dumpTables[i].name, dumpTables[i].type);
+
+ // TODO: Add instance/group/gifts..
+ // TODO: Add a dump level option to skip some non-important tables
+
+ return dump;
+}
+
+bool PlayerDumpWriter::WriteDump(std::string file, uint32 guid)
+{
+ FILE *fout = fopen(file.c_str(), "w");
+ if (!fout) { sLog.outError("Failed to open file!\r\n"); return false; }
+
+ std::string dump = GetDump(guid);
+
+ fprintf(fout,"%s\n",dump.c_str());
+ fclose(fout);
+ return true;
+}
+
+// Reading - High-level functions
+#define ROLLBACK {CharacterDatabase.RollbackTransaction(); fclose(fin); return false;}
+
+bool PlayerDumpReader::LoadDump(std::string file, uint32 account, std::string name, uint32 guid)
+{
+ // check character count
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account);
+ uint8 charcount = 0;
+ if ( result )
+ {
+ Field *fields=result->Fetch();
+ charcount = fields[0].GetUInt8();
+ delete result;
+
+ if (charcount >= 10)
+ {
+ return false;
+ }
+ }
+ }
+ FILE *fin = fopen(file.c_str(), "r");
+ if(!fin) return false;
+
+ QueryResult * result = NULL;
+ char newguid[20], chraccount[20], newpetid[20], currpetid[20], lastpetid[20];
+
+ // make sure the same guid doesn't already exist and is safe to use
+ bool incHighest = true;
+ if(guid != 0 && guid < objmgr.m_hiCharGuid)
+ {
+ result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid);
+ if (result)
+ {
+ guid = objmgr.m_hiCharGuid; // use first free if exists
+ delete result;
+ }
+ else incHighest = false;
+ }
+ else guid = objmgr.m_hiCharGuid;
+
+ // normalize the name if specified and check if it exists
+ if(!normalizePlayerName(name))
+ name = "";
+
+ if(ObjectMgr::IsValidName(name,true))
+ {
+ 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());
+ if (result)
+ {
+ name = ""; // use the one from the dump
+ delete result;
+ }
+ }
+ else name = "";
+
+ // name encoded or empty
+
+ snprintf(newguid, 20, "%d", guid);
+ snprintf(chraccount, 20, "%d", account);
+ snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber());
+ snprintf(lastpetid, 20, "%s", "");
+
+ std::map<uint32,uint32> items;
+ std::map<uint32,uint32> mails;
+ char buf[32000] = "";
+
+ typedef std::map<uint32, uint32> PetIds; // old->new petid relation
+ typedef PetIds::value_type PetIdsPair;
+ PetIds petids;
+
+ CharacterDatabase.BeginTransaction();
+ while(!feof(fin))
+ {
+ if(!fgets(buf, 32000, fin))
+ {
+ if(feof(fin)) break;
+ sLog.outError("LoadPlayerDump: File read error!");
+ ROLLBACK;
+ }
+
+ std::string line; line.assign(buf);
+
+ // skip empty strings
+ if(line.find_first_not_of(" \t\n\r\7")==std::string::npos)
+ continue;
+
+ // determine table name and load type
+ std::string tn = gettablename(line);
+ if(tn.empty())
+ {
+ sLog.outError("LoadPlayerDump: Can't extract table name from line: '%s'!", line.c_str());
+ ROLLBACK;
+ }
+
+ DumpTableType type;
+ uint8 i;
+ for(i = 0; i < DUMP_TABLE_COUNT; i++)
+ {
+ if (tn == dumpTables[i].name)
+ {
+ type = dumpTables[i].type;
+ break;
+ }
+ }
+
+ if (i == DUMP_TABLE_COUNT)
+ {
+ sLog.outError("LoadPlayerDump: Unknown table: '%s'!", tn.c_str());
+ ROLLBACK;
+ }
+
+ // change the data to server values
+ switch(type)
+ {
+ case DTT_CHAR_TABLE:
+ if(!changenth(line, 1, newguid)) ROLLBACK;
+ break;
+
+ case DTT_CHARACTER: // character t.
+ {
+ if(!changenth(line, 1, newguid)) ROLLBACK;
+
+ // guid, data field:guid, items
+ if(!changenth(line, 2, chraccount)) ROLLBACK;
+ std::string vals = getnth(line, 3);
+ if(!changetoknth(vals, OBJECT_FIELD_GUID+1, newguid)) ROLLBACK;
+ for(uint16 field = PLAYER_FIELD_INV_SLOT_HEAD; field < PLAYER_FARSIGHT; field++)
+ if(!changetokGuid(vals, field+1, items, objmgr.m_hiItemGuid, true)) ROLLBACK;
+ if(!changenth(line, 3, vals.c_str())) ROLLBACK;
+ if (name == "")
+ {
+ // check if the original name already exists
+ name = getnth(line, 4);
+ CharacterDatabase.escape_string(name);
+
+ result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str());
+ if (result)
+ {
+ delete result;
+ // rename on login: `at_login` field 30 in raw field list
+ if(!changenth(line, 30, "1")) ROLLBACK;
+ }
+ }
+ else if(!changenth(line, 4, name.c_str())) ROLLBACK;
+
+ break;
+ }
+ case DTT_INVENTORY: // character_inventory t.
+ {
+ if(!changenth(line, 1, newguid)) ROLLBACK;
+
+ // bag, item
+ if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid, true)) ROLLBACK;
+ if(!changeGuid(line, 4, items, objmgr.m_hiItemGuid)) ROLLBACK;
+ break;
+ }
+ case DTT_ITEM: // item_instance t.
+ {
+ // item, owner, data field:item, owner guid
+ if(!changeGuid(line, 1, items, objmgr.m_hiItemGuid)) ROLLBACK;
+ if(!changenth(line, 2, newguid)) ROLLBACK;
+ std::string vals = getnth(line,3);
+ if(!changetokGuid(vals, OBJECT_FIELD_GUID+1, items, objmgr.m_hiItemGuid)) ROLLBACK;
+ if(!changetoknth(vals, ITEM_FIELD_OWNER+1, newguid)) ROLLBACK;
+ if(!changenth(line, 3, vals.c_str())) ROLLBACK;
+ break;
+ }
+ case DTT_ITEM_GIFT: // character_gift
+ {
+ // guid,item_guid,
+ if(!changenth(line, 1, newguid)) ROLLBACK;
+ if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid)) ROLLBACK;
+ break;
+ }
+ case DTT_PET: // character_pet t
+ {
+ //store a map of old pet id to new inserted pet id for use by type 5 tables
+ snprintf(currpetid, 20, "%s", getnth(line, 1).c_str());
+ if(strlen(lastpetid)==0) snprintf(lastpetid, 20, "%s", currpetid);
+ if(strcmp(lastpetid,currpetid)!=0)
+ {
+ snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber());
+ snprintf(lastpetid, 20, "%s", currpetid);
+ }
+
+ std::map<uint32, uint32> :: const_iterator petids_iter = petids.find(atoi(currpetid));
+
+ if(petids_iter == petids.end())
+ {
+ petids.insert(PetIdsPair(atoi(currpetid), atoi(newpetid)));
+ }
+
+ // item, entry, owner, ...
+ if(!changenth(line, 1, newpetid)) ROLLBACK;
+ if(!changenth(line, 3, newguid)) ROLLBACK;
+
+ break;
+ }
+ case DTT_PET_TABLE: // pet_aura, pet_spell, pet_spell_cooldown t
+ {
+ snprintf(currpetid, 20, "%s", getnth(line, 1).c_str());
+
+ // lookup currpetid and match to new inserted pet id
+ std::map<uint32, uint32> :: const_iterator petids_iter = petids.find(atoi(currpetid));
+ if(petids_iter == petids.end()) ROLLBACK; // couldn't find new inserted id
+
+ snprintf(newpetid, 20, "%d", petids_iter->second);
+
+ if(!changenth(line, 1, newpetid)) ROLLBACK;
+
+ break;
+ }
+ case DTT_MAIL: // mail
+ {
+ // id,messageType,stationery,sender,receiver
+ if(!changeGuid(line, 1, mails, objmgr.m_mailid)) ROLLBACK;
+ if(!changenth(line, 5, newguid)) ROLLBACK;
+ break;
+ }
+ case DTT_MAIL_ITEM: // mail_items
+ {
+ // mail_id,item_guid,item_template,receiver
+ if(!changeGuid(line, 1, mails, objmgr.m_mailid)) ROLLBACK;
+ if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid)) ROLLBACK;
+ if(!changenth(line, 4, newguid)) ROLLBACK;
+ break;
+ }
+ default:
+ sLog.outError("Unknown dump table type: %u",type);
+ break;
+ }
+
+ if(!CharacterDatabase.Execute(line.c_str())) ROLLBACK;
+ }
+
+ CharacterDatabase.CommitTransaction();
+
+ objmgr.m_hiItemGuid += items.size();
+ objmgr.m_mailid += mails.size();
+
+ if(incHighest)
+ ++objmgr.m_hiCharGuid;
+
+ fclose(fin);
+
+ return true;
+}
diff --git a/src/game/PlayerDump.h b/src/game/PlayerDump.h
new file mode 100644
index 00000000000..dfcb2e8b528
--- /dev/null
+++ b/src/game/PlayerDump.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _PLAYER_DUMP_H
+#define _PLAYER_DUMP_H
+/*
+#include "Log.h"
+#include "Object.h"
+#include "Bag.h"
+#include "Creature.h"
+#include "Player.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
+#include "Corpse.h"
+#include "QuestDef.h"
+#include "Path.h"
+#include "ItemPrototype.h"
+#include "NPCHandler.h"
+#include "Database/DatabaseEnv.h"
+#include "AuctionHouseObject.h"
+#include "Mail.h"
+#include "Map.h"
+#include "ObjectAccessor.h"
+#include "ObjectDefines.h"
+#include "Policies/Singleton.h"
+#include "Database/SQLStorage.h"
+*/
+#include <string>
+#include <map>
+#include <set>
+
+enum DumpTableType
+{
+ DTT_CHARACTER, // // characters
+
+ DTT_CHAR_TABLE, // // character_action, character_aura, character_homebind,
+ // character_queststatus, character_reputation,
+ // character_spell, character_spell_cooldown, character_ticket,
+ // character_tutorial
+
+ DTT_INVENTORY, // -> item guids collection // character_inventory
+
+ DTT_MAIL, // -> mail ids collection // mail
+ // -> item_text
+
+ DTT_MAIL_ITEM, // <- mail ids // mail_items
+ // -> item guids collection
+
+ DTT_ITEM, // <- item guids // item_instance
+ // -> item_text
+
+ DTT_ITEM_GIFT, // <- item guids // character_gifts
+
+ DTT_PET, // -> pet guids collection // character_pet
+ DTT_PET_TABLE, // <- pet guids // pet_aura, pet_spell, pet_spell_cooldown
+ DTT_ITEM_TEXT, // <- item_text // item_text
+};
+
+class PlayerDump
+{
+ protected:
+ PlayerDump() {}
+};
+
+class PlayerDumpWriter : public PlayerDump
+{
+ public:
+ PlayerDumpWriter() {}
+
+ std::string GetDump(uint32 guid);
+ bool WriteDump(std::string file, uint32 guid);
+ private:
+ typedef std::set<uint32> GUIDs;
+
+ bool DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type);
+ std::string GenerateWhereStr(char const* field, GUIDs const& guids, GUIDs::const_iterator& itr);
+ std::string GenerateWhereStr(char const* field, uint32 guid);
+
+ GUIDs pets;
+ GUIDs mails;
+ GUIDs items;
+ GUIDs texts;
+};
+
+class PlayerDumpReader : public PlayerDump
+{
+ public:
+ PlayerDumpReader() {}
+
+ bool LoadDump(std::string file, uint32 account, std::string name, uint32 guid);
+};
+
+#endif
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp
new file mode 100644
index 00000000000..c784c799351
--- /dev/null
+++ b/src/game/PointMovementGenerator.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "PointMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+
+//----- Point Movement Generator
+template<class T>
+void PointMovementGenerator<T>::Initialize(T &unit)
+{
+ unit.StopMoving();
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z);
+}
+
+template<class T>
+bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if(!&unit)
+ return false;
+
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED))
+ return true;
+
+ Traveller<T> traveller(unit);
+
+ i_destinationHolder.UpdateTraveller(traveller, diff, false);
+
+ if(i_destinationHolder.HasArrived())
+ {
+ unit.StopMoving();
+ MovementInform(unit);
+ return false;
+ }
+
+ return true;
+}
+
+template<class T>
+void PointMovementGenerator<T>::MovementInform(T &unit)
+{
+}
+
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+}
+
+template void PointMovementGenerator<Player>::Initialize(Player&);
+template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
+template void PointMovementGenerator<Player>::MovementInform(Player&);
+
+template void PointMovementGenerator<Creature>::Initialize(Creature&);
+template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff);
diff --git a/src/game/PointMovementGenerator.h b/src/game/PointMovementGenerator.h
new file mode 100644
index 00000000000..7127bbecee5
--- /dev/null
+++ b/src/game/PointMovementGenerator.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_POINTMOVEMENTGENERATOR_H
+#define MANGOS_POINTMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+#include "FollowerReference.h"
+
+template<class T>
+class MANGOS_DLL_SPEC PointMovementGenerator
+: public MovementGeneratorMedium< T, PointMovementGenerator<T> >
+{
+ public:
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), i_nextMoveTime(0) {}
+
+ void Initialize(T &);
+ void Finalize(T &){}
+ void Reset(T &unit){unit.StopMoving();}
+ bool Update(T &, const uint32 &diff);
+
+ void MovementInform(T &);
+
+ MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
+
+ bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ private:
+ TimeTracker i_nextMoveTime;
+ float i_x,i_y,i_z;
+ uint32 id;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+};
+#endif
diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp
new file mode 100644
index 00000000000..e04c4a909e4
--- /dev/null
+++ b/src/game/QueryHandler.cpp
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/DatabaseImpl.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "NPCHandler.h"
+#include "ObjectAccessor.h"
+#include "Pet.h"
+
+void WorldSession::SendNameQueryOpcode(Player *p)
+{
+ if(!p)
+ return;
+
+ // guess size
+ WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+4+4+4+10) );
+ data << p->GetGUID();
+ data << p->GetName();
+ data << uint8(0); // realm name for cross realm BG usage
+ data << uint32(p->getRace());
+ data << uint32(p->getGender());
+ data << uint32(p->getClass());
+ if(DeclinedName const* names = p->GetDeclinedNames())
+ {
+ data << uint8(1); // is declined
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ data << names->name[i];
+ }
+ else
+ data << uint8(0); // is not declined
+
+ SendPacket(&data);
+}
+
+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) "
+ "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
+ "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));
+}
+
+void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId)
+{
+ if(!result)
+ return;
+
+ WorldSession * session = sWorld.FindSession(accountId);
+ if(!session)
+ {
+ delete result;
+ return;
+ }
+
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ std::string name = fields[1].GetCppString();
+ uint32 field = 0;
+ if(name == "")
+ name = session->GetMangosString(LANG_NON_EXIST_CHARACTER);
+ else
+ field = fields[2].GetUInt32();
+
+ // guess size
+ WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+4+4+4+10) );
+ data << MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER);
+ data << name;
+ data << (uint8)0;
+ data << (uint32)(field & 0xFF);
+ data << (uint32)((field >> 16) & 0xFF);
+ data << (uint32)((field >> 8) & 0xFF);
+
+ // if the first declined name field (3) is empty, the rest must be too
+ if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[3].GetCppString() != "")
+ {
+ data << (uint8)1; // is declined
+ for(int i = 3; i < MAX_DECLINED_NAME_CASES+3; ++i)
+ data << fields[i].GetCppString();
+ }
+ else
+ data << (uint8)0; // is declined
+
+ session->SendPacket( &data );
+ delete result;
+}
+
+void WorldSession::HandleNameQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ Player *pChar = objmgr.GetPlayer(guid);
+
+ if (pChar)
+ SendNameQueryOpcode(pChar);
+ else
+ SendNameQueryOpcodeFromDB(guid);
+}
+
+void WorldSession::HandleQueryTimeOpcode( WorldPacket & /*recv_data*/ )
+{
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ SendPacket( &data );
+}
+
+/// Only _static_ data send in this packet !!!
+void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ uint32 entry;
+ recv_data >> entry;
+
+ CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry);
+ if (ci)
+ {
+
+ std::string Name, SubName;
+ Name = ci->Name;
+ SubName = ci->SubName;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ CreatureLocale const *cl = objmgr.GetCreatureLocale(entry);
+ if (cl)
+ {
+ if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty())
+ Name = cl->Name[loc_idx];
+ if (cl->SubName.size() > loc_idx && !cl->SubName[loc_idx].empty())
+ SubName = cl->SubName[loc_idx];
+ }
+ }
+ sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, entry);
+ // guess size
+ WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 );
+ data << (uint32)entry; // creature entry
+ data << Name;
+ data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
+ data << SubName;
+ data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
+ data << (uint32)ci->flag1; // flags wdbFeild7=wad flags1
+ data << (uint32)ci->type;
+ data << (uint32)ci->family; // family wdbFeild9
+ data << (uint32)ci->rank; // rank wdbFeild10
+ data << (uint32)0; // unknown wdbFeild11
+ data << (uint32)ci->PetSpellDataId; // Id from CreatureSpellData.dbc wdbField12
+ data << (uint32)ci->DisplayID_A; // modelid_male1
+ data << (uint32)ci->DisplayID_H; // modelid_female1 ?
+ data << (uint32)ci->DisplayID_A2; // modelid_male2 ?
+ data << (uint32)ci->DisplayID_H2; // modelid_femmale2 ?
+ data << (float)1.0f; // unk
+ data << (float)1.0f; // unk
+ data << (uint8)ci->RacialLeader;
+ SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " );
+ }
+ else
+ {
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog.outDebug( "WORLD: CMSG_CREATURE_QUERY - (%u) NO CREATURE INFO! (GUID: %u, ENTRY: %u)", uint32(GUID_LOPART(guid)), guid, entry );
+ WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 );
+ data << uint32(entry | 0x80000000);
+ SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " );
+ }
+}
+
+/// Only _static_ data send in this packet !!!
+void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ uint32 entryID;
+ recv_data >> entryID;
+
+ const GameObjectInfo *info = objmgr.GetGameObjectInfo(entryID);
+ if(info)
+ {
+
+ std::string Name;
+ std::string CastBarCaption;
+
+ Name = info->name;
+ CastBarCaption = info->castBarCaption;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ GameObjectLocale const *gl = objmgr.GetGameObjectLocale(entryID);
+ if (gl)
+ {
+ if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty())
+ Name = gl->Name[loc_idx];
+ if (gl->CastBarCaption.size() > loc_idx && !gl->CastBarCaption[loc_idx].empty())
+ CastBarCaption = gl->CastBarCaption[loc_idx];
+ }
+ }
+ sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name, entryID);
+ WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 150 );
+ data << entryID;
+ data << (uint32)info->type;
+ data << (uint32)info->displayId;
+ data << Name;
+ data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4
+ data << uint8(0); // 2.0.3, string
+ data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting")
+ data << uint8(0); // 2.0.3, probably string
+ data.append(info->raw.data,24);
+ SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent CMSG_GAMEOBJECT_QUERY " );
+ }
+ else
+ {
+
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog.outDebug( "WORLD: CMSG_GAMEOBJECT_QUERY - (%u) Missing gameobject info for (GUID: %u, ENTRY: %u)", uint32(GUID_LOPART(guid)), guid, entryID );
+ WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 4 );
+ data << uint32(entryID | 0x80000000);
+ SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent CMSG_GAMEOBJECT_QUERY " );
+ }
+}
+
+void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDetail("WORLD: Received MSG_CORPSE_QUERY");
+
+ Corpse *corpse = GetPlayer()->GetCorpse();
+
+ uint8 found = 1;
+ if(!corpse)
+ found = 0;
+
+ WorldPacket data(MSG_CORPSE_QUERY, (1+found*(5*4)));
+ data << uint8(found);
+ if(found)
+ {
+ data << corpse->GetMapId();
+ data << corpse->GetPositionX();
+ data << corpse->GetPositionY();
+ data << corpse->GetPositionZ();
+ data << _player->GetMapId();
+ }
+ SendPacket(&data);
+}
+
+void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ uint32 textID;
+ uint64 guid;
+ GossipText *pGossip;
+ std::string GossipStr;
+
+ recv_data >> textID;
+ sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
+
+ recv_data >> guid;
+ GetPlayer()->SetUInt64Value(UNIT_FIELD_TARGET, guid);
+
+ pGossip = objmgr.GetGossipText(textID);
+
+ WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
+ data << textID;
+
+ if (!pGossip)
+ {
+ for(uint32 i = 0; i < 8; ++i)
+ {
+ data << float(0);
+ data << "Greetings $N";
+ data << "Greetings $N";
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ }
+ }
+ else
+ {
+ std::string Text_0[8], Text_1[8];
+ for (int i=0;i<8;i++)
+ {
+ Text_0[i]=pGossip->Options[i].Text_0;
+ Text_1[i]=pGossip->Options[i].Text_1;
+ }
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID);
+ if (nl)
+ {
+ for (int i=0;i<8;i++)
+ {
+ if (nl->Text_0[i].size() > loc_idx && !nl->Text_0[i][loc_idx].empty())
+ Text_0[i]=nl->Text_0[i][loc_idx];
+ if (nl->Text_1[i].size() > loc_idx && !nl->Text_1[i][loc_idx].empty())
+ Text_1[i]=nl->Text_1[i][loc_idx];
+ }
+ }
+ }
+
+ for (int i=0; i<8; i++)
+ {
+ data << pGossip->Options[i].Probability;
+
+ if ( Text_0[i].empty() )
+ data << Text_1[i];
+ else
+ data << Text_0[i];
+
+ if ( Text_1[i].empty() )
+ data << Text_0[i];
+ else
+ data << Text_1[i];
+
+ data << pGossip->Options[i].Language;
+
+ data << pGossip->Options[i].Emotes[0]._Delay;
+ data << pGossip->Options[i].Emotes[0]._Emote;
+
+ data << pGossip->Options[i].Emotes[1]._Delay;
+ data << pGossip->Options[i].Emotes[1]._Emote;
+
+ data << pGossip->Options[i].Emotes[2]._Delay;
+ data << pGossip->Options[i].Emotes[2]._Emote;
+ }
+ }
+
+ SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
+}
+
+void WorldSession::HandlePageQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 pageID;
+
+ recv_data >> pageID;
+ sLog.outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY for pageID '%u'", pageID);
+
+ while (pageID)
+ {
+ PageText const *pPage = sPageTextStore.LookupEntry<PageText>( pageID );
+ // guess size
+ WorldPacket data( SMSG_PAGE_TEXT_QUERY_RESPONSE, 50 );
+ data << pageID;
+
+ if (!pPage)
+ {
+ data << "Item page missing.";
+ data << uint32(0);
+ pageID = 0;
+ }
+ else
+ {
+ std::string Text = pPage->Text;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ PageTextLocale const *pl = objmgr.GetPageTextLocale(pageID);
+ if (pl)
+ {
+ if (pl->Text.size() > loc_idx && !pl->Text[loc_idx].empty())
+ Text = pl->Text[loc_idx];
+ }
+ }
+
+ data << Text;
+ data << uint32(pPage->Next_Page);
+ pageID = pPage->Next_Page;
+ }
+ SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE " );
+ }
+}
diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp
new file mode 100644
index 00000000000..c92bb9b7b6a
--- /dev/null
+++ b/src/game/QuestDef.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "QuestDef.h"
+#include "Player.h"
+#include "World.h"
+
+Quest::Quest(Field * questRecord)
+{
+ QuestId = questRecord[0].GetUInt32();
+ ZoneOrSort = questRecord[1].GetInt32();
+ SkillOrClass = questRecord[2].GetInt32();
+ MinLevel = questRecord[3].GetUInt32();
+ QuestLevel = questRecord[4].GetUInt32();
+ Type = questRecord[5].GetUInt32();
+ RequiredRaces = questRecord[6].GetUInt32();
+ RequiredSkillValue = questRecord[7].GetUInt32();
+ RepObjectiveFaction = questRecord[8].GetUInt32();
+ RepObjectiveValue = questRecord[9].GetInt32();
+ RequiredMinRepFaction = questRecord[10].GetUInt32();
+ RequiredMinRepValue = questRecord[11].GetInt32();
+ RequiredMaxRepFaction = questRecord[12].GetUInt32();
+ RequiredMaxRepValue = questRecord[13].GetInt32();
+ SuggestedPlayers = questRecord[14].GetUInt32();
+ LimitTime = questRecord[15].GetUInt32();
+ QuestFlags = questRecord[16].GetUInt16();
+ uint32 SpecialFlags = questRecord[17].GetUInt16();
+ CharTitleId = questRecord[18].GetUInt32();
+ PrevQuestId = questRecord[19].GetInt32();
+ NextQuestId = questRecord[20].GetInt32();
+ ExclusiveGroup = questRecord[21].GetInt32();
+ NextQuestInChain = questRecord[22].GetUInt32();
+ SrcItemId = questRecord[23].GetUInt32();
+ SrcItemCount = questRecord[24].GetUInt32();
+ SrcSpell = questRecord[25].GetUInt32();
+ Title = questRecord[26].GetCppString();
+ Details = questRecord[27].GetCppString();
+ Objectives = questRecord[28].GetCppString();
+ OfferRewardText = questRecord[29].GetCppString();
+ RequestItemsText = questRecord[30].GetCppString();
+ EndText = questRecord[31].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ObjectiveText[i] = questRecord[32+i].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemId[i] = questRecord[36+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemCount[i] = questRecord[40+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceId[i] = questRecord[44+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceCount[i] = questRecord[48+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceRef[i] = questRecord[52+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOId[i] = questRecord[56+i].GetInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOCount[i] = questRecord[60+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqSpell[i] = questRecord[64+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemId[i] = questRecord[68+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemCount[i] = questRecord[74+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemId[i] = questRecord[80+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemCount[i] = questRecord[84+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepFaction[i] = questRecord[88+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepValue[i] = questRecord[93+i].GetInt32();
+
+ RewOrReqMoney = questRecord[98].GetInt32();
+ RewMoneyMaxLevel = questRecord[99].GetUInt32();
+ RewSpell = questRecord[100].GetUInt32();
+ RewSpellCast = questRecord[101].GetUInt32();
+ RewMailTemplateId = questRecord[102].GetUInt32();
+ RewMailDelaySecs = questRecord[103].GetUInt32();
+ PointMapId = questRecord[104].GetUInt32();
+ PointX = questRecord[105].GetFloat();
+ PointY = questRecord[106].GetFloat();
+ PointOpt = questRecord[107].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmote[i] = questRecord[108+i].GetUInt32();
+
+ IncompleteEmote = questRecord[112].GetUInt32();
+ CompleteEmote = questRecord[113].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmote[i] = questRecord[114+i].GetInt32();
+
+ QuestStartScript = questRecord[118].GetUInt32();
+ QuestCompleteScript = questRecord[119].GetUInt32();
+
+ QuestFlags |= SpecialFlags << 16;
+
+ m_reqitemscount = 0;
+ m_reqCreatureOrGOcount = 0;
+ m_rewitemscount = 0;
+ m_rewchoiceitemscount = 0;
+
+ for (int i=0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ( ReqItemId[i] )
+ ++m_reqitemscount;
+ if ( ReqCreatureOrGOId[i] )
+ ++m_reqCreatureOrGOcount;
+ }
+
+ for (int i=0; i < QUEST_REWARDS_COUNT; i++)
+ {
+ if ( RewItemId[i] )
+ ++m_rewitemscount;
+ }
+
+ for (int i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
+ {
+ if (RewChoiceItemId[i])
+ ++m_rewchoiceitemscount;
+ }
+}
+
+uint32 Quest::XPValue( Player *pPlayer ) const
+{
+ if( pPlayer )
+ {
+ if( RewMoneyMaxLevel > 0 )
+ {
+ uint32 pLevel = pPlayer->getLevel();
+ uint32 qLevel = QuestLevel;
+ float fullxp = 0;
+ if (qLevel >= 65)
+ fullxp = RewMoneyMaxLevel / 6.0f;
+ else if (qLevel == 64)
+ fullxp = RewMoneyMaxLevel / 4.8f;
+ else if (qLevel == 63)
+ fullxp = RewMoneyMaxLevel / 3.6f;
+ else if (qLevel == 62)
+ fullxp = RewMoneyMaxLevel / 2.4f;
+ else if (qLevel == 61)
+ fullxp = RewMoneyMaxLevel / 1.2f;
+ else if (qLevel > 0 && qLevel <= 60)
+ fullxp = RewMoneyMaxLevel / 0.6f;
+
+ if( pLevel <= qLevel + 5 )
+ return (uint32)fullxp;
+ else if( pLevel == qLevel + 6 )
+ return (uint32)(fullxp * 0.8f);
+ else if( pLevel == qLevel + 7 )
+ return (uint32)(fullxp * 0.6f);
+ else if( pLevel == qLevel + 8 )
+ return (uint32)(fullxp * 0.4f);
+ else if( pLevel == qLevel + 9 )
+ return (uint32)(fullxp * 0.2f);
+ else
+ return (uint32)(fullxp * 0.1f);
+ }
+ }
+ return 0;
+}
+
+int32 Quest::GetRewOrReqMoney() const
+{
+ if(RewOrReqMoney <=0)
+ return RewOrReqMoney;
+
+ return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY));
+}
diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h
new file mode 100644
index 00000000000..ddd24bf57f0
--- /dev/null
+++ b/src/game/QuestDef.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_QUEST_H
+#define MANGOSSERVER_QUEST_H
+
+#include "Platform/Define.h"
+#include "Database/DatabaseEnv.h"
+
+#include <string>
+#include <vector>
+
+class Player;
+
+class ObjectMgr;
+
+#define MAX_QUEST_LOG_SIZE 25
+
+#define QUEST_OBJECTIVES_COUNT 4
+#define QUEST_SOURCE_ITEM_IDS_COUNT 4
+#define QUEST_REWARD_CHOICES_COUNT 6
+#define QUEST_REWARDS_COUNT 4
+#define QUEST_DEPLINK_COUNT 10
+#define QUEST_REPUTATIONS_COUNT 5
+#define QUEST_EMOTE_COUNT 4
+
+enum QuestFailedReasons
+{
+ INVALIDREASON_DONT_HAVE_REQ = 0,
+ INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest.
+ INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race.
+ INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest.
+ INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time.
+ INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account.
+ INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage.
+ INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest.
+ INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today
+ INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time
+};
+
+enum QuestShareMessages
+{
+ QUEST_PARTY_MSG_SHARING_QUEST = 0,
+ QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
+ QUEST_PARTY_MSG_ACCEPT_QUEST = 2,
+ QUEST_PARTY_MSG_REFUSE_QUEST = 3,
+ QUEST_PARTY_MSG_TOO_FAR = 4,
+ QUEST_PARTY_MSG_BUSY = 5,
+ QUEST_PARTY_MSG_LOG_FULL = 6,
+ QUEST_PARTY_MSG_HAVE_QUEST = 7,
+ QUEST_PARTY_MSG_FINISH_QUEST = 8,
+};
+
+enum __QuestTradeSkill
+{
+ QUEST_TRSKILL_NONE = 0,
+ QUEST_TRSKILL_ALCHEMY = 1,
+ QUEST_TRSKILL_BLACKSMITHING = 2,
+ QUEST_TRSKILL_COOKING = 3,
+ QUEST_TRSKILL_ENCHANTING = 4,
+ QUEST_TRSKILL_ENGINEERING = 5,
+ QUEST_TRSKILL_FIRSTAID = 6,
+ QUEST_TRSKILL_HERBALISM = 7,
+ QUEST_TRSKILL_LEATHERWORKING = 8,
+ QUEST_TRSKILL_POISONS = 9,
+ QUEST_TRSKILL_TAILORING = 10,
+ QUEST_TRSKILL_MINING = 11,
+ QUEST_TRSKILL_FISHING = 12,
+ QUEST_TRSKILL_SKINNING = 13,
+ QUEST_TRSKILL_JEWELCRAFTING = 14,
+};
+
+enum QuestStatus
+{
+ QUEST_STATUS_NONE = 0,
+ QUEST_STATUS_COMPLETE = 1,
+ QUEST_STATUS_UNAVAILABLE = 2,
+ QUEST_STATUS_INCOMPLETE = 3,
+ QUEST_STATUS_AVAILABLE = 4,
+ MAX_QUEST_STATUS
+};
+
+enum __QuestGiverStatus
+{
+ DIALOG_STATUS_NONE = 0,
+ DIALOG_STATUS_UNAVAILABLE = 1,
+ DIALOG_STATUS_CHAT = 2,
+ DIALOG_STATUS_INCOMPLETE = 3,
+ DIALOG_STATUS_REWARD_REP = 4,
+ DIALOG_STATUS_AVAILABLE_REP = 5,
+ DIALOG_STATUS_AVAILABLE = 6,
+ DIALOG_STATUS_REWARD2 = 7, // not yellow dot on minimap
+ DIALOG_STATUS_REWARD = 8 // yellow dot on minimap
+};
+
+enum __QuestFlags
+{
+ // Flags used at server and sended to client
+ QUEST_FLAGS_STAY_ALIVE = 1, // Not used currently
+ QUEST_FLAGS_EVENT = 2, // Not used currently
+ QUEST_FLAGS_EXPLORATION = 4, // Not used currently
+ QUEST_FLAGS_SHARABLE = 8, // Can be shared: Player::CanShareQuest()
+ //QUEST_FLAGS_NONE2 = 16, // Not used currently
+ QUEST_FLAGS_EPIC = 32, // Not used currently: Unsure of content
+ QUEST_FLAGS_RAID = 64, // Not used currently
+ QUEST_FLAGS_TBC = 128, // Not used currently: Available if TBC expension enabled only
+ QUEST_FLAGS_UNK2 = 256, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs
+ QUEST_FLAGS_HIDDEN_REWARDS = 512, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
+ QUEST_FLAGS_AUTO_REWARDED = 1024, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
+ QUEST_FLAGS_TBC_RACES = 2048, // Not used currently: Bloodelf/draenei starting zone quests
+ QUEST_FLAGS_DAILY = 4096, // Used to know quest is Daily one
+
+ // Mangos flags for set SpecialFlags in DB if required but used only at server
+ QUEST_MANGOS_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB
+ QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x020000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL)
+ QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT,
+
+ // Mangos flags for internal use only
+ QUEST_MANGOS_FLAGS_DELIVER = 0x040000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_TIMED = 0x200000, // Internal flag computed only
+};
+
+struct QuestLocale
+{
+ QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); }
+
+ std::vector<std::string> Title;
+ std::vector<std::string> Details;
+ std::vector<std::string> Objectives;
+ std::vector<std::string> OfferRewardText;
+ std::vector<std::string> RequestItemsText;
+ std::vector<std::string> EndText;
+ std::vector< std::vector<std::string> > ObjectiveText;
+};
+
+// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
+// all base quest information, and any utility functions such as generating the amount of
+// xp to give
+class Quest
+{
+ friend class ObjectMgr;
+ public:
+ Quest(Field * questRecord);
+ uint32 XPValue( Player *pPlayer ) const;
+
+ bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; }
+ void SetFlag( uint32 flag ) { QuestFlags |= flag; }
+
+ // table data accessors:
+ uint32 GetQuestId() const { return QuestId; }
+ int32 GetZoneOrSort() const { return ZoneOrSort; }
+ int32 GetSkillOrClass() const { return SkillOrClass; }
+ uint32 GetMinLevel() const { return MinLevel; }
+ uint32 GetQuestLevel() const { return QuestLevel; }
+ uint32 GetType() const { return Type; }
+ uint32 GetRequiredRaces() const { return RequiredRaces; }
+ uint32 GetRequiredSkillValue() const { return RequiredSkillValue; }
+ uint32 GetRepObjectiveFaction() const { return RepObjectiveFaction; }
+ int32 GetRepObjectiveValue() const { return RepObjectiveValue; }
+ uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; }
+ int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; }
+ uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; }
+ int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; }
+ uint32 GetSuggestedPlayers() const { return SuggestedPlayers; }
+ uint32 GetLimitTime() const { return LimitTime; }
+ int32 GetPrevQuestId() const { return PrevQuestId; }
+ int32 GetNextQuestId() const { return NextQuestId; }
+ int32 GetExclusiveGroup() const { return ExclusiveGroup; }
+ uint32 GetNextQuestInChain() const { return NextQuestInChain; }
+ uint32 GetCharTitleId() const { return CharTitleId; }
+ uint32 GetSrcItemId() const { return SrcItemId; }
+ uint32 GetSrcItemCount() const { return SrcItemCount; }
+ uint32 GetSrcSpell() const { return SrcSpell; }
+ std::string GetTitle() const { return Title; }
+ std::string GetDetails() const { return Details; }
+ std::string GetObjectives() const { return Objectives; }
+ std::string GetOfferRewardText() const { return OfferRewardText; }
+ std::string GetRequestItemsText() const { return RequestItemsText; }
+ std::string GetEndText() const { return EndText; }
+ int32 GetRewOrReqMoney() const;
+ uint32 GetRewMoneyMaxLevel() const { return RewMoneyMaxLevel; }
+ // use in XP calculation at client
+ uint32 GetRewSpell() const { return RewSpell; }
+ uint32 GetRewSpellCast() const { return RewSpellCast; }
+ uint32 GetRewMailTemplateId() const { return RewMailTemplateId; }
+ uint32 GetRewMailDelaySecs() const { return RewMailDelaySecs; }
+ uint32 GetPointMapId() const { return PointMapId; }
+ float GetPointX() const { return PointX; }
+ float GetPointY() const { return PointY; }
+ uint32 GetPointOpt() const { return PointOpt; }
+ uint32 GetIncompleteEmote() const { return IncompleteEmote; }
+ uint32 GetCompleteEmote() const { return CompleteEmote; }
+ uint32 GetQuestStartScript() const { return QuestStartScript; }
+ uint32 GetQuestCompleteScript() const { return QuestCompleteScript; }
+ bool IsRepeatable() const { return QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE; }
+ bool IsAutoComplete() const { return Objectives.empty(); }
+ uint32 GetFlags() const { return QuestFlags; }
+ bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; }
+
+ // multiple values
+ std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemId[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT];
+ int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject
+ uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSpell[QUEST_OBJECTIVES_COUNT];
+ uint32 RewChoiceItemId[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewChoiceItemCount[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewItemId[QUEST_REWARDS_COUNT];
+ uint32 RewItemCount[QUEST_REWARDS_COUNT];
+ uint32 RewRepFaction[QUEST_REPUTATIONS_COUNT];
+ int32 RewRepValue[QUEST_REPUTATIONS_COUNT];
+ uint32 DetailsEmote[QUEST_EMOTE_COUNT];
+ uint32 OfferRewardEmote[QUEST_EMOTE_COUNT];
+
+ uint32 GetReqItemsCount() const { return m_reqitemscount; }
+ uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; }
+ uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; }
+ uint32 GetRewItemsCount() const { return m_rewitemscount; }
+
+ typedef std::vector<int32> PrevQuests;
+ PrevQuests prevQuests;
+ typedef std::vector<uint32> PrevChainQuests;
+ PrevChainQuests prevChainQuests;
+
+ // cached data
+ private:
+ uint32 m_reqitemscount;
+ uint32 m_reqCreatureOrGOcount;
+ uint32 m_rewchoiceitemscount;
+ uint32 m_rewitemscount;
+
+ // table data
+ protected:
+ uint32 QuestId;
+ int32 ZoneOrSort;
+ int32 SkillOrClass;
+ uint32 MinLevel;
+ uint32 QuestLevel;
+ uint32 Type;
+ uint32 RequiredRaces;
+ uint32 RequiredSkillValue;
+ uint32 RepObjectiveFaction;
+ int32 RepObjectiveValue;
+ uint32 RequiredMinRepFaction;
+ int32 RequiredMinRepValue;
+ uint32 RequiredMaxRepFaction;
+ int32 RequiredMaxRepValue;
+ uint32 SuggestedPlayers;
+ uint32 LimitTime;
+ uint32 QuestFlags;
+ uint32 CharTitleId;
+ int32 PrevQuestId;
+ int32 NextQuestId;
+ int32 ExclusiveGroup;
+ uint32 NextQuestInChain;
+ uint32 SrcItemId;
+ uint32 SrcItemCount;
+ uint32 SrcSpell;
+ std::string Title;
+ std::string Details;
+ std::string Objectives;
+ std::string OfferRewardText;
+ std::string RequestItemsText;
+ std::string EndText;
+ int32 RewOrReqMoney;
+ uint32 RewMoneyMaxLevel;
+ uint32 RewSpell;
+ uint32 RewSpellCast;
+ uint32 RewMailTemplateId;
+ uint32 RewMailDelaySecs;
+ uint32 PointMapId;
+ float PointX;
+ float PointY;
+ uint32 PointOpt;
+ uint32 IncompleteEmote;
+ uint32 CompleteEmote;
+ uint32 QuestStartScript;
+ uint32 QuestCompleteScript;
+};
+
+enum QuestUpdateState
+{
+ QUEST_UNCHANGED = 0,
+ QUEST_CHANGED = 1,
+ QUEST_NEW = 2
+};
+
+struct QuestStatusData
+{
+ QuestStatusData()
+ : m_status(QUEST_STATUS_NONE),m_rewarded(false),
+ m_explored(false), m_timer(0), uState(QUEST_NEW)
+ {
+ memset(m_itemcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ memset(m_creatureOrGOcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ }
+
+ QuestStatus m_status;
+ bool m_rewarded;
+ bool m_explored;
+ uint32 m_timer;
+ QuestUpdateState uState;
+
+ uint32 m_itemcount[ QUEST_OBJECTIVES_COUNT ];
+ uint32 m_creatureOrGOcount[ QUEST_OBJECTIVES_COUNT ];
+};
+#endif
diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp
new file mode 100644
index 00000000000..56ff45d7ef1
--- /dev/null
+++ b/src/game/QuestHandler.cpp
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "GossipDef.h"
+#include "QuestDef.h"
+#include "ObjectAccessor.h"
+#include "ScriptCalls.h"
+#include "Group.h"
+
+void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+ uint8 questStatus = DIALOG_STATUS_NONE;
+ uint8 defstatus = DIALOG_STATUS_NONE;
+
+ Object* questgiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+ if(!questgiver)
+ {
+ sLog.outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)",GuidHigh2TypeId(GUID_HIPART(guid)),GUID_LOPART(guid));
+ return;
+ }
+
+ switch(questgiver->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ {
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u",uint32(GUID_LOPART(guid)) );
+ Creature* cr_questgiver=(Creature*)questgiver;
+ if( !cr_questgiver->IsHostileTo(_player)) // not show quest status to enemies
+ {
+ questStatus = Script->NPCDialogStatus(_player, cr_questgiver);
+ if( questStatus > 6 )
+ questStatus = getDialogStatus(_player, cr_questgiver, defstatus);
+ }
+ break;
+ }
+ case TYPEID_GAMEOBJECT:
+ {
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u",uint32(GUID_LOPART(guid)) );
+ GameObject* go_questgiver=(GameObject*)questgiver;
+ questStatus = Script->GODialogStatus(_player, go_questgiver);
+ if( questStatus > 6 )
+ questStatus = getDialogStatus(_player, go_questgiver, defstatus);
+ break;
+ }
+ default:
+ sLog.outError("QuestGiver called for unexpected type %u", questgiver->GetTypeId());
+ break;
+ }
+
+ //inform client about status of quest
+ _player->PlayerTalkClass->SendQuestGiverStatus(questStatus, guid);
+}
+
+void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u",guid );
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_NONE);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleQuestgiverHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+ // Stop the npc if moving
+ pCreature->StopMoving();
+
+ if(Script->GossipHello( _player, pCreature ) )
+ return;
+
+ pCreature->prepareGossipMenu(_player,0);
+ pCreature->sendPreparedGossip( _player );
+}
+
+void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 guid;
+ uint32 quest;
+ recv_data >> guid >> quest;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest );
+
+ Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM|TYPEMASK_PLAYER);
+
+ // no or incorrect quest giver
+ if(!pObject
+ || (pObject->GetTypeId()!=TYPEID_PLAYER && !pObject->hasQuest(quest))
+ || (pObject->GetTypeId()==TYPEID_PLAYER && !((Player*)pObject)->CanShareQuest(quest))
+ )
+ {
+ _player->PlayerTalkClass->CloseGossip();
+ _player->SetDivider( 0 );
+ return;
+ }
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest);
+ if ( qInfo )
+ {
+ // prevent cheating
+ if(!GetPlayer()->CanTakeQuest(qInfo,true) )
+ {
+ _player->PlayerTalkClass->CloseGossip();
+ _player->SetDivider( 0 );
+ return;
+ }
+
+ if( _player->GetDivider() != 0 )
+ {
+ Player *pPlayer = ObjectAccessor::FindPlayer( _player->GetDivider() );
+ if( pPlayer )
+ {
+ pPlayer->SendPushToPartyResponse( _player, QUEST_PARTY_MSG_ACCEPT_QUEST );
+ _player->SetDivider( 0 );
+ }
+ }
+
+ if( _player->CanAddQuest( qInfo, true ) )
+ {
+ _player->AddQuest( qInfo, pObject );
+
+ if ( _player->CanCompleteQuest( quest ) )
+ _player->CompleteQuest( quest );
+
+ switch(pObject->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ Script->QuestAccept(_player, ((Creature*)pObject), qInfo );
+ break;
+ case TYPEID_ITEM:
+ case TYPEID_CONTAINER:
+ {
+ Script->ItemQuestAccept(_player, ((Item*)pObject), qInfo );
+
+ // destroy not required for quest finish quest starting item
+ bool destroyItem = true;
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ((qInfo->ReqItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetProto()->MaxCount != 0))
+ {
+ destroyItem = false;
+ break;
+ }
+ }
+
+ if(destroyItem)
+ _player->DestroyItem(((Item*)pObject)->GetBagSlot(),((Item*)pObject)->GetSlot(),true);
+
+ break;
+ }
+ case TYPEID_GAMEOBJECT:
+ Script->GOQuestAccept(_player, ((GameObject*)pObject), qInfo );
+ break;
+ }
+ _player->PlayerTalkClass->CloseGossip();
+
+ if( qInfo->GetSrcSpell() > 0 )
+ _player->CastSpell( _player, qInfo->GetSrcSpell(), true);
+
+ return;
+ }
+ }
+
+ _player->PlayerTalkClass->CloseGossip();
+}
+
+void WorldSession::HandleQuestgiverQuestQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 guid;
+ uint32 quest;
+ recv_data >> guid >> quest;
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest );
+
+ // Verify that the guid is valid and is a questgiver or involved in the requested quest
+ Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM);
+ if(!pObject||!pObject->hasQuest(quest) && !pObject->hasInvolvedQuest(quest))
+ {
+ _player->PlayerTalkClass->CloseGossip();
+ return;
+ }
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest);
+ if ( pQuest )
+ {
+ _player->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, pObject->GetGUID(), true);
+ }
+}
+
+void WorldSession::HandleQuestQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 quest;
+ recv_data >> quest;
+ sLog.outDebug( "WORLD: Received CMSG_QUEST_QUERY quest = %u",quest );
+
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest);
+ if ( pQuest )
+ {
+ _player->PlayerTalkClass->SendQuestQueryResponse( pQuest );
+ }
+}
+
+void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ uint32 quest, reward;
+ uint64 guid;
+ recv_data >> guid >> quest >> reward;
+
+ if(reward >= QUEST_REWARD_CHOICES_COUNT)
+ {
+ sLog.outError("Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player %s (guid %d) tried to get invalid reward (%u) (probably packet hacking)", _player->GetName(), _player->GetGUIDLow(), reward);
+ return;
+ }
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid)),quest,reward );
+
+ Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+ if(!pObject)
+ return;
+
+ if(!pObject->hasInvolvedQuest(quest))
+ return;
+
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest);
+ if( pQuest )
+ {
+ if( _player->CanRewardQuest( pQuest, reward, true ) )
+ {
+ _player->RewardQuest( pQuest, reward, pObject );
+
+ switch(pObject->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ if( !(Script->ChooseReward( _player, ((Creature*)pObject), pQuest, reward )) )
+ {
+ // Send next quest
+ if(Quest const* nextquest = _player->GetNextQuest( guid ,pQuest ) )
+ _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true);
+ }
+ break;
+ case TYPEID_GAMEOBJECT:
+ if( !Script->GOChooseReward( _player, ((GameObject*)pObject), pQuest, reward ) )
+ {
+ // Send next quest
+ if(Quest const* nextquest = _player->GetNextQuest( guid ,pQuest ) )
+ _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true);
+ }
+ break;
+ }
+ }
+ else
+ _player->PlayerTalkClass->SendQuestGiverOfferReward( pQuest, guid, true );
+ }
+}
+
+void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint32 quest;
+ uint64 guid;
+ recv_data >> guid >> quest;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest );
+
+ Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+ if(!pObject||!pObject->hasInvolvedQuest(quest))
+ return;
+
+ if ( _player->CanCompleteQuest( quest ) )
+ _player->CompleteQuest( quest );
+
+ if( _player->GetQuestStatus( quest ) != QUEST_STATUS_COMPLETE )
+ return;
+
+ if(Quest const *pQuest = objmgr.GetQuestTemplate(quest))
+ _player->PlayerTalkClass->SendQuestGiverOfferReward( pQuest, guid, true );
+}
+
+void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recv_data*/ )
+{
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_CANCEL" );
+
+ _player->PlayerTalkClass->CloseGossip();
+}
+
+void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ uint8 slot1, slot2;
+ recv_data >> slot1 >> slot2;
+
+ if(slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE)
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1, slot2 );
+
+ GetPlayer()->SwapQuestSlot(slot1,slot2);
+}
+
+void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1);
+
+ uint8 slot;
+ recv_data >> slot;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u",slot );
+
+ if( slot < MAX_QUEST_LOG_SIZE )
+ {
+ if(uint32 quest = _player->GetQuestSlotQuestId(slot))
+ {
+ if(!_player->TakeQuestSourceItem( quest, true ))
+ return; // can't un-equip some items, reject quest cancel
+
+ _player->SetQuestStatus( quest, QUEST_STATUS_NONE);
+ }
+
+ _player->SetQuestSlot(slot, 0);
+ }
+}
+
+void WorldSession::HandleQuestConfirmAccept(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 quest;
+ recv_data >> quest;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest );
+}
+
+void WorldSession::HandleQuestComplete(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint32 quest;
+ uint64 guid;
+ recv_data >> guid >> quest;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest );
+
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest);
+ if( pQuest )
+ {
+ if( _player->GetQuestStatus( quest ) != QUEST_STATUS_COMPLETE )
+ {
+ if( pQuest->IsRepeatable() )
+ _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanCompleteRepeatableQuest(pQuest), false);
+ else
+ _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest,false), false);
+ }
+ else
+ _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest,false), false);
+ }
+}
+
+void WorldSession::HandleQuestAutoLaunch(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH (Send your log to anakin if you see this message)" );
+}
+
+void WorldSession::HandleQuestPushToParty(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4);
+
+ uint32 quest;
+ recvPacket >> quest;
+
+ sLog.outDebug( "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", quest );
+
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest);
+ if( pQuest )
+ {
+ if( _player->GetGroup() )
+ {
+ Group *pGroup = _player->GetGroup();
+ if( pGroup )
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pPlayer = itr->getSource();
+ if (!pPlayer || pPlayer == _player) // skip self
+ continue;
+
+ _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST);
+
+ if( _player->GetDistance( pPlayer ) > 10 )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_TOO_FAR );
+ continue;
+ }
+
+ if( !pPlayer->SatisfyQuestStatus( pQuest, false ) )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_HAVE_QUEST );
+ continue;
+ }
+
+ if( pPlayer->GetQuestStatus( quest ) == QUEST_STATUS_COMPLETE )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_FINISH_QUEST );
+ continue;
+ }
+
+ if( !pPlayer->CanTakeQuest( pQuest, false ) )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_CANT_TAKE_QUEST );
+ continue;
+ }
+
+ if( !pPlayer->SatisfyQuestLog( false ) )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_LOG_FULL );
+ continue;
+ }
+
+ if( pPlayer->GetDivider() != 0 )
+ {
+ _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_BUSY );
+ continue;
+ }
+
+ pPlayer->PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, _player->GetGUID(), true );
+ pPlayer->SetDivider( _player->GetGUID() );
+ }
+ }
+ }
+ }
+}
+
+void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8+1);
+
+ uint64 guid;
+ uint8 msg;
+ recvPacket >> guid >> msg;
+
+ sLog.outDebug( "WORLD: Received MSG_QUEST_PUSH_RESULT" );
+
+ if( _player->GetDivider() != 0 )
+ {
+ Player *pPlayer = ObjectAccessor::FindPlayer( _player->GetDivider() );
+ if( pPlayer )
+ {
+ WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) );
+ data << uint64(guid);
+ data << uint8(msg); // valid values: 0-8
+ pPlayer->GetSession()->SendPacket(&data);
+ _player->SetDivider( 0 );
+ }
+ }
+}
+
+uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus)
+{
+ uint32 result = defstatus;
+
+ QuestRelations const* qir;
+ QuestRelations const* qr;
+
+ switch(questgiver->GetTypeId())
+ {
+ case TYPEID_GAMEOBJECT:
+ {
+ qir = &objmgr.mGOQuestInvolvedRelations;
+ qr = &objmgr.mGOQuestRelations;
+ break;
+ }
+ case TYPEID_UNIT:
+ {
+ qir = &objmgr.mCreatureQuestInvolvedRelations;
+ qr = &objmgr.mCreatureQuestRelations;
+ break;
+ }
+ default:
+ //its imposible, but check ^)
+ sLog.outError("Warning: GetDialogStatus called for unexpected type %u", questgiver->GetTypeId());
+ return DIALOG_STATUS_NONE;
+ }
+
+ for(QuestRelations::const_iterator i = qir->lower_bound(questgiver->GetEntry()); i != qir->upper_bound(questgiver->GetEntry()); ++i )
+ {
+ uint32 result2 = 0;
+ uint32 quest_id = i->second;
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest_id);
+ if ( !pQuest ) continue;
+
+ QuestStatus status = pPlayer->GetQuestStatus( quest_id );
+ if( (status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) ||
+ (pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false)) )
+ {
+ if ( pQuest->IsAutoComplete() && pQuest->IsRepeatable() )
+ result2 = DIALOG_STATUS_REWARD_REP;
+ else
+ result2 = DIALOG_STATUS_REWARD;
+ }
+ else if ( status == QUEST_STATUS_INCOMPLETE )
+ result2 = DIALOG_STATUS_INCOMPLETE;
+
+ if (result2 > result)
+ result = result2;
+ }
+
+ for(QuestRelations::const_iterator i = qr->lower_bound(questgiver->GetEntry()); i != qr->upper_bound(questgiver->GetEntry()); ++i )
+ {
+ uint32 result2 = 0;
+ uint32 quest_id = i->second;
+ Quest const *pQuest = objmgr.GetQuestTemplate(quest_id);
+ if ( !pQuest )
+ continue;
+
+ QuestStatus status = pPlayer->GetQuestStatus( quest_id );
+ if ( status == QUEST_STATUS_NONE )
+ {
+ if ( pPlayer->CanSeeStartQuest( pQuest ) )
+ {
+ if ( pPlayer->SatisfyQuestLevel(pQuest, false) )
+ {
+ if ( pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded))
+ result2 = DIALOG_STATUS_REWARD_REP;
+ else if (pPlayer->getLevel() <= pQuest->GetQuestLevel() + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) )
+ {
+ if (pQuest->HasFlag(QUEST_FLAGS_DAILY))
+ result2 = DIALOG_STATUS_AVAILABLE_REP;
+ else
+ result2 = DIALOG_STATUS_AVAILABLE;
+ }
+ else
+ result2 = DIALOG_STATUS_CHAT;
+ }
+ else
+ result2 = DIALOG_STATUS_UNAVAILABLE;
+ }
+ }
+
+ if (result2 > result)
+ result = result2;
+ }
+
+ if(questgiver->GetTypeId()==TYPEID_UNIT && ((Creature*)questgiver)->isCanTrainingAndResetTalentsOf(pPlayer) && result < DIALOG_STATUS_CHAT)
+ result = DIALOG_STATUS_CHAT;
+
+ return result;
+}
+
+void WorldSession::HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
+
+ uint32 count = 0;
+
+ WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
+ data << uint32(count); // placeholder
+
+ for(Player::ClientGUIDs::iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr)
+ {
+ uint8 questStatus = DIALOG_STATUS_NONE;
+ uint8 defstatus = DIALOG_STATUS_NONE;
+
+ if(IS_CREATURE_GUID(*itr))
+ {
+ Creature *questgiver = ObjectAccessor::GetCreature(*_player, *itr);
+ if(!questgiver || questgiver->IsHostileTo(_player))
+ continue;
+ if(!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
+ continue;
+ questStatus = Script->NPCDialogStatus(_player, questgiver);
+ if( questStatus > 6 )
+ questStatus = getDialogStatus(_player, questgiver, defstatus);
+
+ data << uint64(questgiver->GetGUID());
+ data << uint8(questStatus);
+ ++count;
+ }
+ else if(IS_GAMEOBJECT_GUID(*itr))
+ {
+ GameObject *questgiver = ObjectAccessor::GetGameObject(*_player, *itr);
+ if(!questgiver)
+ continue;
+ if(questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
+ continue;
+ questStatus = Script->GODialogStatus(_player, questgiver);
+ if( questStatus > 6 )
+ questStatus = getDialogStatus(_player, questgiver, defstatus);
+
+ data << uint64(questgiver->GetGUID());
+ data << uint8(questStatus);
+ ++count;
+ }
+ }
+
+ data.put<uint32>(0, count); // write real count
+ SendPacket(&data);
+}
diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp
new file mode 100644
index 00000000000..1022a0699f6
--- /dev/null
+++ b/src/game/RandomMovementGenerator.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Creature.h"
+#include "MapManager.h"
+#include "RandomMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "Map.h"
+#include "Util.h"
+
+#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+
+template<>
+void
+RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
+{
+ float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist;
+
+ creature.GetRespawnCoord(X, Y, Z);
+ creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance);
+
+ z = creature.GetPositionZ();
+ uint32 mapid=creature.GetMapId();
+ Map const* map = MapManager::Instance().GetBaseMap(mapid);
+
+ // For 2D/3D system selection
+ bool is_land_ok = creature.canWalk();
+ bool is_water_ok = creature.canSwim();
+ bool is_air_ok = creature.canFly();
+
+ const float angle = rand_norm()*(M_PI*2);
+ const float range = rand_norm()*wander_distance;
+ const float distanceX = range * cos(angle);
+ const float distanceY = range * sin(angle);
+
+ nx = X + distanceX;
+ ny = Y + distanceY;
+ dist = distanceX*distanceX + distanceY*distanceY;
+
+ if (is_air_ok) // 3D system above ground and above water (flying mode)
+ {
+ const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
+ nz = Z + distanceZ;
+ float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height.
+ float wz = map->GetWaterLevel(nx, ny);
+ if (tz >= nz || wz >= nz)
+ return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick
+ }
+ //else if (is_water_ok) // 3D system under water and above ground (swimming mode)
+ else // 2D only
+ {
+ dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE)
+ // The fastest way to get an accurate result 90% of the time.
+ // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher
+ if (fabs(nz-Z)>dist)
+ return; // let's forget this bad coords where a z cannot be find and retry at next tick
+ }
+ }
+ }
+
+ Traveller<Creature> traveller(creature);
+ creature.SetOrientation(creature.GetAngle(nx,ny));
+ i_destinationHolder.SetDestination(traveller, nx, ny, nz);
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (is_air_ok)
+ {
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ }
+ //else if (is_water_ok) // Swimming mode to be done with more than this check
+ else
+ {
+ i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
+ }
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Initialize(Creature &creature)
+{
+ if(!creature.isAlive())
+ return;
+
+ if (creature.canFly())
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE );
+ _setRandomLocation(creature);
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Reset(Creature &creature)
+{
+ Initialize(creature);
+}
+
+template<>
+bool
+RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ {
+ i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+ return true;
+ }
+
+ i_nextMoveTime.Update(diff);
+
+ if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+
+ if(!i_destinationHolder.HasArrived() && creature.IsStopped())
+ creature.addUnitState(UNIT_STAT_ROAMING);
+
+ CreatureTraveller traveller(creature);
+
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false, true) )
+ {
+ if(i_nextMoveTime.Passed())
+ {
+ if (creature.canFly())
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f)
+ {
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ }
+ return true;
+}
diff --git a/src/game/RandomMovementGenerator.h b/src/game/RandomMovementGenerator.h
new file mode 100644
index 00000000000..62b836e4261
--- /dev/null
+++ b/src/game/RandomMovementGenerator.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_RANDOMMOTIONGENERATOR_H
+#define MANGOS_RANDOMMOTIONGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+
+template<class T>
+class MANGOS_DLL_SPEC RandomMovementGenerator
+: public MovementGeneratorMedium< T, RandomMovementGenerator<T> >
+{
+ public:
+ RandomMovementGenerator(const Unit &) : i_nextMoveTime(0) {}
+
+ void _setRandomLocation(T &);
+ void Initialize(T &);
+ void Finalize(T &) {}
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+ void UpdateMapPosition(uint32 mapid, float &x ,float &y, float &z)
+ {
+ i_destinationHolder.GetLocationNow(mapid, x,y,z);
+ }
+ MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
+ private:
+ TimeTrackerSmall i_nextMoveTime;
+
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ uint32 i_nextMove;
+};
+#endif
diff --git a/src/game/ReactorAI.cpp b/src/game/ReactorAI.cpp
new file mode 100644
index 00000000000..a91af776e6d
--- /dev/null
+++ b/src/game/ReactorAI.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ByteBuffer.h"
+#include "ReactorAI.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "Log.h"
+#include "ObjectAccessor.h"
+
+#define REACTOR_VISIBLE_RANGE (26.46f)
+
+int
+ReactorAI::Permissible(const Creature *creature)
+{
+ if( creature->isCivilian() || creature->IsNeutralToAll() )
+ return PERMIT_BASE_REACTIVE;
+
+ return PERMIT_BASE_NO;
+}
+
+void
+ReactorAI::MoveInLineOfSight(Unit *)
+{
+}
+
+void
+ReactorAI::AttackStart(Unit *p)
+{
+ if(!p)
+ return;
+
+ if(i_creature.Attack(p,true))
+ {
+ DEBUG_LOG("Tag unit GUID: %u (TypeId: %u) as a victim", p->GetGUIDLow(), p->GetTypeId());
+ i_creature.SetInCombatWith(p);
+ p->SetInCombatWith(&i_creature);
+
+ i_creature.AddThreat(p, 0.0f);
+ i_victimGuid = p->GetGUID();
+ i_creature.GetMotionMaster()->MoveChase(p);
+ }
+}
+
+bool
+ReactorAI::IsVisible(Unit *) const
+{
+ return false;
+}
+
+void
+ReactorAI::UpdateAI(const uint32 /*time_diff*/)
+{
+ // update i_victimGuid if i_creature.getVictim() !=0 and changed
+ if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
+ return;
+
+ i_victimGuid = i_creature.getVictim()->GetGUID();
+
+ if( i_creature.isAttackReady() )
+ {
+ if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
+ {
+ i_creature.AttackerStateUpdate(i_creature.getVictim());
+ i_creature.resetAttackTimer();
+ }
+ }
+}
+
+void
+ReactorAI::EnterEvadeMode()
+{
+ if( !i_creature.isAlive() )
+ {
+ DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow());
+ i_creature.GetMotionMaster()->MovementExpired();
+ i_creature.GetMotionMaster()->MoveIdle();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.DeleteThreatList();
+ return;
+ }
+
+ Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
+
+ if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking due to target %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", i_creature.GetGUIDLow());
+ }
+
+ i_creature.RemoveAllAuras();
+ i_creature.DeleteThreatList();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.SetLootRecipient(NULL);
+
+ // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+ if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ i_creature.GetMotionMaster()->MoveTargetedHome();
+}
diff --git a/src/game/ReactorAI.h b/src/game/ReactorAI.h
new file mode 100644
index 00000000000..a64208b89ef
--- /dev/null
+++ b/src/game/ReactorAI.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_REACTORAI_H
+#define MANGOS_REACTORAI_H
+
+#include "CreatureAI.h"
+
+class Unit;
+
+class MANGOS_DLL_DECL ReactorAI : public CreatureAI
+{
+ public:
+
+ ReactorAI(Creature &c) : i_creature(c), i_victimGuid(0) {}
+
+ void MoveInLineOfSight(Unit *);
+ void AttackStart(Unit *);
+ void EnterEvadeMode();
+ bool IsVisible(Unit *) const;
+
+ void UpdateAI(const uint32);
+ static int Permissible(const Creature *);
+
+ private:
+ Creature &i_creature;
+ uint64 i_victimGuid;
+};
+#endif
diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp
new file mode 100644
index 00000000000..c3021958804
--- /dev/null
+++ b/src/game/ScriptCalls.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
+
+#include "Platform/Define.h"
+#include "ScriptCalls.h"
+
+ScriptsSet Script=NULL;
+
+void UnloadScriptingModule()
+{
+ if(Script)
+ {
+ //todo: some check if some func from script library is called right now
+ Script->ScriptsFree();
+ MANGOS_CLOSE_LIBRARY(Script->hScriptsLib);
+ delete Script;
+ Script = NULL;
+ }
+}
+
+bool LoadScriptingModule(char const* libName)
+{
+ ScriptsSet testScript=new _ScriptSet;
+
+ std::string name = strlen(libName) ? libName : MANGOS_SCRIPT_NAME;
+ name += MANGOS_SCRIPT_EXT;
+
+ testScript->hScriptsLib=MANGOS_LOAD_LIBRARY(name.c_str());
+
+ if(!testScript->hScriptsLib )
+ {
+ printf("Error loading Scripts Library %s !\n",name.c_str());
+ delete testScript;
+ return false;
+ }
+
+ if( !(testScript->ScriptsInit =(scriptCallScriptsInit )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ScriptsInit" ))
+ ||!(testScript->ScriptsFree =(scriptCallScriptsFree )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ScriptsFree" ))
+ ||!(testScript->GossipHello =(scriptCallGossipHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipHello" ))
+ ||!(testScript->GOChooseReward =(scriptCallGOChooseReward )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOChooseReward" ))
+ ||!(testScript->QuestAccept =(scriptCallQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestAccept" ))
+ ||!(testScript->GossipSelect =(scriptCallGossipSelect )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipSelect" ))
+ ||!(testScript->GossipSelectWithCode=(scriptCallGossipSelectWithCode)MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipSelectWithCode"))
+ ||!(testScript->QuestSelect =(scriptCallQuestSelect )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestSelect" ))
+ ||!(testScript->QuestComplete =(scriptCallQuestComplete )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestComplete" ))
+ ||!(testScript->NPCDialogStatus =(scriptCallNPCDialogStatus )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"NPCDialogStatus" ))
+ ||!(testScript->GODialogStatus =(scriptCallGODialogStatus )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GODialogStatus" ))
+ ||!(testScript->ChooseReward =(scriptCallChooseReward )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ChooseReward" ))
+ ||!(testScript->ItemHello =(scriptCallItemHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemHello" ))
+ ||!(testScript->GOHello =(scriptCallGOHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOHello" ))
+ ||!(testScript->scriptAreaTrigger =(scriptCallAreaTrigger )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"AreaTrigger" ))
+ ||!(testScript->ItemQuestAccept =(scriptCallItemQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemQuestAccept" ))
+ ||!(testScript->GOQuestAccept =(scriptCallGOQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOQuestAccept" ))
+ ||!(testScript->ReceiveEmote =(scriptCallReceiveEmote )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ReceiveEmote" ))
+ ||!(testScript->ItemUse =(scriptCallItemUse )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemUse" ))
+ ||!(testScript->GetAI =(scriptCallGetAI )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GetAI" ))
+ ||!(testScript->CreateInstanceData =(scriptCallCreateInstanceData )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"CreateInstanceData" ))
+ )
+ {
+ printf("Error loading Scripts Library %s !\n Library missing required functions.",name.c_str());
+ MANGOS_CLOSE_LIBRARY(testScript->hScriptsLib);
+ delete testScript;
+ return false;
+ }
+
+ printf("Scripts Library %s was successfully loaded.\n",name.c_str());
+
+ //heh we are still there :P we have a valid library
+ //we reload script
+ UnloadScriptingModule();
+
+ Script=testScript;
+ Script->ScriptsInit();
+
+ return true;
+}
diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h
new file mode 100644
index 00000000000..07c415497ad
--- /dev/null
+++ b/src/game/ScriptCalls.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __SCRIPT_CALLS_H
+#define __SCRIPT_CALLS_H
+
+#include "Common.h"
+#include "ObjectMgr.h"
+
+class Creature;
+class CreatureAI;
+class GameObject;
+class Item;
+class Player;
+class Quest;
+class SpellCastTargets;
+class Map;
+class InstanceData;
+
+bool LoadScriptingModule(char const* libName = "");
+void UnloadScriptingModule();
+
+typedef void(MANGOS_IMPORT * scriptCallScriptsInit) ();
+typedef void(MANGOS_IMPORT * scriptCallScriptsFree) ();
+
+typedef bool(MANGOS_IMPORT * scriptCallGossipHello) (Player *player, Creature *_Creature );
+typedef bool(MANGOS_IMPORT * scriptCallQuestAccept) (Player *player, Creature *_Creature, Quest const *);
+typedef bool(MANGOS_IMPORT * scriptCallGossipSelect)(Player *player, Creature *_Creature, uint32 sender, uint32 action);
+typedef bool(MANGOS_IMPORT * scriptCallGossipSelectWithCode)( Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode );
+typedef bool(MANGOS_IMPORT * scriptCallQuestSelect)( Player *player, Creature *_Creature, Quest const* );
+typedef bool(MANGOS_IMPORT * scriptCallQuestComplete)(Player *player, Creature *_Creature, Quest const*);
+typedef uint32(MANGOS_IMPORT * scriptCallNPCDialogStatus)( Player *player, Creature *_Creature);
+typedef uint32(MANGOS_IMPORT * scriptCallGODialogStatus)( Player *player, GameObject * _GO);
+typedef bool(MANGOS_IMPORT * scriptCallChooseReward)( Player *player, Creature *_Creature, Quest const*, uint32 opt );
+typedef bool(MANGOS_IMPORT * scriptCallItemHello)( Player *player, Item *, Quest const*);
+typedef bool(MANGOS_IMPORT * scriptCallGOHello)( Player *player, GameObject * );
+typedef bool(MANGOS_IMPORT * scriptCallAreaTrigger)( Player *player, AreaTriggerEntry const* );
+typedef bool(MANGOS_IMPORT * scriptCallItemQuestAccept)(Player *player, Item *, Quest const*);
+typedef bool(MANGOS_IMPORT * scriptCallGOQuestAccept)(Player *player, GameObject *, Quest const*);
+typedef bool(MANGOS_IMPORT * scriptCallGOChooseReward)(Player *player, GameObject *, Quest const*, uint32 opt );
+typedef bool(MANGOS_IMPORT * scriptCallReceiveEmote) ( Player *player, Creature *_Creature, uint32 emote );
+typedef bool(MANGOS_IMPORT * scriptCallItemUse) (Player *player, Item *_Item, SpellCastTargets const& targets);
+typedef CreatureAI* (MANGOS_IMPORT * scriptCallGetAI) ( Creature *_Creature );
+typedef InstanceData* (MANGOS_IMPORT * scriptCallCreateInstanceData) (Map *map);
+
+typedef struct
+{
+ scriptCallScriptsInit ScriptsInit;
+ scriptCallScriptsFree ScriptsFree;
+
+ scriptCallGossipHello GossipHello;
+ scriptCallGOChooseReward GOChooseReward;
+ scriptCallQuestAccept QuestAccept;
+ scriptCallGossipSelect GossipSelect;
+ scriptCallGossipSelectWithCode GossipSelectWithCode;
+ scriptCallQuestSelect QuestSelect;
+ scriptCallQuestComplete QuestComplete;
+ scriptCallNPCDialogStatus NPCDialogStatus;
+ scriptCallGODialogStatus GODialogStatus;
+ scriptCallChooseReward ChooseReward;
+ scriptCallItemHello ItemHello;
+ scriptCallGOHello GOHello;
+ scriptCallAreaTrigger scriptAreaTrigger;
+ scriptCallItemQuestAccept ItemQuestAccept;
+ scriptCallGOQuestAccept GOQuestAccept;
+ scriptCallReceiveEmote ReceiveEmote;
+ scriptCallItemUse ItemUse;
+ scriptCallGetAI GetAI;
+ scriptCallCreateInstanceData CreateInstanceData;
+
+ MANGOS_LIBRARY_HANDLE hScriptsLib;
+}_ScriptSet,*ScriptsSet;
+
+extern ScriptsSet Script;
+#endif
diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h
new file mode 100644
index 00000000000..06c6daead2a
--- /dev/null
+++ b/src/game/SharedDefines.h
@@ -0,0 +1,1997 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_SHAREDDEFINES_H
+#define MANGOS_SHAREDDEFINES_H
+
+#include "Platform/Define.h"
+#include <cassert>
+
+enum Gender
+{
+ GENDER_MALE = 0,
+ GENDER_FEMALE = 1,
+ GENDER_NONE = 2
+};
+
+// Race value is index in ChrRaces.dbc
+enum Races
+{
+ RACE_HUMAN = 1,
+ RACE_ORC = 2,
+ RACE_DWARF = 3,
+ RACE_NIGHTELF = 4,
+ RACE_UNDEAD_PLAYER = 5,
+ RACE_TAUREN = 6,
+ RACE_GNOME = 7,
+ RACE_TROLL = 8,
+ RACE_GOBLIN = 9,
+ RACE_BLOODELF = 10,
+ RACE_DRAENEI = 11,
+ RACE_FEL_ORC = 12,
+ RACE_NAGA = 13,
+ RACE_BROKEN = 14,
+ RACE_SKELETON = 15,
+ MAX_RACES = 16
+};
+
+#define RACEMASK_ALL_PLAYABLE \
+ ((1<<(RACE_HUMAN-1)) |(1<<(RACE_ORC-1)) |(1<<(RACE_DWARF-1)) | \
+ (1<<(RACE_NIGHTELF-1))|(1<<(RACE_UNDEAD_PLAYER-1))|(1<<(RACE_TAUREN-1)) | \
+ (1<<(RACE_GNOME-1)) |(1<<(RACE_TROLL-1)) |(1<<(RACE_BLOODELF-1))| \
+ (1<<(RACE_DRAENEI-1)) )
+
+// Class value is index in ChrClasses.dbc
+enum Classes
+{
+ CLASS_WARRIOR = 1,
+ CLASS_PALADIN = 2,
+ CLASS_HUNTER = 3,
+ CLASS_ROGUE = 4,
+ CLASS_PRIEST = 5,
+ // CLASS_UNK1 = 6, DK
+ CLASS_SHAMAN = 7,
+ CLASS_MAGE = 8,
+ CLASS_WARLOCK = 9,
+ // CLASS_UNK2 = 10,unused
+ CLASS_DRUID = 11,
+ MAX_CLASSES = 12
+};
+
+#define CLASSMASK_ALL_PLAYABLE \
+ ((1<<(CLASS_WARRIOR-1))|(1<<(CLASS_PALADIN-1))|(1<<(CLASS_HUNTER-1))| \
+ (1<<(CLASS_ROGUE-1)) |(1<<(CLASS_PRIEST-1)) |(1<<(CLASS_SHAMAN-1))| \
+ (1<<(CLASS_MAGE-1)) |(1<<(CLASS_WARLOCK-1))|(1<<(CLASS_DRUID-1)) )
+
+#define CLASSMASK_WAND_USERS ((1<<(CLASS_PRIEST-1))|(1<<(CLASS_MAGE-1))|(1<<(CLASS_WARLOCK-1)))
+
+#define PLAYER_MAX_BATTLEGROUND_QUEUES 3
+
+enum ReputationRank
+{
+ REP_HATED = 0,
+ REP_HOSTILE = 1,
+ REP_UNFRIENDLY = 2,
+ REP_NEUTRAL = 3,
+ REP_FRIENDLY = 4,
+ REP_HONORED = 5,
+ REP_REVERED = 6,
+ REP_EXALTED = 7
+};
+
+#define MIN_REPUTATION_RANK (REP_HATED)
+#define MAX_REPUTATION_RANK 8
+
+enum MoneyConstants
+{
+ COPPER = 1,
+ SILVER = COPPER*100,
+ GOLD = SILVER*100
+};
+
+enum Stats
+{
+ STAT_STRENGTH = 0,
+ STAT_AGILITY = 1,
+ STAT_STAMINA = 2,
+ STAT_INTELLECT = 3,
+ STAT_SPIRIT = 4
+};
+
+#define MAX_STATS 5
+
+enum Powers
+{
+ POWER_MANA = 0,
+ POWER_RAGE = 1,
+ POWER_FOCUS = 2,
+ POWER_ENERGY = 3,
+ POWER_HAPPINESS = 4,
+ POWER_RUNES = 5,
+ POWER_HEALTH = 0xFFFFFFFE // (-2 as signed value)
+};
+
+#define MAX_POWERS 5 // not count POWER_RUNES for now
+
+enum SpellSchools
+{
+ SPELL_SCHOOL_NORMAL = 0,
+ SPELL_SCHOOL_HOLY = 1,
+ SPELL_SCHOOL_FIRE = 2,
+ SPELL_SCHOOL_NATURE = 3,
+ SPELL_SCHOOL_FROST = 4,
+ SPELL_SCHOOL_SHADOW = 5,
+ SPELL_SCHOOL_ARCANE = 6
+};
+
+#define MAX_SPELL_SCHOOL 7
+
+enum SpellSchoolMask
+{
+ SPELL_SCHOOL_MASK_NONE = 0x00, // not exist
+ SPELL_SCHOOL_MASK_NORMAL = (1 << SPELL_SCHOOL_NORMAL), // PHYSICAL (Armor)
+ SPELL_SCHOOL_MASK_HOLY = (1 << SPELL_SCHOOL_HOLY ),
+ SPELL_SCHOOL_MASK_FIRE = (1 << SPELL_SCHOOL_FIRE ),
+ SPELL_SCHOOL_MASK_NATURE = (1 << SPELL_SCHOOL_NATURE),
+ SPELL_SCHOOL_MASK_FROST = (1 << SPELL_SCHOOL_FROST ),
+ SPELL_SCHOOL_MASK_SHADOW = (1 << SPELL_SCHOOL_SHADOW),
+ SPELL_SCHOOL_MASK_ARCANE = (1 << SPELL_SCHOOL_ARCANE),
+
+ // unions
+
+ // 124, not include normal and holy damage
+ SPELL_SCHOOL_MASK_SPELL = ( SPELL_SCHOOL_MASK_FIRE |
+ SPELL_SCHOOL_MASK_NATURE | SPELL_SCHOOL_MASK_FROST |
+ SPELL_SCHOOL_MASK_SHADOW | SPELL_SCHOOL_MASK_ARCANE ),
+ // 126
+ SPELL_SCHOOL_MASK_MAGIC = ( SPELL_SCHOOL_MASK_HOLY | SPELL_SCHOOL_MASK_SPELL ),
+
+ // 127
+ SPELL_SCHOOL_MASK_ALL = ( SPELL_SCHOOL_MASK_NORMAL | SPELL_SCHOOL_MASK_MAGIC )
+};
+
+#define SPELL_SCHOOL_MASK_MAGIC \
+ ( SPELL_SCHOOL_MASK_HOLY | SPELL_SCHOOL_MASK_FIRE | SPELL_SCHOOL_MASK_NATURE | \
+ SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW | \
+ SPELL_SCHOOL_MASK_ARCANE )
+
+inline SpellSchools GetFirstSchoolInMask(SpellSchoolMask mask)
+{
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if(mask & (1 << i))
+ return SpellSchools(i);
+
+ return SPELL_SCHOOL_NORMAL;
+}
+
+enum ItemQualities
+{
+ ITEM_QUALITY_POOR = 0, //GREY
+ ITEM_QUALITY_NORMAL = 1, //WHITE
+ ITEM_QUALITY_UNCOMMON = 2, //GREEN
+ ITEM_QUALITY_RARE = 3, //BLUE
+ ITEM_QUALITY_EPIC = 4, //PURPLE
+ ITEM_QUALITY_LEGENDARY = 5, //ORANGE
+ ITEM_QUALITY_ARTIFACT = 6 //LIGHT YELLOW
+};
+
+#define MAX_ITEM_QUALITY 7
+
+// ***********************************
+// Spell Attributes definitions
+// ***********************************
+
+#define SPELL_ATTR_UNK0 0x00000001 // 0
+#define SPELL_ATTR_RANGED 0x00000002 // 1 All ranged abilites have this flag
+#define SPELL_ATTR_ON_NEXT_SWING_1 0x00000004 // 2 on next swing
+#define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 2.4.2
+#define SPELL_ATTR_UNK4 0x00000010 // 4
+#define SPELL_ATTR_UNK5 0x00000020 // 5 trade spells?
+#define SPELL_ATTR_PASSIVE 0x00000040 // 6 Passive spell
+#define SPELL_ATTR_UNK7 0x00000080 // 7 visible?
+#define SPELL_ATTR_UNK8 0x00000100 // 8
+#define SPELL_ATTR_UNK9 0x00000200 // 9
+#define SPELL_ATTR_ON_NEXT_SWING_2 0x00000400 // 10 on next swing 2
+#define SPELL_ATTR_UNK11 0x00000800 // 11
+#define SPELL_ATTR_DAYTIME_ONLY 0x00001000 // 12 only useable at daytime, not set in 2.4.2
+#define SPELL_ATTR_NIGHT_ONLY 0x00002000 // 13 only useable at night, not set in 2.4.2
+#define SPELL_ATTR_INDOORS_ONLY 0x00004000 // 14 only useable indoors, not set in 2.4.2
+#define SPELL_ATTR_OUTDOORS_ONLY 0x00008000 // 15 Only useable outdoors.
+#define SPELL_ATTR_NOT_SHAPESHIFT 0x00010000 // 16 Not while shapeshifted
+#define SPELL_ATTR_ONLY_STEALTHED 0x00020000 // 17 Must be in stealth
+#define SPELL_ATTR_UNK18 0x00040000 // 18
+#define SPELL_ATTR_LEVEL_DAMAGE_CALCULATION 0x00080000 // 19 spelldamage depends on caster level
+#define SPELL_ATTR_STOP_ATTACK_TARGET 0x00100000 // 20 Stop attack after use this spell (and not begin attack if use)
+#define SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK 0x00200000 // 21 Cannot be dodged/parried/blocked
+#define SPELL_ATTR_UNK22 0x00400000 // 22
+#define SPELL_ATTR_UNK23 0x00800000 // 23 castable while dead?
+#define SPELL_ATTR_CASTABLE_WHILE_MOUNTED 0x01000000 // 24 castable while mounted
+#define SPELL_ATTR_DISABLED_WHILE_ACTIVE 0x02000000 // 25 Activate and start cooldown after aura fade or remove summoned creature or go
+#define SPELL_ATTR_UNK26 0x04000000 // 26
+#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_UNK30 0x40000000 // 30 breakable by damage?
+#define SPELL_ATTR_CANT_CANCEL 0x80000000 // 31 positive aura can't be canceled
+
+#define SPELL_ATTR_EX_UNK0 0x00000001 // 0
+#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 1
+#define SPELL_ATTR_EX_UNK3 0x00000008 // 3
+#define SPELL_ATTR_EX_UNK4 0x00000010 // 4
+#define SPELL_ATTR_EX_NOT_BREAK_STEALTH 0x00000020 // 5 Not break stealth
+#define SPELL_ATTR_EX_CHANNELED_2 0x00000040 // 6 channeled 2
+#define SPELL_ATTR_EX_NEGATIVE 0x00000080 // 7
+#define SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET 0x00000100 // 8 Spell req target not to be in combat state
+#define SPELL_ATTR_EX_UNK9 0x00000200 // 9
+#define SPELL_ATTR_EX_UNK10 0x00000400 // 10
+#define SPELL_ATTR_EX_UNK11 0x00000800 // 11
+#define SPELL_ATTR_EX_UNK12 0x00001000 // 12
+#define SPELL_ATTR_EX_UNK13 0x00002000 // 13
+#define SPELL_ATTR_EX_UNK14 0x00004000 // 14
+#define SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY 0x00008000 // 15 remove auras on immunity
+#define SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE 0x00010000 // 16 unaffected by school immunity
+#define SPELL_ATTR_EX_UNK17 0x00020000 // 17
+#define SPELL_ATTR_EX_UNK18 0x00040000 // 18
+#define SPELL_ATTR_EX_UNK19 0x00080000 // 19
+#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
+#define SPELL_ATTR_EX_UNK23 0x00800000 // 23
+#define SPELL_ATTR_EX_UNK24 0x01000000 // 24 Req fishing pole??
+#define SPELL_ATTR_EX_UNK25 0x02000000 // 25 not set in 2.4.2
+#define SPELL_ATTR_EX_UNK26 0x04000000 // 26
+#define SPELL_ATTR_EX_UNK27 0x08000000 // 27
+#define SPELL_ATTR_EX_UNK28 0x10000000 // 28
+#define SPELL_ATTR_EX_UNK29 0x20000000 // 29
+#define SPELL_ATTR_EX_UNK30 0x40000000 // 30 overpower
+#define SPELL_ATTR_EX_UNK31 0x80000000 // 31
+
+#define SPELL_ATTR_EX2_UNK0 0x00000001 // 0
+#define SPELL_ATTR_EX2_UNK1 0x00000002 // 1
+#define SPELL_ATTR_EX2_UNK2 0x00000004 // 2
+#define SPELL_ATTR_EX2_UNK3 0x00000008 // 3
+#define SPELL_ATTR_EX2_UNK4 0x00000010 // 4
+#define SPELL_ATTR_EX2_UNK5 0x00000020 // 5
+#define SPELL_ATTR_EX2_UNK6 0x00000040 // 6
+#define SPELL_ATTR_EX2_UNK7 0x00000080 // 7
+#define SPELL_ATTR_EX2_UNK8 0x00000100 // 8 not set in 2.4.2
+#define SPELL_ATTR_EX2_UNK9 0x00000200 // 9
+#define SPELL_ATTR_EX2_UNK10 0x00000400 // 10
+#define SPELL_ATTR_EX2_HEALTH_FUNNEL 0x00000800 // 11
+#define SPELL_ATTR_EX2_UNK12 0x00001000 // 12
+#define SPELL_ATTR_EX2_UNK13 0x00002000 // 13
+#define SPELL_ATTR_EX2_UNK14 0x00004000 // 14
+#define SPELL_ATTR_EX2_UNK15 0x00008000 // 15 not set in 2.4.2
+#define SPELL_ATTR_EX2_UNK16 0x00010000 // 16
+#define SPELL_ATTR_EX2_UNK17 0x00020000 // 17 Hunters Shot and Stings only have this flag
+#define SPELL_ATTR_EX2_UNK18 0x00040000 // 18 Only Revive pet - possible req dead pet
+#define SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT 0x00080000 // 19 does not necessarly need shapeshift
+#define SPELL_ATTR_EX2_UNK20 0x00100000 // 20
+#define SPELL_ATTR_EX2_UNK21 0x00200000 // 21
+#define SPELL_ATTR_EX2_UNK22 0x00400000 // 22
+#define SPELL_ATTR_EX2_UNK23 0x00800000 // 23 Only mage Arcane Concentration have this flag
+#define SPELL_ATTR_EX2_UNK24 0x01000000 // 24
+#define SPELL_ATTR_EX2_UNK25 0x02000000 // 25
+#define SPELL_ATTR_EX2_UNK26 0x04000000 // 26 unaffected by school immunity
+#define SPELL_ATTR_EX2_UNK27 0x08000000 // 27
+#define SPELL_ATTR_EX2_UNK28 0x10000000 // 28
+#define SPELL_ATTR_EX2_CANT_CRIT 0x20000000 // 29 Spell can't crit
+#define SPELL_ATTR_EX2_UNK30 0x40000000 // 30
+#define SPELL_ATTR_EX2_UNK31 0x80000000 // 31
+
+#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_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
+#define SPELL_ATTR_EX3_UNK8 0x00000100 // 8
+#define SPELL_ATTR_EX3_UNK9 0x00000200 // 9
+#define SPELL_ATTR_EX3_MAIN_HAND 0x00000400 // 10 Main hand weapon required
+#define SPELL_ATTR_EX3_BATTLEGROUND 0x00000800 // 11 Can casted only on battleground
+#define SPELL_ATTR_EX3_UNK12 0x00001000 // 12
+#define SPELL_ATTR_EX3_UNK13 0x00002000 // 13
+#define SPELL_ATTR_EX3_UNK14 0x00004000 // 14 "Honorless Target" only this spells have this flag
+#define SPELL_ATTR_EX3_UNK15 0x00008000 // 15 Auto Shoot, Shoot, Throw, - this is autoshot flag
+#define SPELL_ATTR_EX3_UNK16 0x00010000 // 16
+#define SPELL_ATTR_EX3_NO_INITIAL_AGGRO 0x00020000 // 17 no initial aggro
+#define SPELL_ATTR_EX3_UNK18 0x00040000 // 18
+#define SPELL_ATTR_EX3_UNK19 0x00080000 // 19
+#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
+#define SPELL_ATTR_EX3_UNK23 0x00800000 // 23
+#define SPELL_ATTR_EX3_REQ_OFFHAND 0x01000000 // 24 Req offhand weapon
+#define SPELL_ATTR_EX3_UNK25 0x02000000 // 25
+#define SPELL_ATTR_EX3_UNK26 0x04000000 // 26
+#define SPELL_ATTR_EX3_UNK27 0x08000000 // 27
+#define SPELL_ATTR_EX3_UNK28 0x10000000 // 28
+#define SPELL_ATTR_EX3_UNK29 0x20000000 // 29
+#define SPELL_ATTR_EX3_UNK30 0x40000000 // 30
+#define SPELL_ATTR_EX3_UNK31 0x80000000 // 31
+
+#define SPELL_ATTR_EX4_UNK0 0x00000001 // 0
+#define SPELL_ATTR_EX4_UNK1 0x00000002 // 1 proc on finishing move?
+#define SPELL_ATTR_EX4_UNK2 0x00000004 // 2
+#define SPELL_ATTR_EX4_UNK3 0x00000008 // 3
+#define SPELL_ATTR_EX4_UNK4 0x00000010 // 4
+#define SPELL_ATTR_EX4_UNK5 0x00000020 // 5
+#define SPELL_ATTR_EX4_UNK6 0x00000040 // 6
+#define SPELL_ATTR_EX4_UNK7 0x00000080 // 7
+#define SPELL_ATTR_EX4_UNK8 0x00000100 // 8
+#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
+#define SPELL_ATTR_EX4_UNK12 0x00001000 // 12
+#define SPELL_ATTR_EX4_UNK13 0x00002000 // 13
+#define SPELL_ATTR_EX4_UNK14 0x00004000 // 14
+#define SPELL_ATTR_EX4_UNK15 0x00008000 // 15
+#define SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA 0x00010000 // 16 not usable in arena
+#define SPELL_ATTR_EX4_USABLE_IN_ARENA 0x00020000 // 17 usable in arena
+#define SPELL_ATTR_EX4_UNK18 0x00040000 // 18
+#define SPELL_ATTR_EX4_UNK19 0x00080000 // 19
+#define SPELL_ATTR_EX4_UNK20 0x00100000 // 20
+#define SPELL_ATTR_EX4_UNK21 0x00200000 // 21
+#define SPELL_ATTR_EX4_UNK22 0x00400000 // 22
+#define SPELL_ATTR_EX4_UNK23 0x00800000 // 23
+#define SPELL_ATTR_EX4_UNK24 0x01000000 // 24
+#define SPELL_ATTR_EX4_UNK25 0x02000000 // 25 pet scaling auras
+#define SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND 0x04000000 // 26 Can only be used in Outland.
+#define SPELL_ATTR_EX4_UNK27 0x08000000 // 27
+#define SPELL_ATTR_EX4_UNK28 0x10000000 // 28
+#define SPELL_ATTR_EX4_UNK29 0x20000000 // 29
+#define SPELL_ATTR_EX4_UNK30 0x40000000 // 30
+#define SPELL_ATTR_EX4_UNK31 0x80000000 // 31
+
+#define SPELL_ATTR_EX5_UNK0 0x00000001 // 0
+#define SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP 0x00000002 // 1 not need reagents if UNIT_FLAG_PREPARATION
+#define SPELL_ATTR_EX5_UNK2 0x00000004 // 2
+#define SPELL_ATTR_EX5_USABLE_WHILE_STUNNED 0x00000008 // 3 usable while stunned
+#define SPELL_ATTR_EX5_UNK4 0x00000010 // 4
+#define SPELL_ATTR_EX5_SINGLE_TARGET_SPELL 0x00000020 // 5 Only one target can be apply at a time
+#define SPELL_ATTR_EX5_UNK6 0x00000040 // 6
+#define SPELL_ATTR_EX5_UNK7 0x00000080 // 7
+#define SPELL_ATTR_EX5_UNK8 0x00000100 // 8
+#define SPELL_ATTR_EX5_UNK9 0x00000200 // 9
+#define SPELL_ATTR_EX5_UNK10 0x00000400 // 10
+#define SPELL_ATTR_EX5_UNK11 0x00000800 // 11
+#define SPELL_ATTR_EX5_UNK12 0x00001000 // 12
+#define SPELL_ATTR_EX5_UNK13 0x00002000 // 13
+#define SPELL_ATTR_EX5_UNK14 0x00004000 // 14
+#define SPELL_ATTR_EX5_UNK15 0x00008000 // 15
+#define SPELL_ATTR_EX5_UNK16 0x00010000 // 16
+#define SPELL_ATTR_EX5_USABLE_WHILE_FEARED 0x00020000 // 17 usable while feared
+#define SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED 0x00040000 // 18 usable while confused
+#define SPELL_ATTR_EX5_UNK19 0x00080000 // 19
+#define SPELL_ATTR_EX5_UNK20 0x00100000 // 20
+#define SPELL_ATTR_EX5_UNK21 0x00200000 // 21
+#define SPELL_ATTR_EX5_UNK22 0x00400000 // 22
+#define SPELL_ATTR_EX5_UNK23 0x00800000 // 23
+#define SPELL_ATTR_EX5_UNK24 0x01000000 // 24
+#define SPELL_ATTR_EX5_UNK25 0x02000000 // 25
+#define SPELL_ATTR_EX5_UNK26 0x04000000 // 26
+#define SPELL_ATTR_EX5_UNK27 0x08000000 // 27
+#define SPELL_ATTR_EX5_UNK28 0x10000000 // 28
+#define SPELL_ATTR_EX5_UNK29 0x20000000 // 29
+#define SPELL_ATTR_EX5_UNK30 0x40000000 // 30
+#define SPELL_ATTR_EX5_UNK31 0x80000000 // 31 Forces all nearby enemies to focus attacks caster
+
+#define SPELL_ATTR_EX6_UNK0 0x00000001 // 0 Only Move spell have this flag
+#define SPELL_ATTR_EX6_UNK1 0x00000002 // 1 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK2 0x00000004 // 2
+#define SPELL_ATTR_EX6_UNK3 0x00000008 // 3
+#define SPELL_ATTR_EX6_UNK4 0x00000010 // 4 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK5 0x00000020 // 5
+#define SPELL_ATTR_EX6_UNK6 0x00000040 // 6
+#define SPELL_ATTR_EX6_UNK7 0x00000080 // 7
+#define SPELL_ATTR_EX6_UNK8 0x00000100 // 8
+#define SPELL_ATTR_EX6_UNK9 0x00000200 // 9 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK10 0x00000400 // 10
+#define SPELL_ATTR_EX6_UNK11 0x00000800 // 11
+#define SPELL_ATTR_EX6_UNK12 0x00001000 // 12 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK13 0x00002000 // 13 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK14 0x00004000 // 14 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK15 0x00008000 // 15 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK16 0x00010000 // 16 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK17 0x00020000 // 17 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK18 0x00040000 // 18 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK19 0x00080000 // 19 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK20 0x00100000 // 20 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK21 0x00200000 // 21 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK22 0x00400000 // 22 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK23 0x00800000 // 23 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK24 0x01000000 // 24 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK25 0x02000000 // 25 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK26 0x04000000 // 26 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK27 0x08000000 // 27 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK28 0x10000000 // 28 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK29 0x20000000 // 29 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 2.4.2
+#define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 2.4.2
+
+enum SheathTypes
+{
+ SHEATHETYPE_NONE = 0,
+ SHEATHETYPE_MAINHAND = 1,
+ SHEATHETYPE_OFFHAND = 2,
+ SHEATHETYPE_LARGEWEAPONLEFT = 3,
+ SHEATHETYPE_LARGEWEAPONRIGHT = 4,
+ SHEATHETYPE_HIPWEAPONLEFT = 5,
+ SHEATHETYPE_HIPWEAPONRIGHT = 6,
+ SHEATHETYPE_SHIELD = 7
+};
+
+#define MAX_SHEATHETYPE 8
+
+enum CharacterSlot
+{
+ SLOT_HEAD = 0,
+ SLOT_NECK = 1,
+ SLOT_SHOULDERS = 2,
+ SLOT_SHIRT = 3,
+ SLOT_CHEST = 4,
+ SLOT_WAIST = 5,
+ SLOT_LEGS = 6,
+ SLOT_FEET = 7,
+ SLOT_WRISTS = 8,
+ SLOT_HANDS = 9,
+ SLOT_FINGER1 = 10,
+ SLOT_FINGER2 = 11,
+ SLOT_TRINKET1 = 12,
+ SLOT_TRINKET2 = 13,
+ SLOT_BACK = 14,
+ SLOT_MAIN_HAND = 15,
+ SLOT_OFF_HAND = 16,
+ SLOT_RANGED = 17,
+ SLOT_TABARD = 18,
+ SLOT_EMPTY = 19
+};
+
+enum Language
+{
+ LANG_UNIVERSAL = 0,
+ LANG_ORCISH = 1,
+ LANG_DARNASSIAN = 2,
+ LANG_TAURAHE = 3,
+ LANG_DWARVISH = 6,
+ LANG_COMMON = 7,
+ LANG_DEMONIC = 8,
+ LANG_TITAN = 9,
+ LANG_THALASSIAN = 10,
+ LANG_DRACONIC = 11,
+ LANG_KALIMAG = 12,
+ LANG_GNOMISH = 13,
+ LANG_TROLL = 14,
+ LANG_GUTTERSPEAK = 33,
+ LANG_DRAENEI = 35,
+ LANG_ZOMBIE = 36,
+ LANG_GNOMISH_BINARY = 37,
+ LANG_GOBLIN_BINARY = 38,
+ LANG_ADDON = 0xFFFFFFFF // used by addons, in 2.4.0 not exit, replaced by messagetype?
+};
+
+#define LANGUAGES_COUNT 19
+
+enum Team
+{
+ HORDE = 67,
+ ALLIANCE = 469,
+ //TEAM_STEAMWHEEDLE_CARTEL = 169, // not used in code
+ //TEAM_ALLIANCE_FORCES = 891,
+ //TEAM_HORDE_FORCES = 892,
+ //TEAM_SANCTUARY = 936,
+ //TEAM_OUTLAND = 980,
+ //TEAM_OTHER = 0, // if ReputationListId > 0 && Flags != FACTION_FLAG_TEAM_HEADER
+};
+
+enum SpellEffects
+{
+ SPELL_EFFECT_INSTAKILL = 1,
+ SPELL_EFFECT_SCHOOL_DAMAGE = 2,
+ SPELL_EFFECT_DUMMY = 3,
+ SPELL_EFFECT_PORTAL_TELEPORT = 4,
+ SPELL_EFFECT_TELEPORT_UNITS = 5,
+ SPELL_EFFECT_APPLY_AURA = 6,
+ SPELL_EFFECT_ENVIRONMENTAL_DAMAGE = 7,
+ SPELL_EFFECT_POWER_DRAIN = 8,
+ SPELL_EFFECT_HEALTH_LEECH = 9,
+ SPELL_EFFECT_HEAL = 10,
+ SPELL_EFFECT_BIND = 11,
+ SPELL_EFFECT_PORTAL = 12,
+ SPELL_EFFECT_RITUAL_BASE = 13,
+ SPELL_EFFECT_RITUAL_SPECIALIZE = 14,
+ SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL = 15,
+ SPELL_EFFECT_QUEST_COMPLETE = 16,
+ SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL = 17,
+ SPELL_EFFECT_RESURRECT = 18,
+ SPELL_EFFECT_ADD_EXTRA_ATTACKS = 19,
+ SPELL_EFFECT_DODGE = 20,
+ SPELL_EFFECT_EVADE = 21,
+ SPELL_EFFECT_PARRY = 22,
+ SPELL_EFFECT_BLOCK = 23,
+ SPELL_EFFECT_CREATE_ITEM = 24,
+ SPELL_EFFECT_WEAPON = 25,
+ SPELL_EFFECT_DEFENSE = 26,
+ SPELL_EFFECT_PERSISTENT_AREA_AURA = 27,
+ SPELL_EFFECT_SUMMON = 28,
+ SPELL_EFFECT_LEAP = 29,
+ SPELL_EFFECT_ENERGIZE = 30,
+ SPELL_EFFECT_WEAPON_PERCENT_DAMAGE = 31,
+ SPELL_EFFECT_TRIGGER_MISSILE = 32,
+ SPELL_EFFECT_OPEN_LOCK = 33,
+ SPELL_EFFECT_SUMMON_CHANGE_ITEM = 34,
+ SPELL_EFFECT_APPLY_AREA_AURA_PARTY = 35,
+ SPELL_EFFECT_LEARN_SPELL = 36,
+ SPELL_EFFECT_SPELL_DEFENSE = 37,
+ SPELL_EFFECT_DISPEL = 38,
+ SPELL_EFFECT_LANGUAGE = 39,
+ SPELL_EFFECT_DUAL_WIELD = 40,
+ SPELL_EFFECT_SUMMON_WILD = 41,
+ SPELL_EFFECT_SUMMON_GUARDIAN = 42,
+ SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER= 43,
+ SPELL_EFFECT_SKILL_STEP = 44,
+ SPELL_EFFECT_UNDEFINED_45 = 45,
+ SPELL_EFFECT_SPAWN = 46,
+ SPELL_EFFECT_TRADE_SKILL = 47,
+ SPELL_EFFECT_STEALTH = 48,
+ SPELL_EFFECT_DETECT = 49,
+ // SPELL_EFFECT_SUMMON_OBJECT = 50,
+ SPELL_EFFECT_TRANS_DOOR = 50,
+ SPELL_EFFECT_FORCE_CRITICAL_HIT = 51,
+ SPELL_EFFECT_GUARANTEE_HIT = 52,
+ SPELL_EFFECT_ENCHANT_ITEM = 53,
+ SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY = 54,
+ SPELL_EFFECT_TAMECREATURE = 55,
+ SPELL_EFFECT_SUMMON_PET = 56,
+ SPELL_EFFECT_LEARN_PET_SPELL = 57,
+ SPELL_EFFECT_WEAPON_DAMAGE = 58,
+ SPELL_EFFECT_OPEN_LOCK_ITEM = 59,
+ SPELL_EFFECT_PROFICIENCY = 60,
+ SPELL_EFFECT_SEND_EVENT = 61,
+ SPELL_EFFECT_POWER_BURN = 62,
+ SPELL_EFFECT_THREAT = 63,
+ SPELL_EFFECT_TRIGGER_SPELL = 64,
+ SPELL_EFFECT_HEALTH_FUNNEL = 65,
+ SPELL_EFFECT_POWER_FUNNEL = 66,
+ SPELL_EFFECT_HEAL_MAX_HEALTH = 67,
+ SPELL_EFFECT_INTERRUPT_CAST = 68,
+ SPELL_EFFECT_DISTRACT = 69,
+ SPELL_EFFECT_PULL = 70,
+ SPELL_EFFECT_PICKPOCKET = 71,
+ SPELL_EFFECT_ADD_FARSIGHT = 72,
+ SPELL_EFFECT_SUMMON_POSSESSED = 73,
+ SPELL_EFFECT_SUMMON_TOTEM = 74,
+ SPELL_EFFECT_HEAL_MECHANICAL = 75,
+ SPELL_EFFECT_SUMMON_OBJECT_WILD = 76,
+ SPELL_EFFECT_SCRIPT_EFFECT = 77,
+ SPELL_EFFECT_ATTACK = 78,
+ SPELL_EFFECT_SANCTUARY = 79,
+ SPELL_EFFECT_ADD_COMBO_POINTS = 80,
+ SPELL_EFFECT_CREATE_HOUSE = 81,
+ SPELL_EFFECT_BIND_SIGHT = 82,
+ SPELL_EFFECT_DUEL = 83,
+ SPELL_EFFECT_STUCK = 84,
+ SPELL_EFFECT_SUMMON_PLAYER = 85,
+ SPELL_EFFECT_ACTIVATE_OBJECT = 86,
+ SPELL_EFFECT_SUMMON_TOTEM_SLOT1 = 87,
+ SPELL_EFFECT_SUMMON_TOTEM_SLOT2 = 88,
+ SPELL_EFFECT_SUMMON_TOTEM_SLOT3 = 89,
+ SPELL_EFFECT_SUMMON_TOTEM_SLOT4 = 90,
+ SPELL_EFFECT_THREAT_ALL = 91,
+ SPELL_EFFECT_ENCHANT_HELD_ITEM = 92,
+ SPELL_EFFECT_SUMMON_PHANTASM = 93,
+ SPELL_EFFECT_SELF_RESURRECT = 94,
+ SPELL_EFFECT_SKINNING = 95,
+ SPELL_EFFECT_CHARGE = 96,
+ SPELL_EFFECT_SUMMON_CRITTER = 97,
+ SPELL_EFFECT_KNOCK_BACK = 98,
+ SPELL_EFFECT_DISENCHANT = 99,
+ SPELL_EFFECT_INEBRIATE = 100,
+ SPELL_EFFECT_FEED_PET = 101,
+ SPELL_EFFECT_DISMISS_PET = 102,
+ SPELL_EFFECT_REPUTATION = 103,
+ SPELL_EFFECT_SUMMON_OBJECT_SLOT1 = 104,
+ SPELL_EFFECT_SUMMON_OBJECT_SLOT2 = 105,
+ SPELL_EFFECT_SUMMON_OBJECT_SLOT3 = 106,
+ SPELL_EFFECT_SUMMON_OBJECT_SLOT4 = 107,
+ SPELL_EFFECT_DISPEL_MECHANIC = 108,
+ SPELL_EFFECT_SUMMON_DEAD_PET = 109,
+ SPELL_EFFECT_DESTROY_ALL_TOTEMS = 110,
+ SPELL_EFFECT_DURABILITY_DAMAGE = 111,
+ SPELL_EFFECT_SUMMON_DEMON = 112,
+ SPELL_EFFECT_RESURRECT_NEW = 113,
+ SPELL_EFFECT_ATTACK_ME = 114,
+ SPELL_EFFECT_DURABILITY_DAMAGE_PCT = 115,
+ SPELL_EFFECT_SKIN_PLAYER_CORPSE = 116,
+ SPELL_EFFECT_SPIRIT_HEAL = 117,
+ SPELL_EFFECT_SKILL = 118,
+ SPELL_EFFECT_APPLY_AREA_AURA_PET = 119,
+ SPELL_EFFECT_TELEPORT_GRAVEYARD = 120,
+ SPELL_EFFECT_NORMALIZED_WEAPON_DMG = 121,
+ SPELL_EFFECT_122 = 122,
+ SPELL_EFFECT_SEND_TAXI = 123,
+ SPELL_EFFECT_PLAYER_PULL = 124,
+ SPELL_EFFECT_MODIFY_THREAT_PERCENT = 125,
+ SPELL_EFFECT_STEAL_BENEFICIAL_BUFF = 126,
+ SPELL_EFFECT_PROSPECTING = 127,
+ SPELL_EFFECT_APPLY_AREA_AURA_FRIEND = 128,
+ SPELL_EFFECT_APPLY_AREA_AURA_ENEMY = 129,
+ SPELL_EFFECT_REDIRECT_THREAT = 130,
+ SPELL_EFFECT_131 = 131,
+ SPELL_EFFECT_132 = 132,
+ SPELL_EFFECT_UNLEARN_SPECIALIZATION = 133,
+ SPELL_EFFECT_KILL_CREDIT = 134,
+ SPELL_EFFECT_135 = 135,
+ SPELL_EFFECT_HEAL_PCT = 136,
+ SPELL_EFFECT_ENERGIZE_PCT = 137,
+ SPELL_EFFECT_138 = 138,
+ SPELL_EFFECT_139 = 139,
+ SPELL_EFFECT_FORCE_CAST = 140,
+ SPELL_EFFECT_141 = 141,
+ SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE = 142,
+ SPELL_EFFECT_APPLY_AREA_AURA_OWNER = 143,
+ SPELL_EFFECT_144 = 144,
+ SPELL_EFFECT_145 = 145,
+ SPELL_EFFECT_146 = 146,
+ SPELL_EFFECT_QUEST_FAIL = 147,
+ SPELL_EFFECT_148 = 148,
+ SPELL_EFFECT_149 = 149,
+ SPELL_EFFECT_150 = 150,
+ SPELL_EFFECT_TRIGGER_SPELL_2 = 151,
+ SPELL_EFFECT_152 = 152,
+ SPELL_EFFECT_153 = 153,
+ TOTAL_SPELL_EFFECTS = 154
+};
+
+// 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_DEFENSE = 1, // C |
+ AURA_STATE_HEALTHLESS_20_PERCENT = 2, // CcT |
+ AURA_STATE_BERSERKING = 3, // C T |
+ //AURA_STATE_UNKNOWN4 = 4, // c t| some limitation to charge spells (?) and target test spells
+ AURA_STATE_JUDGEMENT = 5, // C |
+ //AURA_STATE_UNKNOWN6 = 6, // | not used
+ AURA_STATE_HUNTER_PARRY = 7, // C |
+ AURA_STATE_ROGUE_ATTACK_FROM_STEALTH = 7, // C | FIX ME: not implemented yet!
+ //AURA_STATE_UNKNOWN7c = 7, // c | random/focused bursts spells (?)
+ //AURA_STATE_UNKNOWN8 = 8, // | not used
+ //AURA_STATE_UNKNOWN9 = 9, // | not used
+ AURA_STATE_WARRIOR_VICTORY_RUSH = 10, // C | warrior victory rush
+ AURA_STATE_HUNTER_CRIT_STRIKE = 10, // C | hunter crit strike
+ AURA_STATE_CRIT = 11, // C |
+ AURA_STATE_FAERIE_FIRE = 12, // c t|
+ AURA_STATE_HEALTHLESS_35_PERCENT = 13, // C T |
+ AURA_STATE_IMMOLATE = 14, // T |
+ AURA_STATE_SWIFTMEND = 15, // T |
+ AURA_STATE_DEADLY_POISON = 16, // T |
+ AURA_STATE_FORBEARANCE = 17, // c t|
+ AURA_STATE_WEAKENED_SOUL = 18, // t|
+ AURA_STATE_HYPOTHERMIA = 19 // c |
+};
+
+// Spell mechanics
+enum Mechanics
+{
+ MECHANIC_NONE = 0,
+ MECHANIC_CHARM = 1,
+ MECHANIC_CONFUSED = 2,
+ MECHANIC_DISARM = 3,
+ MECHANIC_DISTRACT = 4,
+ MECHANIC_FEAR = 5,
+ MECHANIC_FUMBLE = 6,
+ MECHANIC_ROOT = 7,
+ MECHANIC_PACIFY = 8, //0 spells use this mechanic
+ MECHANIC_SILENCE = 9,
+ MECHANIC_SLEEP = 10,
+ MECHANIC_SNARE = 11,
+ MECHANIC_STUN = 12,
+ MECHANIC_FREEZE = 13,
+ MECHANIC_KNOCKOUT = 14,
+ MECHANIC_BLEED = 15,
+ MECHANIC_BANDAGE = 16,
+ MECHANIC_POLYMORPH = 17,
+ MECHANIC_BANISH = 18,
+ MECHANIC_SHIELD = 19,
+ MECHANIC_SHACKLE = 20,
+ MECHANIC_MOUNT = 21,
+ MECHANIC_PERSUADE = 22, //0 spells use this mechanic
+ MECHANIC_TURN = 23,
+ MECHANIC_HORROR = 24,
+ MECHANIC_INVULNERABILITY = 25,
+ MECHANIC_INTERRUPT = 26,
+ MECHANIC_DAZE = 27,
+ MECHANIC_DISCOVERY = 28,
+ MECHANIC_IMMUNE_SHIELD = 29, // Divine (Blessing) Shield/Protection and Ice Block
+ MECHANIC_SAPPED = 30
+};
+
+// Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967da6)
+#define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK ( \
+ (1<<MECHANIC_CHARM )|(1<<MECHANIC_CONFUSED )|(1<<MECHANIC_FEAR )| \
+ (1<<MECHANIC_ROOT )|(1<<MECHANIC_PACIFY )|(1<<MECHANIC_SLEEP )| \
+ (1<<MECHANIC_SNARE )|(1<<MECHANIC_STUN )|(1<<MECHANIC_FREEZE)| \
+ (1<<MECHANIC_KNOCKOUT)|(1<<MECHANIC_POLYMORPH)|(1<<MECHANIC_BANISH)| \
+ (1<<MECHANIC_SHACKLE )|(1<<MECHANIC_TURN )|(1<<MECHANIC_HORROR)| \
+ (1<<MECHANIC_DAZE )|(1<<MECHANIC_SAPPED ) )
+
+// Spell dispell type
+enum DispelType
+{
+ DISPEL_NONE = 0,
+ DISPEL_MAGIC = 1,
+ DISPEL_CURSE = 2,
+ DISPEL_DISEASE = 3,
+ DISPEL_POISON = 4,
+ DISPEL_STEALTH = 5,
+ DISPEL_INVISIBILITY = 6,
+ DISPEL_ALL = 7,
+ DISPEL_SPE_NPC_ONLY = 8,
+ DISPEL_ENRAGE = 9,
+ DISPEL_ZG_TICKET = 10
+};
+
+#define DISPEL_ALL_MASK ( (1<<DISPEL_MAGIC) | (1<<DISPEL_CURSE) | (1<<DISPEL_DISEASE) | (1<<DISPEL_POISON) )
+
+//To all Immune system,if target has immunes,
+//some spell that related to ImmuneToDispel or ImmuneToSchool or ImmuneToDamage type can't cast to it,
+//some spell_effects that related to ImmuneToEffect<effect>(only this effect in the spell) can't cast to it,
+//some aura(related to Mechanics or ImmuneToState<aura>) can't apply to it.
+enum SpellImmunity
+{
+ IMMUNITY_EFFECT = 0, // enum SpellEffects
+ IMMUNITY_STATE = 1, // enum AuraType
+ IMMUNITY_SCHOOL = 2, // enum SpellSchoolMask
+ IMMUNITY_DAMAGE = 3, // enum SpellSchoolMask
+ IMMUNITY_DISPEL = 4, // enum DispelType
+ IMMUNITY_MECHANIC = 5 // enum Mechanics
+};
+
+#define MAX_SPELL_IMMUNITY 6
+
+enum Targets
+{
+ TARGET_SELF = 1,
+ TARGET_RANDOM_ENEMY_CHAIN_IN_AREA = 2, // only one spell has that, but regardless, it's a target type after all
+ TARGET_PET = 5,
+ TARGET_CHAIN_DAMAGE = 6,
+ TARGET_AREAEFFECT_CUSTOM = 8,
+ TARGET_INNKEEPER_COORDINATES = 9, // uses in teleport to innkeeper spells
+ TARGET_ALL_ENEMY_IN_AREA = 15,
+ TARGET_ALL_ENEMY_IN_AREA_INSTANT = 16,
+ TARGET_TABLE_X_Y_Z_COORDINATES = 17, // uses in teleport spells and some other
+ TARGET_EFFECT_SELECT = 18, // highly depends on the spell effect
+ TARGET_ALL_PARTY_AROUND_CASTER = 20,
+ TARGET_SINGLE_FRIEND = 21,
+ TARGET_ALL_AROUND_CASTER = 22, // used only in TargetA, target selection dependent from TargetB
+ TARGET_GAMEOBJECT = 23,
+ TARGET_IN_FRONT_OF_CASTER = 24,
+ TARGET_DUELVSPLAYER = 25,
+ TARGET_GAMEOBJECT_ITEM = 26,
+ TARGET_MASTER = 27,
+ TARGET_ALL_ENEMY_IN_AREA_CHANNELED = 28,
+ TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER = 30, // in TargetB used only with TARGET_ALL_AROUND_CASTER and in self casting range in TargetA
+ TARGET_ALL_FRIENDLY_UNITS_IN_AREA = 31,
+ TARGET_MINION = 32,
+ TARGET_ALL_PARTY = 33,
+ TARGET_ALL_PARTY_AROUND_CASTER_2 = 34, // used in Tranquility
+ TARGET_SINGLE_PARTY = 35,
+ TARGET_AREAEFFECT_PARTY = 37,
+ TARGET_SCRIPT = 38,
+ TARGET_SELF_FISHING = 39,
+ TARGET_TOTEM_EARTH = 41,
+ TARGET_TOTEM_WATER = 42,
+ TARGET_TOTEM_AIR = 43,
+ TARGET_TOTEM_FIRE = 44,
+ TARGET_CHAIN_HEAL = 45,
+ TARGET_SCRIPT_COORDINATES = 46,
+ TARGET_DYNAMIC_OBJECT = 47,
+ TARGET_SUMMON = 48,
+ TARGET_AREAEFFECT_CUSTOM_2 = 52,
+ TARGET_CURRENT_ENEMY_COORDINATES = 53, // set unit coordinates as dest, only 16 target B imlemented
+ TARGET_RANDOM_RAID_MEMBER = 56,
+ TARGET_SINGLE_FRIEND_2 = 57,
+ TARGET_AREAEFFECT_PARTY_AND_CLASS = 61,
+ TARGET_DUELVSPLAYER_COORDINATES = 63,
+ TARGET_BEHIND_VICTIM = 65, // uses in teleport behind spells
+ TARGET_SINGLE_ENEMY = 77,
+ TARGET_SELF2 = 87,
+ TARGET_NONCOMBAT_PET = 90,
+};
+
+enum SpellMissInfo
+{
+ SPELL_MISS_NONE = 0,
+ SPELL_MISS_MISS = 1,
+ SPELL_MISS_RESIST = 2,
+ SPELL_MISS_DODGE = 3,
+ SPELL_MISS_PARRY = 4,
+ SPELL_MISS_BLOCK = 5,
+ SPELL_MISS_EVADE = 6,
+ SPELL_MISS_IMMUNE = 7,
+ SPELL_MISS_IMMUNE2 = 8,
+ SPELL_MISS_DEFLECT = 9,
+ SPELL_MISS_ABSORB = 10,
+ SPELL_MISS_REFLECT = 11,
+};
+
+enum SpellHitType
+{
+ SPELL_HIT_TYPE_UNK1 = 0x00001,
+ SPELL_HIT_TYPE_CRIT = 0x00002,
+ SPELL_HIT_TYPE_UNK2 = 0x00004,
+ SPELL_HIT_TYPE_UNK3 = 0x00008,
+ SPELL_HIT_TYPE_UNK4 = 0x00020
+};
+
+enum SpellDmgClass
+{
+ SPELL_DAMAGE_CLASS_NONE = 0,
+ SPELL_DAMAGE_CLASS_MAGIC = 1,
+ SPELL_DAMAGE_CLASS_MELEE = 2,
+ SPELL_DAMAGE_CLASS_RANGED = 3
+};
+
+enum SpellPreventionType
+{
+ SPELL_PREVENTION_TYPE_NONE = 0,
+ SPELL_PREVENTION_TYPE_SILENCE = 1,
+ SPELL_PREVENTION_TYPE_PACIFY = 2
+};
+
+enum GameobjectTypes
+{
+ GAMEOBJECT_TYPE_DOOR = 0,
+ GAMEOBJECT_TYPE_BUTTON = 1,
+ GAMEOBJECT_TYPE_QUESTGIVER = 2,
+ GAMEOBJECT_TYPE_CHEST = 3,
+ GAMEOBJECT_TYPE_BINDER = 4,
+ GAMEOBJECT_TYPE_GENERIC = 5,
+ GAMEOBJECT_TYPE_TRAP = 6,
+ GAMEOBJECT_TYPE_CHAIR = 7,
+ GAMEOBJECT_TYPE_SPELL_FOCUS = 8,
+ GAMEOBJECT_TYPE_TEXT = 9,
+ GAMEOBJECT_TYPE_GOOBER = 10,
+ GAMEOBJECT_TYPE_TRANSPORT = 11,
+ GAMEOBJECT_TYPE_AREADAMAGE = 12,
+ GAMEOBJECT_TYPE_CAMERA = 13,
+ GAMEOBJECT_TYPE_MAP_OBJECT = 14,
+ GAMEOBJECT_TYPE_MO_TRANSPORT = 15,
+ GAMEOBJECT_TYPE_DUEL_ARBITER = 16,
+ GAMEOBJECT_TYPE_FISHINGNODE = 17,
+ GAMEOBJECT_TYPE_SUMMONING_RITUAL = 18,
+ GAMEOBJECT_TYPE_MAILBOX = 19,
+ GAMEOBJECT_TYPE_AUCTIONHOUSE = 20,
+ GAMEOBJECT_TYPE_GUARDPOST = 21,
+ GAMEOBJECT_TYPE_SPELLCASTER = 22,
+ GAMEOBJECT_TYPE_MEETINGSTONE = 23,
+ GAMEOBJECT_TYPE_FLAGSTAND = 24,
+ GAMEOBJECT_TYPE_FISHINGHOLE = 25,
+ GAMEOBJECT_TYPE_FLAGDROP = 26,
+ GAMEOBJECT_TYPE_MINI_GAME = 27,
+ GAMEOBJECT_TYPE_LOTTERY_KIOSK = 28,
+ GAMEOBJECT_TYPE_CAPTURE_POINT = 29,
+ GAMEOBJECT_TYPE_AURA_GENERATOR = 30,
+ GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY = 31,
+ GAMEOBJECT_TYPE_DO_NOT_USE_YET = 32,
+ GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING = 33,
+ GAMEOBJECT_TYPE_GUILD_BANK = 34,
+};
+
+#define MAX_GAMEOBJECT_TYPE 35 // sending to client this or greater value can crash client.
+
+#define GAMEOBJECT_FISHINGNODE_ENTRY 35591 // Better to define it somewhere instead of hardcoding everywhere
+
+enum GameObjectFlags
+{
+ GO_FLAG_IN_USE = 0x01, //disables interaction while animated
+ GO_FLAG_LOCKED = 0x02, //require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip
+ GO_FLAG_INTERACT_COND = 0x04, //cannot interact (condition to interact)
+ GO_FLAG_TRANSPORT = 0x08, //any kind of transport? Object can transport (elevator, boat, car)
+ GO_FLAG_UNK1 = 0x10, //
+ GO_FLAG_NODESPAWN = 0x20, //never despawn, typically for doors, they just change state
+ GO_FLAG_TRIGGERED = 0x40, //typically, summoned objects. Triggered by spell or other events
+};
+
+enum TextEmotes
+{
+ TEXTEMOTE_AGREE = 1,
+ TEXTEMOTE_AMAZE = 2,
+ TEXTEMOTE_ANGRY = 3,
+ TEXTEMOTE_APOLOGIZE = 4,
+ TEXTEMOTE_APPLAUD = 5,
+ TEXTEMOTE_BASHFUL = 6,
+ TEXTEMOTE_BECKON = 7,
+ TEXTEMOTE_BEG = 8,
+ TEXTEMOTE_BITE = 9,
+ TEXTEMOTE_BLEED = 10,
+ TEXTEMOTE_BLINK = 11,
+ TEXTEMOTE_BLUSH = 12,
+ TEXTEMOTE_BONK = 13,
+ TEXTEMOTE_BORED = 14,
+ TEXTEMOTE_BOUNCE = 15,
+ TEXTEMOTE_BRB = 16,
+ TEXTEMOTE_BOW = 17,
+ TEXTEMOTE_BURP = 18,
+ TEXTEMOTE_BYE = 19,
+ TEXTEMOTE_CACKLE = 20,
+ TEXTEMOTE_CHEER = 21,
+ TEXTEMOTE_CHICKEN = 22,
+ TEXTEMOTE_CHUCKLE = 23,
+ TEXTEMOTE_CLAP = 24,
+ TEXTEMOTE_CONFUSED = 25,
+ TEXTEMOTE_CONGRATULATE = 26,
+ TEXTEMOTE_COUGH = 27,
+ TEXTEMOTE_COWER = 28,
+ TEXTEMOTE_CRACK = 29,
+ TEXTEMOTE_CRINGE = 30,
+ TEXTEMOTE_CRY = 31,
+ TEXTEMOTE_CURIOUS = 32,
+ TEXTEMOTE_CURTSEY = 33,
+ TEXTEMOTE_DANCE = 34,
+ TEXTEMOTE_DRINK = 35,
+ TEXTEMOTE_DROOL = 36,
+ TEXTEMOTE_EAT = 37,
+ TEXTEMOTE_EYE = 38,
+ TEXTEMOTE_FART = 39,
+ TEXTEMOTE_FIDGET = 40,
+ TEXTEMOTE_FLEX = 41,
+ TEXTEMOTE_FROWN = 42,
+ TEXTEMOTE_GASP = 43,
+ TEXTEMOTE_GAZE = 44,
+ TEXTEMOTE_GIGGLE = 45,
+ TEXTEMOTE_GLARE = 46,
+ TEXTEMOTE_GLOAT = 47,
+ TEXTEMOTE_GREET = 48,
+ TEXTEMOTE_GRIN = 49,
+ TEXTEMOTE_GROAN = 50,
+ TEXTEMOTE_GROVEL = 51,
+ TEXTEMOTE_GUFFAW = 52,
+ TEXTEMOTE_HAIL = 53,
+ TEXTEMOTE_HAPPY = 54,
+ TEXTEMOTE_HELLO = 55,
+ TEXTEMOTE_HUG = 56,
+ TEXTEMOTE_HUNGRY = 57,
+ TEXTEMOTE_KISS = 58,
+ TEXTEMOTE_KNEEL = 59,
+ TEXTEMOTE_LAUGH = 60,
+ TEXTEMOTE_LAYDOWN = 61,
+ TEXTEMOTE_MESSAGE = 62,
+ TEXTEMOTE_MOAN = 63,
+ TEXTEMOTE_MOON = 64,
+ TEXTEMOTE_MOURN = 65,
+ TEXTEMOTE_NO = 66,
+ TEXTEMOTE_NOD = 67,
+ TEXTEMOTE_NOSEPICK = 68,
+ TEXTEMOTE_PANIC = 69,
+ TEXTEMOTE_PEER = 70,
+ TEXTEMOTE_PLEAD = 71,
+ TEXTEMOTE_POINT = 72,
+ TEXTEMOTE_POKE = 73,
+ TEXTEMOTE_PRAY = 74,
+ TEXTEMOTE_ROAR = 75,
+ TEXTEMOTE_ROFL = 76,
+ TEXTEMOTE_RUDE = 77,
+ TEXTEMOTE_SALUTE = 78,
+ TEXTEMOTE_SCRATCH = 79,
+ TEXTEMOTE_SEXY = 80,
+ TEXTEMOTE_SHAKE = 81,
+ TEXTEMOTE_SHOUT = 82,
+ TEXTEMOTE_SHRUG = 83,
+ TEXTEMOTE_SHY = 84,
+ TEXTEMOTE_SIGH = 85,
+ TEXTEMOTE_SIT = 86,
+ TEXTEMOTE_SLEEP = 87,
+ TEXTEMOTE_SNARL = 88,
+ TEXTEMOTE_SPIT = 89,
+ TEXTEMOTE_STARE = 90,
+ TEXTEMOTE_SURPRISED = 91,
+ TEXTEMOTE_SURRENDER = 92,
+ TEXTEMOTE_TALK = 93,
+ TEXTEMOTE_TALKEX = 94,
+ TEXTEMOTE_TALKQ = 95,
+ TEXTEMOTE_TAP = 96,
+ TEXTEMOTE_THANK = 97,
+ TEXTEMOTE_THREATEN = 98,
+ TEXTEMOTE_TIRED = 99,
+ TEXTEMOTE_VICTORY = 100,
+ TEXTEMOTE_WAVE = 101,
+ TEXTEMOTE_WELCOME = 102,
+ TEXTEMOTE_WHINE = 103,
+ TEXTEMOTE_WHISTLE = 104,
+ TEXTEMOTE_WORK = 105,
+ TEXTEMOTE_YAWN = 106,
+ TEXTEMOTE_BOGGLE = 107,
+ TEXTEMOTE_CALM = 108,
+ TEXTEMOTE_COLD = 109,
+ TEXTEMOTE_COMFORT = 110,
+ TEXTEMOTE_CUDDLE = 111,
+ TEXTEMOTE_DUCK = 112,
+ TEXTEMOTE_INSULT = 113,
+ TEXTEMOTE_INTRODUCE = 114,
+ TEXTEMOTE_JK = 115,
+ TEXTEMOTE_LICK = 116,
+ TEXTEMOTE_LISTEN = 117,
+ TEXTEMOTE_LOST = 118,
+ TEXTEMOTE_MOCK = 119,
+ TEXTEMOTE_PONDER = 120,
+ TEXTEMOTE_POUNCE = 121,
+ TEXTEMOTE_PRAISE = 122,
+ TEXTEMOTE_PURR = 123,
+ TEXTEMOTE_PUZZLE = 124,
+ TEXTEMOTE_RAISE = 125,
+ TEXTEMOTE_READY = 126,
+ TEXTEMOTE_SHIMMY = 127,
+ TEXTEMOTE_SHIVER = 128,
+ TEXTEMOTE_SHOO = 129,
+ TEXTEMOTE_SLAP = 130,
+ TEXTEMOTE_SMIRK = 131,
+ TEXTEMOTE_SNIFF = 132,
+ TEXTEMOTE_SNUB = 133,
+ TEXTEMOTE_SOOTHE = 134,
+ TEXTEMOTE_STINK = 135,
+ TEXTEMOTE_TAUNT = 136,
+ TEXTEMOTE_TEASE = 137,
+ TEXTEMOTE_THIRSTY = 138,
+ TEXTEMOTE_VETO = 139,
+ TEXTEMOTE_SNICKER = 140,
+ TEXTEMOTE_STAND = 141,
+ TEXTEMOTE_TICKLE = 142,
+ TEXTEMOTE_VIOLIN = 143,
+ TEXTEMOTE_SMILE = 163,
+ TEXTEMOTE_RASP = 183,
+ TEXTEMOTE_PITY = 203,
+ TEXTEMOTE_GROWL = 204,
+ TEXTEMOTE_BARK = 205,
+ TEXTEMOTE_SCARED = 223,
+ TEXTEMOTE_FLOP = 224,
+ TEXTEMOTE_LOVE = 225,
+ TEXTEMOTE_MOO = 226,
+ TEXTEMOTE_OPENFIRE = 327,
+ TEXTEMOTE_FLIRT = 328,
+ TEXTEMOTE_JOKE = 329,
+ TEXTEMOTE_COMMEND = 243,
+ TEXTEMOTE_WINK = 363,
+ TEXTEMOTE_PAT = 364,
+ TEXTEMOTE_SERIOUS = 365,
+ TEXTEMOTE_MOUNTSPECIAL = 366,
+ TEXTEMOTE_GOODLUCK = 367,
+ TEXTEMOTE_BLAME = 368,
+ TEXTEMOTE_BLANK = 369,
+ TEXTEMOTE_BRANDISH = 370,
+ TEXTEMOTE_BREATH = 371,
+ TEXTEMOTE_DISAGREE = 372,
+ TEXTEMOTE_DOUBT = 373,
+ TEXTEMOTE_EMBARRASS = 374,
+ TEXTEMOTE_ENCOURAGE = 375,
+ TEXTEMOTE_ENEMY = 376,
+ TEXTEMOTE_EYEBROW = 377,
+ TEXTEMOTE_TOAST = 378
+};
+
+enum Emote
+{
+ EMOTE_ONESHOT_NONE = 0,
+ EMOTE_ONESHOT_TALK = 1,
+ EMOTE_ONESHOT_BOW = 2,
+ EMOTE_ONESHOT_WAVE = 3,
+ EMOTE_ONESHOT_CHEER = 4,
+ EMOTE_ONESHOT_EXCLAMATION = 5,
+ EMOTE_ONESHOT_QUESTION = 6,
+ EMOTE_ONESHOT_EAT = 7,
+ EMOTE_STATE_DANCE = 10,
+ EMOTE_ONESHOT_LAUGH = 11,
+ EMOTE_STATE_SLEEP = 12,
+ EMOTE_STATE_SIT = 13,
+ EMOTE_ONESHOT_RUDE = 14,
+ EMOTE_ONESHOT_ROAR = 15,
+ EMOTE_ONESHOT_KNEEL = 16,
+ EMOTE_ONESHOT_KISS = 17,
+ EMOTE_ONESHOT_CRY = 18,
+ EMOTE_ONESHOT_CHICKEN = 19,
+ EMOTE_ONESHOT_BEG = 20,
+ EMOTE_ONESHOT_APPLAUD = 21,
+ EMOTE_ONESHOT_SHOUT = 22,
+ EMOTE_ONESHOT_FLEX = 23,
+ EMOTE_ONESHOT_SHY = 24,
+ EMOTE_ONESHOT_POINT = 25,
+ EMOTE_STATE_STAND = 26,
+ EMOTE_STATE_READYUNARMED = 27,
+ EMOTE_STATE_WORK = 28,
+ EMOTE_STATE_POINT = 29,
+ EMOTE_STATE_NONE = 30,
+ EMOTE_ONESHOT_WOUND = 33,
+ EMOTE_ONESHOT_WOUNDCRITICAL = 34,
+ EMOTE_ONESHOT_ATTACKUNARMED = 35,
+ EMOTE_ONESHOT_ATTACK1H = 36,
+ EMOTE_ONESHOT_ATTACK2HTIGHT = 37,
+ EMOTE_ONESHOT_ATTACK2HLOOSE = 38,
+ EMOTE_ONESHOT_PARRYUNARMED = 39,
+ EMOTE_ONESHOT_PARRYSHIELD = 43,
+ EMOTE_ONESHOT_READYUNARMED = 44,
+ EMOTE_ONESHOT_READY1H = 45,
+ EMOTE_ONESHOT_READYBOW = 48,
+ EMOTE_ONESHOT_SPELLPRECAST = 50,
+ EMOTE_ONESHOT_SPELLCAST = 51,
+ EMOTE_ONESHOT_BATTLEROAR = 53,
+ EMOTE_ONESHOT_SPECIALATTACK1H = 54,
+ EMOTE_ONESHOT_KICK = 60,
+ EMOTE_ONESHOT_ATTACKTHROWN = 61,
+ EMOTE_STATE_STUN = 64,
+ EMOTE_STATE_DEAD = 65,
+ EMOTE_ONESHOT_SALUTE = 66,
+ EMOTE_STATE_KNEEL = 68,
+ EMOTE_STATE_USESTANDING = 69,
+ EMOTE_ONESHOT_WAVE_NOSHEATHE = 70,
+ EMOTE_ONESHOT_CHEER_NOSHEATHE = 71,
+ EMOTE_ONESHOT_EAT_NOSHEATHE = 92,
+ EMOTE_STATE_STUN_NOSHEATHE = 93,
+ EMOTE_ONESHOT_DANCE = 94,
+ EMOTE_ONESHOT_SALUTE_NOSHEATH = 113,
+ EMOTE_STATE_USESTANDING_NOSHEATHE = 133,
+ EMOTE_ONESHOT_LAUGH_NOSHEATHE = 153,
+ EMOTE_STATE_WORK_NOSHEATHE = 173,
+ EMOTE_STATE_SPELLPRECAST = 193,
+ EMOTE_ONESHOT_READYRIFLE = 213,
+ EMOTE_STATE_READYRIFLE = 214,
+ EMOTE_STATE_WORK_NOSHEATHE_MINING = 233,
+ EMOTE_STATE_WORK_NOSHEATHE_CHOPWOOD= 234,
+ EMOTE_zzOLDONESHOT_LIFTOFF = 253,
+ EMOTE_ONESHOT_LIFTOFF = 254,
+ EMOTE_ONESHOT_YES = 273,
+ EMOTE_ONESHOT_NO = 274,
+ EMOTE_ONESHOT_TRAIN = 275,
+ EMOTE_ONESHOT_LAND = 293,
+ EMOTE_STATE_AT_EASE = 313,
+ EMOTE_STATE_READY1H = 333,
+ EMOTE_STATE_SPELLKNEELSTART = 353,
+ EMOTE_STATE_SUBMERGED = 373,
+ EMOTE_ONESHOT_SUBMERGE = 374,
+ EMOTE_STATE_READY2H = 375,
+ EMOTE_STATE_READYBOW = 376,
+ EMOTE_ONESHOT_MOUNTSPECIAL = 377,
+ EMOTE_STATE_TALK = 378,
+ EMOTE_STATE_FISHING = 379,
+ EMOTE_ONESHOT_FISHING = 380,
+ EMOTE_ONESHOT_LOOT = 381,
+ EMOTE_STATE_WHIRLWIND = 382,
+ EMOTE_STATE_DROWNED = 383,
+ EMOTE_STATE_HOLD_BOW = 384,
+ EMOTE_STATE_HOLD_RIFLE = 385,
+ EMOTE_STATE_HOLD_THROWN = 386,
+ EMOTE_ONESHOT_DROWN = 387,
+ EMOTE_ONESHOT_STOMP = 388,
+ EMOTE_ONESHOT_ATTACKOFF = 389,
+ EMOTE_ONESHOT_ATTACKOFFPIERCE = 390,
+ EMOTE_STATE_ROAR = 391,
+ EMOTE_STATE_LAUGH = 392,
+ EMOTE_ONESHOT_CREATURE_SPECIAL = 393,
+ EMOTE_ONESHOT_JUMPLANDRUN = 394,
+ EMOTE_ONESHOT_JUMPEND = 395,
+ EMOTE_ONESHOT_TALK_NOSHEATHE = 396,
+ EMOTE_ONESHOT_POINT_NOSHEATHE = 397,
+ EMOTE_STATE_CANNIBALIZE = 398,
+ EMOTE_ONESHOT_JUMPSTART = 399,
+ EMOTE_STATE_DANCESPECIAL = 400,
+ EMOTE_ONESHOT_DANCESPECIAL = 401,
+ EMOTE_ONESHOT_CUSTOMSPELL01 = 402,
+ EMOTE_ONESHOT_CUSTOMSPELL02 = 403,
+ EMOTE_ONESHOT_CUSTOMSPELL03 = 404,
+ EMOTE_ONESHOT_CUSTOMSPELL04 = 405,
+ EMOTE_ONESHOT_CUSTOMSPELL05 = 406,
+ EMOTE_ONESHOT_CUSTOMSPELL06 = 407,
+ EMOTE_ONESHOT_CUSTOMSPELL07 = 408,
+ EMOTE_ONESHOT_CUSTOMSPELL08 = 409,
+ EMOTE_ONESHOT_CUSTOMSPELL09 = 410,
+ EMOTE_ONESHOT_CUSTOMSPELL10 = 411,
+ EMOTE_STATE_EXCLAIM = 412,
+ EMOTE_STATE_SIT_CHAIR_MED = 415,
+ EMOTE_STATE_SPELLEFFECT_HOLD = 422
+};
+
+enum Anim
+{
+ ANIM_STAND = 0x0,
+ ANIM_DEATH = 0x1,
+ ANIM_SPELL = 0x2,
+ ANIM_STOP = 0x3,
+ ANIM_WALK = 0x4,
+ ANIM_RUN = 0x5,
+ ANIM_DEAD = 0x6,
+ ANIM_RISE = 0x7,
+ ANIM_STANDWOUND = 0x8,
+ ANIM_COMBATWOUND = 0x9,
+ ANIM_COMBATCRITICAL = 0xA,
+ ANIM_SHUFFLE_LEFT = 0xB,
+ ANIM_SHUFFLE_RIGHT = 0xC,
+ ANIM_WALK_BACKWARDS = 0xD,
+ ANIM_STUN = 0xE,
+ ANIM_HANDS_CLOSED = 0xF,
+ ANIM_ATTACKUNARMED = 0x10,
+ ANIM_ATTACK1H = 0x11,
+ ANIM_ATTACK2HTIGHT = 0x12,
+ ANIM_ATTACK2HLOOSE = 0x13,
+ ANIM_PARRYUNARMED = 0x14,
+ ANIM_PARRY1H = 0x15,
+ ANIM_PARRY2HTIGHT = 0x16,
+ ANIM_PARRY2HLOOSE = 0x17,
+ ANIM_PARRYSHIELD = 0x18,
+ ANIM_READYUNARMED = 0x19,
+ ANIM_READY1H = 0x1A,
+ ANIM_READY2HTIGHT = 0x1B,
+ ANIM_READY2HLOOSE = 0x1C,
+ ANIM_READYBOW = 0x1D,
+ ANIM_DODGE = 0x1E,
+ ANIM_SPELLPRECAST = 0x1F,
+ ANIM_SPELLCAST = 0x20,
+ ANIM_SPELLCASTAREA = 0x21,
+ ANIM_NPCWELCOME = 0x22,
+ ANIM_NPCGOODBYE = 0x23,
+ ANIM_BLOCK = 0x24,
+ ANIM_JUMPSTART = 0x25,
+ ANIM_JUMP = 0x26,
+ ANIM_JUMPEND = 0x27,
+ ANIM_FALL = 0x28,
+ ANIM_SWIMIDLE = 0x29,
+ ANIM_SWIM = 0x2A,
+ ANIM_SWIM_LEFT = 0x2B,
+ ANIM_SWIM_RIGHT = 0x2C,
+ ANIM_SWIM_BACKWARDS = 0x2D,
+ ANIM_ATTACKBOW = 0x2E,
+ ANIM_FIREBOW = 0x2F,
+ ANIM_READYRIFLE = 0x30,
+ ANIM_ATTACKRIFLE = 0x31,
+ ANIM_LOOT = 0x32,
+ ANIM_SPELL_PRECAST_DIRECTED = 0x33,
+ ANIM_SPELL_PRECAST_OMNI = 0x34,
+ ANIM_SPELL_CAST_DIRECTED = 0x35,
+ ANIM_SPELL_CAST_OMNI = 0x36,
+ ANIM_SPELL_BATTLEROAR = 0x37,
+ ANIM_SPELL_READYABILITY = 0x38,
+ ANIM_SPELL_SPECIAL1H = 0x39,
+ ANIM_SPELL_SPECIAL2H = 0x3A,
+ ANIM_SPELL_SHIELDBASH = 0x3B,
+ ANIM_EMOTE_TALK = 0x3C,
+ ANIM_EMOTE_EAT = 0x3D,
+ ANIM_EMOTE_WORK = 0x3E,
+ ANIM_EMOTE_USE_STANDING = 0x3F,
+ ANIM_EMOTE_EXCLAMATION = 0x40,
+ ANIM_EMOTE_QUESTION = 0x41,
+ ANIM_EMOTE_BOW = 0x42,
+ ANIM_EMOTE_WAVE = 0x43,
+ ANIM_EMOTE_CHEER = 0x44,
+ ANIM_EMOTE_DANCE = 0x45,
+ ANIM_EMOTE_LAUGH = 0x46,
+ ANIM_EMOTE_SLEEP = 0x47,
+ ANIM_EMOTE_SIT_GROUND = 0x48,
+ ANIM_EMOTE_RUDE = 0x49,
+ ANIM_EMOTE_ROAR = 0x4A,
+ ANIM_EMOTE_KNEEL = 0x4B,
+ ANIM_EMOTE_KISS = 0x4C,
+ ANIM_EMOTE_CRY = 0x4D,
+ ANIM_EMOTE_CHICKEN = 0x4E,
+ ANIM_EMOTE_BEG = 0x4F,
+ ANIM_EMOTE_APPLAUD = 0x50,
+ ANIM_EMOTE_SHOUT = 0x51,
+ ANIM_EMOTE_FLEX = 0x52,
+ ANIM_EMOTE_SHY = 0x53,
+ ANIM_EMOTE_POINT = 0x54,
+ ANIM_ATTACK1HPIERCE = 0x55,
+ ANIM_ATTACK2HLOOSEPIERCE = 0x56,
+ ANIM_ATTACKOFF = 0x57,
+ ANIM_ATTACKOFFPIERCE = 0x58,
+ ANIM_SHEATHE = 0x59,
+ ANIM_HIPSHEATHE = 0x5A,
+ ANIM_MOUNT = 0x5B,
+ ANIM_RUN_LEANRIGHT = 0x5C,
+ ANIM_RUN_LEANLEFT = 0x5D,
+ ANIM_MOUNT_SPECIAL = 0x5E,
+ ANIM_KICK = 0x5F,
+ ANIM_SITDOWN = 0x60,
+ ANIM_SITTING = 0x61,
+ ANIM_SITUP = 0x62,
+ ANIM_SLEEPDOWN = 0x63,
+ ANIM_SLEEPING = 0x64,
+ ANIM_SLEEPUP = 0x65,
+ ANIM_SITCHAIRLOW = 0x66,
+ ANIM_SITCHAIRMEDIUM = 0x67,
+ ANIM_SITCHAIRHIGH = 0x68,
+ ANIM_LOADBOW = 0x69,
+ ANIM_LOADRIFLE = 0x6A,
+ ANIM_ATTACKTHROWN = 0x6B,
+ ANIM_READYTHROWN = 0x6C,
+ ANIM_HOLDBOW = 0x6D,
+ ANIM_HOLDRIFLE = 0x6E,
+ ANIM_HOLDTHROWN = 0x6F,
+ ANIM_LOADTHROWN = 0x70,
+ ANIM_EMOTE_SALUTE = 0x71,
+ ANIM_KNEELDOWN = 0x72,
+ ANIM_KNEELING = 0x73,
+ ANIM_KNEELUP = 0x74,
+ ANIM_ATTACKUNARMEDOFF = 0x75,
+ ANIM_SPECIALUNARMED = 0x76,
+ ANIM_STEALTHWALK = 0x77,
+ ANIM_STEALTHSTAND = 0x78,
+ ANIM_KNOCKDOWN = 0x79,
+ ANIM_EATING = 0x7A,
+ ANIM_USESTANDINGLOOP = 0x7B,
+ ANIM_CHANNELCASTDIRECTED = 0x7C,
+ ANIM_CHANNELCASTOMNI = 0x7D,
+ ANIM_WHIRLWIND = 0x7E,
+ ANIM_BIRTH = 0x7F,
+ ANIM_USESTANDINGSTART = 0x80,
+ ANIM_USESTANDINGEND = 0x81,
+ ANIM_HOWL = 0x82,
+ ANIM_DROWN = 0x83,
+ ANIM_DROWNED = 0x84,
+ ANIM_FISHINGCAST = 0x85,
+ ANIM_FISHINGLOOP = 0x86,
+ ANIM_FLY = 0x87,
+ ANIM_EMOTE_WORK_NO_SHEATHE = 0x88,
+ ANIM_EMOTE_STUN_NO_SHEATHE = 0x89,
+ ANIM_EMOTE_USE_STANDING_NO_SHEATHE= 0x8A,
+ ANIM_SPELL_SLEEP_DOWN = 0x8B,
+ ANIM_SPELL_KNEEL_START = 0x8C,
+ ANIM_SPELL_KNEEL_LOOP = 0x8D,
+ ANIM_SPELL_KNEEL_END = 0x8E,
+ ANIM_SPRINT = 0x8F,
+ ANIM_IN_FIGHT = 0x90,
+
+ ANIM_GAMEOBJ_SPAWN = 145,
+ ANIM_GAMEOBJ_CLOSE = 146,
+ ANIM_GAMEOBJ_CLOSED = 147,
+ ANIM_GAMEOBJ_OPEN = 148,
+ ANIM_GAMEOBJ_OPENED = 149,
+ ANIM_GAMEOBJ_DESTROY = 150,
+ ANIM_GAMEOBJ_DESTROYED = 151,
+ ANIM_GAMEOBJ_REBUILD = 152,
+ ANIM_GAMEOBJ_CUSTOM0 = 153,
+ ANIM_GAMEOBJ_CUSTOM1 = 154,
+ ANIM_GAMEOBJ_CUSTOM2 = 155,
+ ANIM_GAMEOBJ_CUSTOM3 = 156,
+ ANIM_GAMEOBJ_DESPAWN = 157,
+ ANIM_HOLD = 158,
+ ANIM_DECAY = 159,
+ ANIM_BOWPULL = 160,
+ ANIM_BOWRELEASE = 161,
+ ANIM_SHIPSTART = 162,
+ ANIM_SHIPMOVEING = 163,
+ ANIM_SHIPSTOP = 164,
+ ANIM_GROUPARROW = 165,
+ ANIM_ARROW = 166,
+ ANIM_CORPSEARROW = 167,
+ ANIM_GUIDEARROW = 168,
+ ANIM_SWAY = 169,
+ ANIM_DRUIDCATPOUNCE = 170,
+ ANIM_DRUIDCATRIP = 171,
+ ANIM_DRUIDCATRAKE = 172,
+ ANIM_DRUIDCATRAVAGE = 173,
+ ANIM_DRUIDCATCLAW = 174,
+ ANIM_DRUIDCATCOWER = 175,
+ ANIM_DRUIDBEARSWIPE = 176,
+ ANIM_DRUIDBEARBITE = 177,
+ ANIM_DRUIDBEARMAUL = 178,
+ ANIM_DRUIDBEARBASH = 179,
+ ANIM_DRAGONTAIL = 180,
+ ANIM_DRAGONSTOMP = 181,
+ ANIM_DRAGONSPIT = 182,
+ ANIM_DRAGONSPITHOVER = 183,
+ ANIM_DRAGONSPITFLY = 184,
+ ANIM_EMOTEYES = 185,
+ ANIM_EMOTENO = 186,
+ ANIM_JUMPLANDRUN = 187,
+ ANIM_LOOTHOLD = 188,
+ ANIM_LOOTUP = 189,
+ ANIM_STANDHIGH = 190,
+ ANIM_IMPACT = 191,
+ ANIM_LIFTOFF = 192,
+ ANIM_HOVER = 193,
+ ANIM_SUCCUBUSENTICE = 194,
+ ANIM_EMOTETRAIN = 195,
+ ANIM_EMOTEDEAD = 196,
+ ANIM_EMOTEDANCEONCE = 197,
+ ANIM_DEFLECT = 198,
+ ANIM_EMOTEEATNOSHEATHE = 199,
+ ANIM_LAND = 200,
+ ANIM_SUBMERGE = 201,
+ ANIM_SUBMERGED = 202,
+ ANIM_CANNIBALIZE = 203,
+ ANIM_ARROWBIRTH = 204,
+ ANIM_GROURARROWBIRTH = 205,
+ ANIM_CORPSEARROWBIRTH = 206,
+ ANIM_GUIDEARROWBIRTH = 207,
+ ANIM_EMOTETALKNOSHEATHE = 208,
+ ANIM_EMOTEPOINTNOSHEATHE = 209,
+ ANIM_EMOTESALUTENOSHEATHE = 210,
+ ANIM_EMOTEDANCESPECIAL = 211,
+ ANIM_MUTILATE = 212,
+ ANIM_CUSTOMSPELL01 = 213,
+ ANIM_CUSTOMSPELL02 = 214,
+ ANIM_CUSTOMSPELL03 = 215,
+ ANIM_CUSTOMSPELL04 = 216,
+ ANIM_CUSTOMSPELL05 = 217,
+ ANIM_CUSTOMSPELL06 = 218,
+ ANIM_CUSTOMSPELL07 = 219,
+ ANIM_CUSTOMSPELL08 = 220,
+ ANIM_CUSTOMSPELL09 = 221,
+ ANIM_CUSTOMSPELL10 = 222,
+ ANIM_StealthRun = 223
+};
+
+enum LockKeyType
+{
+ LOCK_KEY_NONE = 0,
+ LOCK_KEY_ITEM = 1,
+ LOCK_KEY_SKILL = 2
+};
+
+enum LockType
+{
+ LOCKTYPE_PICKLOCK = 1,
+ LOCKTYPE_HERBALISM = 2,
+ LOCKTYPE_MINING = 3,
+ LOCKTYPE_DISARM_TRAP = 4,
+ LOCKTYPE_OPEN = 5,
+ LOCKTYPE_TREASURE = 6,
+ LOCKTYPE_CALCIFIED_ELVEN_GEMS = 7,
+ LOCKTYPE_CLOSE = 8,
+ LOCKTYPE_ARM_TRAP = 9,
+ LOCKTYPE_QUICK_OPEN = 10,
+ LOCKTYPE_QUICK_CLOSE = 11,
+ LOCKTYPE_OPEN_TINKERING = 12,
+ LOCKTYPE_OPEN_KNEELING = 13,
+ LOCKTYPE_OPEN_ATTACKING = 14,
+ LOCKTYPE_GAHZRIDIAN = 15,
+ LOCKTYPE_BLASTING = 16,
+ LOCKTYPE_SLOW_OPEN = 17,
+ LOCKTYPE_SLOW_CLOSE = 18,
+ LOCKTYPE_FISHING = 19
+};
+
+enum TrainerType // this is important type for npcs!
+{
+ TRAINER_TYPE_CLASS = 0,
+ TRAINER_TYPE_MOUNTS = 1, // on blizz it's 2
+ TRAINER_TYPE_TRADESKILLS = 2,
+ TRAINER_TYPE_PETS = 3
+};
+
+#define MAX_TRAINER_TYPE 4
+
+enum CreatureType
+{
+ CREATURE_TYPE_BEAST = 1,
+ CREATURE_TYPE_DRAGON = 2,
+ CREATURE_TYPE_DEMON = 3,
+ CREATURE_TYPE_ELEMENTAL = 4,
+ CREATURE_TYPE_GIANT = 5,
+ CREATURE_TYPE_UNDEAD = 6,
+ CREATURE_TYPE_HUMANOID = 7,
+ CREATURE_TYPE_CRITTER = 8,
+ CREATURE_TYPE_MECHANICAL = 9,
+ CREATURE_TYPE_NOTSPECIFIED = 10,
+ CREATURE_TYPE_TOTEM = 11,
+ CREATURE_TYPE_NON_COMBAT_PET = 12,
+ CREATURE_TYPE_GAS_CLOUD = 13
+};
+
+uint32 const CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD = (1 << (CREATURE_TYPE_HUMANOID-1)) | (1 << (CREATURE_TYPE_UNDEAD-1));
+
+enum CreatureFamily
+{
+ CREATURE_FAMILY_WOLF = 1,
+ CREATURE_FAMILY_CAT = 2,
+ CREATURE_FAMILY_SPIDER = 3,
+ CREATURE_FAMILY_BEAR = 4,
+ CREATURE_FAMILY_BOAR = 5,
+ CREATURE_FAMILY_CROCILISK = 6,
+ CREATURE_FAMILY_CARRION_BIRD = 7,
+ CREATURE_FAMILY_CRAB = 8,
+ CREATURE_FAMILY_GORILLA = 9,
+ CREATURE_FAMILY_RAPTOR = 11,
+ CREATURE_FAMILY_TALLSTRIDER = 12,
+ CREATURE_FAMILY_FELHUNTER = 15,
+ CREATURE_FAMILY_VOIDWALKER = 16,
+ CREATURE_FAMILY_SUCCUBUS = 17,
+ CREATURE_FAMILY_DOOMGUARD = 19,
+ CREATURE_FAMILY_SCORPID = 20,
+ CREATURE_FAMILY_TURTLE = 21,
+ CREATURE_FAMILY_IMP = 23,
+ CREATURE_FAMILY_BAT = 24,
+ CREATURE_FAMILY_HYENA = 25,
+ CREATURE_FAMILY_OWL = 26,
+ CREATURE_FAMILY_WIND_SERPENT = 27,
+ CREATURE_FAMILY_REMOTE_CONTROL = 28,
+ CREATURE_FAMILY_FELGUARD = 29,
+ CREATURE_FAMILY_DRAGONHAWK = 30,
+ CREATURE_FAMILY_RAVAGER = 31,
+ CREATURE_FAMILY_WARP_STALKER = 32,
+ CREATURE_FAMILY_SPOREBAT = 33,
+ CREATURE_FAMILY_NETHER_RAY = 34,
+ CREATURE_FAMILY_SERPENT = 35,
+ CREATURE_FAMILY_SEA_LION = 36
+};
+
+enum CreatureEliteType
+{
+ CREATURE_ELITE_NORMAL = 0,
+ CREATURE_ELITE_ELITE = 1,
+ CREATURE_ELITE_RAREELITE = 2,
+ CREATURE_ELITE_WORLDBOSS = 3,
+ CREATURE_ELITE_RARE = 4,
+ CREATURE_UNKNOWN = 5 // found in 2.2.3 for 2 mobs
+};
+
+// values based at QuestInfo.dbc
+enum QuestTypes
+{
+ QUEST_TYPE_ELITE = 1,
+ QUEST_TYPE_LIFE = 21,
+ QUEST_TYPE_PVP = 41,
+ QUEST_TYPE_RAID = 62,
+ QUEST_TYPE_DUNGEON = 81,
+ QUEST_TYPE_WORLD_EVENT = 82,
+ QUEST_TYPE_LEGENDARY = 83,
+ QUEST_TYPE_ESCORT = 84,
+ QUEST_TYPE_HEROIC = 85,
+};
+
+// values based at QuestSort.dbc
+enum QuestSort
+{
+ QUEST_SORT_EPIC = 1,
+ QUEST_SORT_WAILING_CAVERNS_OLD = 21,
+ QUEST_SORT_SEASONAL = 22,
+ QUEST_SORT_UNDERCITY_OLD = 23,
+ QUEST_SORT_HERBALISM = 24,
+ QUEST_SORT_SCARLET_MONASTERY_OLD= 25,
+ QUEST_SORT_ULDAMN_OLD = 41,
+ QUEST_SORT_WARLOCK = 61,
+ QUEST_SORT_WARRIOR = 81,
+ QUEST_SORT_SHAMAN = 82,
+ QUEST_SORT_FISHING = 101,
+ QUEST_SORT_BLACKSMITHING = 121,
+ QUEST_SORT_PALADIN = 141,
+ QUEST_SORT_MAGE = 161,
+ QUEST_SORT_ROGUE = 162,
+ QUEST_SORT_ALCHEMY = 181,
+ QUEST_SORT_LEATHERWORKING = 182,
+ QUEST_SORT_ENGINERING = 201,
+ QUEST_SORT_TREASURE_MAP = 221,
+ QUEST_SORT_SUNKEN_TEMPLE_OLD = 241,
+ QUEST_SORT_HUNTER = 261,
+ QUEST_SORT_PRIEST = 262,
+ QUEST_SORT_DRUID = 263,
+ QUEST_SORT_TAILORING = 264,
+ QUEST_SORT_SPECIAL = 284,
+ QUEST_SORT_COOKING = 304,
+ QUEST_SORT_FIRST_AID = 324,
+ QUEST_SORT_LEGENDARY = 344,
+ QUEST_SORT_DARKMOON_FAIRE = 364,
+ QUEST_SORT_AHN_QIRAJ_WAR = 365,
+ QUEST_SORT_LUNAR_FESTIVAL = 366,
+ QUEST_SORT_REPUTATION = 367,
+ QUEST_SORT_INVASION = 368,
+ QUEST_SORT_MIDSUMMER = 369,
+ QUEST_SORT_BREWFEST = 370
+};
+
+inline uint8 ClassByQuestSort(int32 QuestSort)
+{
+ switch(QuestSort)
+ {
+ case QUEST_SORT_WARLOCK: return CLASS_WARLOCK;
+ case QUEST_SORT_WARRIOR: return CLASS_WARRIOR;
+ case QUEST_SORT_SHAMAN: return CLASS_SHAMAN;
+ case QUEST_SORT_PALADIN: return CLASS_PALADIN;
+ case QUEST_SORT_MAGE: return CLASS_MAGE;
+ case QUEST_SORT_ROGUE: return CLASS_ROGUE;
+ case QUEST_SORT_HUNTER: return CLASS_HUNTER;
+ case QUEST_SORT_PRIEST: return CLASS_PRIEST;
+ case QUEST_SORT_DRUID: return CLASS_DRUID;
+ }
+ return 0;
+}
+
+enum SkillType
+{
+ SKILL_FROST = 6,
+ SKILL_FIRE = 8,
+ SKILL_ARMS = 26,
+ SKILL_COMBAT = 38,
+ SKILL_SUBTLETY = 39,
+ SKILL_POISONS = 40,
+ SKILL_SWORDS = 43,
+ SKILL_AXES = 44,
+ SKILL_BOWS = 45,
+ SKILL_GUNS = 46,
+ SKILL_BEAST_MASTERY = 50,
+ SKILL_SURVIVAL = 51,
+ SKILL_MACES = 54,
+ SKILL_HOLY = 56,
+ SKILL_2H_SWORDS = 55,
+ SKILL_SHADOW = 78,
+ SKILL_DEFENSE = 95,
+ SKILL_LANG_COMMON = 98,
+ SKILL_RACIAL_DWARVEN = 101,
+ SKILL_LANG_ORCISH = 109,
+ SKILL_LANG_DWARVEN = 111,
+ SKILL_LANG_DARNASSIAN = 113,
+ SKILL_LANG_TAURAHE = 115,
+ SKILL_DUAL_WIELD = 118,
+ SKILL_RACIAL_TAUREN = 124,
+ SKILL_ORC_RACIAL = 125,
+ SKILL_RACIAL_NIGHT_ELF = 126,
+ SKILL_FIRST_AID = 129,
+ SKILL_FERAL_COMBAT = 134,
+ SKILL_STAVES = 136,
+ SKILL_LANG_THALASSIAN = 137,
+ SKILL_LANG_DRACONIC = 138,
+ SKILL_LANG_DEMON_TONGUE = 139,
+ SKILL_LANG_TITAN = 140,
+ SKILL_LANG_OLD_TONGUE = 141,
+ SKILL_SURVIVAL2 = 142,
+ SKILL_RIDING_HORSE = 148,
+ SKILL_RIDING_WOLF = 149,
+ SKILL_RIDING_RAM = 152,
+ SKILL_RIDING_TIGER = 150,
+ SKILL_SWIMING = 155,
+ SKILL_2H_MACES = 160,
+ SKILL_UNARMED = 162,
+ SKILL_MARKSMANSHIP = 163,
+ SKILL_BLACKSMITHING = 164,
+ SKILL_LEATHERWORKING = 165,
+ SKILL_ALCHEMY = 171,
+ SKILL_2H_AXES = 172,
+ SKILL_DAGGERS = 173,
+ SKILL_THROWN = 176,
+ SKILL_HERBALISM = 182,
+ SKILL_GENERIC_DND = 183,
+ SKILL_RETRIBUTION = 184,
+ SKILL_COOKING = 185,
+ SKILL_MINING = 186,
+ SKILL_PET_IMP = 188,
+ SKILL_PET_FELHUNTER = 189,
+ SKILL_TAILORING = 197,
+ SKILL_ENGINERING = 202,
+ SKILL_PET_SPIDER = 203,
+ SKILL_PET_VOIDWALKER = 204,
+ SKILL_PET_SUCCUBUS = 205,
+ SKILL_PET_INFERNAL = 206,
+ SKILL_PET_DOOMGUARD = 207,
+ SKILL_PET_WOLF = 208,
+ SKILL_PET_CAT = 209,
+ SKILL_PET_BEAR = 210,
+ SKILL_PET_BOAR = 211,
+ SKILL_PET_CROCILISK = 212,
+ SKILL_PET_CARRION_BIRD = 213,
+ SKILL_PET_GORILLA = 215,
+ SKILL_PET_CRAB = 214,
+ SKILL_PET_RAPTOR = 217,
+ SKILL_PET_TALLSTRIDER = 218,
+ SKILL_RACIAL_UNDED = 220,
+ SKILL_WEAPON_TALENTS = 222,
+ SKILL_CROSSBOWS = 226,
+ SKILL_SPEARS = 227,
+ SKILL_WANDS = 228,
+ SKILL_POLEARMS = 229,
+ SKILL_PET_SCORPID = 236,
+ SKILL_ARCANE = 237,
+ SKILL_OPEN_LOCK = 242,
+ SKILL_PET_TURTLE = 251,
+ SKILL_ASSASSINATION = 253,
+ SKILL_FURY = 256,
+ SKILL_PROTECTION = 257,
+ SKILL_BEAST_TRAINING = 261,
+ SKILL_PROTECTION2 = 267,
+ SKILL_PET_TALENTS = 270,
+ SKILL_PLATE_MAIL = 293,
+ SKILL_LANG_GNOMISH = 313,
+ SKILL_LANG_TROLL = 315,
+ SKILL_ENCHANTING = 333,
+ SKILL_DEMONOLOGY = 354,
+ SKILL_AFFLICTION = 355,
+ SKILL_FISHING = 356,
+ SKILL_ENHANCEMENT = 373,
+ SKILL_RESTORATION = 374,
+ SKILL_ELEMENTAL_COMBAT = 375,
+ SKILL_SKINNING = 393,
+ SKILL_MAIL = 413,
+ SKILL_LEATHER = 414,
+ SKILL_CLOTH = 415,
+ SKILL_SHIELD = 433,
+ SKILL_FIST_WEAPONS = 473,
+ SKILL_RIDING_RAPTOR = 533,
+ SKILL_RIDING_MECHANOSTRIDER = 553,
+ SKILL_RIDING_UNDEAD_HORSE = 554,
+ SKILL_RESTORATION2 = 573,
+ SKILL_BALANCE = 574,
+ SKILL_DESTRUCTION = 593,
+ SKILL_HOLY2 = 594,
+ SKILL_DISCIPLINE = 613,
+ SKILL_LOCKPICKING = 633,
+ SKILL_PET_BAT = 653,
+ SKILL_PET_HYENA = 654,
+ SKILL_PET_OWL = 655,
+ SKILL_PET_WIND_SERPENT = 656,
+ SKILL_LANG_GUTTERSPEAK = 673,
+ SKILL_RIDING_KODO = 713,
+ SKILL_RACIAL_TROLL = 733,
+ SKILL_RACIAL_GNOME = 753,
+ SKILL_RACIAL_HUMAN = 754,
+ SKILL_JEWELCRAFTING = 755,
+ SKILL_RACIAL_BLOODELF = 756,
+ SKILL_PET_EVENT_RC = 758,
+ SKILL_LANG_DRAENEI = 759,
+ SKILL_RACIAL_DRAENEI = 760,
+ SKILL_PET_FELGUARD = 761,
+ SKILL_RIDING = 762,
+ SKILL_PET_DRAGONHAWK = 763,
+ SKILL_PET_NETHER_RAY = 764,
+ SKILL_PET_SPOREBAT = 765,
+ SKILL_PET_WARP_STALKER = 766,
+ SKILL_PET_RAVAGER = 767,
+ SKILL_PET_SERPENT = 768,
+ SKILL_INTERNAL = 769
+};
+
+#define MAX_SKILL_TYPE 770
+
+inline uint32 SkillByQuestSort(int32 QuestSort)
+{
+ switch(QuestSort)
+ {
+ case QUEST_SORT_HERBALISM: return SKILL_HERBALISM;
+ case QUEST_SORT_FISHING: return SKILL_FISHING;
+ case QUEST_SORT_BLACKSMITHING: return SKILL_BLACKSMITHING;
+ case QUEST_SORT_ALCHEMY: return SKILL_ALCHEMY;
+ case QUEST_SORT_LEATHERWORKING: return SKILL_LEATHERWORKING;
+ case QUEST_SORT_ENGINERING: return SKILL_ENGINERING;
+ case QUEST_SORT_TAILORING: return SKILL_TAILORING;
+ case QUEST_SORT_COOKING: return SKILL_COOKING;
+ case QUEST_SORT_FIRST_AID: return SKILL_FIRST_AID;
+ }
+ return 0;
+}
+
+enum SkillCategory
+{
+ SKILL_CATEGORY_ATTRIBUTES = 5,
+ SKILL_CATEGORY_WEAPON = 6,
+ SKILL_CATEGORY_CLASS = 7,
+ SKILL_CATEGORY_ARMOR = 8,
+ SKILL_CATEGORY_SECONDARY = 9, // secondary professions
+ SKILL_CATEGORY_LANGUAGES = 10,
+ SKILL_CATEGORY_PROFESSION = 11, // primary professions
+ SKILL_CATEGORY_NOT_DISPLAYED = 12
+};
+
+enum TotemCategory
+{
+ TC_SKINNING_SKIFE = 1,
+ TC_EARTH_TOTEM = 2,
+ TC_AIR_TOTEM = 3,
+ TC_FIRE_TOTEM = 4,
+ TC_WATER_TOTEM = 5,
+ TC_COPPER_ROD = 6,
+ TC_SILVER_ROD = 7,
+ TC_GOLDEN_ROD = 8,
+ TC_TRUESILVER_ROD = 9,
+ TC_ARCANITE_ROD = 10,
+ TC_MINING_PICK = 11,
+ TC_PHILOSOPHERS_STONE = 12,
+ TC_BLACKSMITH_HAMMER = 13,
+ TC_ARCLIGHT_SPANNER = 14,
+ TC_GYROMATIC_MA = 15,
+ TC_MASTER_TOTEM = 21,
+ TC_FEL_IRON_ROD = 41,
+ TC_ADAMANTITE_ROD = 62,
+ TC_ETERNIUM_ROD = 63
+};
+
+enum UnitDynFlags
+{
+ UNIT_DYNFLAG_LOOTABLE = 0x0001,
+ UNIT_DYNFLAG_TRACK_UNIT = 0x0002,
+ UNIT_DYNFLAG_OTHER_TAGGER = 0x0004,
+ UNIT_DYNFLAG_ROOTED = 0x0008,
+ UNIT_DYNFLAG_SPECIALINFO = 0x0010,
+ UNIT_DYNFLAG_DEAD = 0x0020
+};
+
+enum CorpseDynFlags
+{
+ CORPSE_DYNFLAG_LOOTABLE = 0x0001
+};
+
+// Passive Spell codes explicit used in code
+#define SPELL_ID_GENERIC_LEARN 483
+#define SPELL_ID_PASSIVE_BATTLE_STANCE 2457
+#define SPELL_ID_PASSIVE_RESURRECTION_SICKNESS 15007
+
+enum WeatherType
+{
+ WEATHER_TYPE_FINE = 0,
+ WEATHER_TYPE_RAIN = 1,
+ WEATHER_TYPE_SNOW = 2,
+ WEATHER_TYPE_STORM = 3,
+ WEATHER_TYPE_THUNDERS = 86,
+ WEATHER_TYPE_BLACKRAIN = 90
+};
+
+#define MAX_WEATHER_TYPE 4
+
+enum ChatMsg
+{
+ CHAT_MSG_ADDON = 0xFFFFFFFF,
+ CHAT_MSG_SYSTEM = 0x00,
+ CHAT_MSG_SAY = 0x01,
+ CHAT_MSG_PARTY = 0x02,
+ CHAT_MSG_RAID = 0x03,
+ CHAT_MSG_GUILD = 0x04,
+ CHAT_MSG_OFFICER = 0x05,
+ CHAT_MSG_YELL = 0x06,
+ CHAT_MSG_WHISPER = 0x07,
+ CHAT_MSG_WHISPER_INFORM = 0x08,
+ CHAT_MSG_REPLY = 0x09,
+ CHAT_MSG_EMOTE = 0x0A,
+ CHAT_MSG_TEXT_EMOTE = 0x0B,
+ CHAT_MSG_MONSTER_SAY = 0x0C,
+ CHAT_MSG_MONSTER_PARTY = 0x0D,
+ CHAT_MSG_MONSTER_YELL = 0x0E,
+ CHAT_MSG_MONSTER_WHISPER = 0x0F,
+ CHAT_MSG_MONSTER_EMOTE = 0x10,
+ CHAT_MSG_CHANNEL = 0x11,
+ CHAT_MSG_CHANNEL_JOIN = 0x12,
+ CHAT_MSG_CHANNEL_LEAVE = 0x13,
+ CHAT_MSG_CHANNEL_LIST = 0x14,
+ CHAT_MSG_CHANNEL_NOTICE = 0x15,
+ CHAT_MSG_CHANNEL_NOTICE_USER = 0x16,
+ CHAT_MSG_AFK = 0x17,
+ CHAT_MSG_DND = 0x18,
+ CHAT_MSG_IGNORED = 0x19,
+ CHAT_MSG_SKILL = 0x1A,
+ CHAT_MSG_LOOT = 0x1B,
+ CHAT_MSG_MONEY = 0x1C,
+ CHAT_MSG_OPENING = 0x1D,
+ CHAT_MSG_TRADESKILLS = 0x1E,
+ CHAT_MSG_PET_INFO = 0x1F,
+ CHAT_MSG_COMBAT_MISC_INFO = 0x20,
+ CHAT_MSG_COMBAT_XP_GAIN = 0x21,
+ CHAT_MSG_COMBAT_HONOR_GAIN = 0x22,
+ CHAT_MSG_COMBAT_FACTION_CHANGE = 0x23,
+ CHAT_MSG_BG_SYSTEM_NEUTRAL = 0x24,
+ CHAT_MSG_BG_SYSTEM_ALLIANCE = 0x25,
+ CHAT_MSG_BG_SYSTEM_HORDE = 0x26,
+ CHAT_MSG_RAID_LEADER = 0x27,
+ CHAT_MSG_RAID_WARNING = 0x28,
+ CHAT_MSG_RAID_BOSS_WHISPER = 0x29,
+ CHAT_MSG_RAID_BOSS_EMOTE = 0x2A,
+ CHAT_MSG_FILTERED = 0x2B,
+ CHAT_MSG_BATTLEGROUND = 0x2C,
+ CHAT_MSG_BATTLEGROUND_LEADER = 0x2D,
+ CHAT_MSG_RESTRICTED = 0x2E,
+};
+
+#define MAX_CHAT_MSG_TYPE 0x2F
+
+// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask
+enum PetDiet
+{
+ PET_DIET_MEAT = 1,
+ PET_DIET_FISH = 2,
+ PET_DIET_CHEESE = 3,
+ PET_DIET_BREAD = 4,
+ PET_DIET_FUNGAS = 5,
+ PET_DIET_FRUIT = 6,
+ PET_DIET_RAW_MEAT = 7,
+ PET_DIET_RAW_FISH = 8
+};
+
+#define MAX_PET_DIET 9
+
+#define CHAIN_SPELL_JUMP_RADIUS 10
+
+// Max values for Guild & Guild Bank
+#define GUILD_BANK_MAX_TABS 6
+#define GUILD_BANK_MAX_SLOTS 98
+#define GUILD_BANK_MAX_LOGS 24
+#define GUILD_EVENTLOG_MAX_ENTRIES 100
+#define GUILD_MAX_RANKS 10
+
+enum AiReaction
+{
+ AI_REACTION_UNK1 = 1,
+ AI_REACTION_AGGRO = 2,
+ AI_REACTION_UNK3 = 3,
+ AI_REACTION_UNK4 = 4
+};
+
+// Diminishing Returns Types
+enum DiminishingReturnsType
+{
+ DRTYPE_NONE = 0, // this spell is not diminished, but may have limited it's duration to 10s
+ DRTYPE_PLAYER = 1, // this spell is diminished only when applied on players
+ DRTYPE_ALL = 2 // this spell is diminished in every case
+};
+
+// Diminishing Return Groups
+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_SLEEP,
+ DIMINISHING_CONTROL_ROOT, // Immobilizing effects from casted spells
+ DIMINISHING_TRIGGER_ROOT, // Immobilizing effects from triggered spells like Frostbite
+ DIMINISHING_FEAR, // Non-warlock fears
+ DIMINISHING_CHARM,
+ // Mage Specific
+ DIMINISHING_POLYMORPH,
+ // Rogue Specific
+ DIMINISHING_KIDNEYSHOT, // Kidney Shot is not diminished with Cheap Shot
+ // Warlock Specific
+ DIMINISHING_DEATHCOIL, // Death Coil Diminish only with another Death Coil
+ DIMINISHING_WARLOCK_FEAR, // Also with Sedduction
+ // Shared Class Specific
+ DIMINISHING_BLIND_CYCLONE, // From 2.3.0
+ DIMINISHING_DISARM, // From 2.3.0
+ DIMINISHING_SILENCE, // From 2.3.0
+ DIMINISHING_FREEZE, // Hunter's Freezing Trap
+ DIMINISHING_KNOCKOUT, // Also with Sap, all Knockout mechanics are here
+ DIMINISHING_BANISH,
+ // Other
+ // Don't Diminish, but limit duration to 10s
+ DIMINISHING_LIMITONLY
+};
+
+enum DungeonDifficulties
+{
+ DIFFICULTY_NORMAL = 0,
+ DIFFICULTY_HEROIC = 1,
+ TOTAL_DIFFICULTIES
+};
+
+enum SummonType
+{
+ SUMMON_TYPE_CRITTER = 41,
+ SUMMON_TYPE_GUARDIAN = 61,
+ SUMMON_TYPE_TOTEM_SLOT1 = 63,
+ SUMMON_TYPE_WILD = 64,
+ SUMMON_TYPE_POSESSED = 65,
+ SUMMON_TYPE_DEMON = 66,
+ SUMMON_TYPE_SUMMON = 67,
+ SUMMON_TYPE_TOTEM_SLOT2 = 81,
+ SUMMON_TYPE_TOTEM_SLOT3 = 82,
+ SUMMON_TYPE_TOTEM_SLOT4 = 83,
+ SUMMON_TYPE_TOTEM = 121,
+ SUMMON_TYPE_UNKNOWN3 = 181,
+ SUMMON_TYPE_UNKNOWN4 = 187,
+ SUMMON_TYPE_UNKNOWN1 = 247,
+ SUMMON_TYPE_UNKNOWN5 = 307,
+ SUMMON_TYPE_CRITTER2 = 407,
+ SUMMON_TYPE_UNKNOWN6 = 409,
+ SUMMON_TYPE_UNKNOWN2 = 427,
+ SUMMON_TYPE_POSESSED2 = 428
+};
+#endif
diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp
new file mode 100644
index 00000000000..388d06ad184
--- /dev/null
+++ b/src/game/SkillDiscovery.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "ProgressBar.h"
+#include "Policies/SingletonImp.h"
+#include "ObjectAccessor.h"
+#include "World.h"
+#include "Util.h"
+#include "SkillDiscovery.h"
+#include "SpellMgr.h"
+#include <map>
+
+struct SkillDiscoveryEntry
+{
+ uint32 spellId;
+ float chance;
+
+ SkillDiscoveryEntry()
+ : spellId(0), chance(0) {}
+
+ SkillDiscoveryEntry(uint16 _spellId, float _chance)
+ : spellId(_spellId), chance(_chance) {}
+};
+
+typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
+typedef HM_NAMESPACE::hash_map<int32, SkillDiscoveryList> SkillDiscoveryMap;
+
+static SkillDiscoveryMap SkillDiscoveryStore;
+
+void LoadSkillDiscoveryTable()
+{
+
+ SkillDiscoveryStore.clear(); // need for reload
+
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *result = WorldDatabase.PQuery("SELECT spellId, reqSpell, chance FROM skill_discovery_template");
+
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+
+ std::ostringstream ssNonDiscoverableEntries;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 spellId = fields[0].GetUInt32();
+ int32 reqSkillOrSpell = fields[1].GetInt32();
+ float chance = fields[2].GetFloat();
+
+ if( chance <= 0 ) // chance
+ {
+ ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " chance = " << chance << "\n";
+ continue;
+ }
+
+ if(reqSkillOrSpell > 0) // spell case
+ {
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(reqSkillOrSpell);
+ if( !spellEntry )
+ {
+ sLog.outErrorDb("Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table",spellId,reqSkillOrSpell);
+ continue;
+ }
+
+ if( spellEntry->Mechanic != MECHANIC_DISCOVERY )
+ {
+ sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc but listed in `skill_discovery_template` table",spellId);
+ continue;
+ }
+
+ SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, chance) );
+ }
+ else if( reqSkillOrSpell == 0 ) // skill case
+ {
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId);
+
+ if(lower==upper)
+ {
+ sLog.outErrorDb("Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table",spellId);
+ continue;
+ }
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, chance) );
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId);
+ continue;
+ }
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ 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());
+ }
+ else
+ {
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." );
+ }
+}
+
+uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
+{
+ // check spell case
+ SkillDiscoveryMap::iterator tab = SkillDiscoveryStore.find(spellId);
+
+ if(tab != SkillDiscoveryStore.end())
+ {
+ for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
+ {
+ if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY))
+ && !player->HasSpell(item_iter->spellId) )
+ return item_iter->spellId;
+ }
+
+ return 0;
+ }
+
+ // check skill line case
+ tab = SkillDiscoveryStore.find(-(int32)skillId);
+ if(tab != SkillDiscoveryStore.end())
+ {
+ for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
+ {
+ if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY))
+ && !player->HasSpell(item_iter->spellId) )
+ return item_iter->spellId;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/game/SkillDiscovery.h b/src/game/SkillDiscovery.h
new file mode 100644
index 00000000000..6a7a16c3849
--- /dev/null
+++ b/src/game/SkillDiscovery.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_SKILLDISCOVERY_H
+#define MANGOS_SKILLDISCOVERY_H
+
+#include "Common.h"
+
+class Player;
+
+void LoadSkillDiscoveryTable();
+uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player);
+#endif
diff --git a/src/game/SkillExtraItems.cpp b/src/game/SkillExtraItems.cpp
new file mode 100644
index 00000000000..42e465be41d
--- /dev/null
+++ b/src/game/SkillExtraItems.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "SkillExtraItems.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "ProgressBar.h"
+#include "Player.h"
+#include <map>
+
+// some type definitions
+// no use putting them in the header file, they're only used in this .cpp
+
+// struct to store information about extra item creation
+// one entry for every spell that is able to create an extra item
+struct SkillExtraItemEntry
+{
+ // the spell id of the specialization required to create extra items
+ uint32 requiredSpecialization;
+ // the chance to create one additional item
+ float additionalCreateChance;
+ // maximum number of extra items created per crafting
+ uint8 additionalMaxNum;
+
+ SkillExtraItemEntry()
+ : requiredSpecialization(0), additionalCreateChance(0.0f), additionalMaxNum(0) {}
+
+ SkillExtraItemEntry(uint32 rS, float aCC, uint8 aMN)
+ : requiredSpecialization(rS), additionalCreateChance(aCC), additionalMaxNum(aMN) {}
+};
+
+// map to store the extra item creation info, the key is the spellId of the creation spell, the mapped value is the assigned SkillExtraItemEntry
+typedef std::map<uint32,SkillExtraItemEntry> SkillExtraItemMap;
+
+SkillExtraItemMap SkillExtraItemStore;
+
+// loads the extra item creation info from DB
+void LoadSkillExtraItemTable()
+{
+ uint32 count = 0;
+
+ SkillExtraItemStore.clear(); // need for reload
+
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.PQuery("SELECT spellId, requiredSpecialization, additionalCreateChance, additionalMaxNum FROM skill_extra_item_template");
+
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 spellId = fields[0].GetUInt32();
+
+ if(!sSpellStore.LookupEntry(spellId))
+ {
+ sLog.outError("Skill specialization %u has non-existent spell id in `skill_extra_item_template`!", spellId);
+ continue;
+ }
+
+ uint32 requiredSpecialization = fields[1].GetUInt32();
+ if(!sSpellStore.LookupEntry(requiredSpecialization))
+ {
+ sLog.outError("Skill specialization %u have not existed required specialization spell id %u in `skill_extra_item_template`!", spellId,requiredSpecialization);
+ continue;
+ }
+
+ float additionalCreateChance = fields[2].GetFloat();
+ if(additionalCreateChance <= 0.0f)
+ {
+ sLog.outError("Skill specialization %u has too low additional create chance in `skill_extra_item_template`!", spellId);
+ continue;
+ }
+
+ uint8 additionalMaxNum = fields[3].GetUInt8();
+ if(!additionalMaxNum)
+ {
+ sLog.outError("Skill specialization %u has 0 max number of extra items in `skill_extra_item_template`!", spellId);
+ continue;
+ }
+
+ SkillExtraItemEntry& skillExtraItemEntry = SkillExtraItemStore[spellId];
+
+ skillExtraItemEntry.requiredSpecialization = requiredSpecialization;
+ skillExtraItemEntry.additionalCreateChance = additionalCreateChance;
+ skillExtraItemEntry.additionalMaxNum = additionalMaxNum;
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell specialization definitions", count );
+ }
+ else
+ {
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty." );
+ }
+}
+
+bool canCreateExtraItems(Player * player, uint32 spellId, float &additionalChance, uint8 &additionalMax)
+{
+ // get the info for the specified spell
+ SkillExtraItemMap::const_iterator ret = SkillExtraItemStore.find(spellId);
+ if(ret==SkillExtraItemStore.end())
+ return false;
+
+ SkillExtraItemEntry const* specEntry = &ret->second;
+
+ // if no entry, then no extra items can be created
+ if(!specEntry)
+ return false;
+
+ // the player doesn't have the required specialization, return false
+ if(!player->HasSpell(specEntry->requiredSpecialization))
+ return false;
+
+ // set the arguments to the appropriate values
+ additionalChance = specEntry->additionalCreateChance;
+ additionalMax = specEntry->additionalMaxNum;
+
+ // enable extra item creation
+ return true;
+}
diff --git a/src/game/SkillExtraItems.h b/src/game/SkillExtraItems.h
new file mode 100644
index 00000000000..51d34da40dd
--- /dev/null
+++ b/src/game/SkillExtraItems.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_SKILL_EXTRA_ITEMS_H
+#define MANGOS_SKILL_EXTRA_ITEMS_H
+
+#include "Common.h"
+
+// predef classes used in functions
+class Player;
+// returns true and sets the appropriate info if the player can create extra items with the given spellId
+bool canCreateExtraItems(Player * player, uint32 spellId, float &additionalChance, uint8 &additionalMax);
+// function to load the extra item creation info from DB
+void LoadSkillExtraItemTable();
+#endif
diff --git a/src/game/SkillHandler.cpp b/src/game/SkillHandler.cpp
new file mode 100644
index 00000000000..18016018ecd
--- /dev/null
+++ b/src/game/SkillHandler.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Player.h"
+#include "World.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "ObjectAccessor.h"
+#include "UpdateMask.h"
+#include "SpellAuras.h"
+
+void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+4);
+
+ uint32 talent_id, requested_rank;
+ recv_data >> talent_id >> requested_rank;
+
+ uint32 CurTalentPoints = GetPlayer()->GetFreeTalentPoints();
+
+ if(CurTalentPoints == 0)
+ return;
+
+ if (requested_rank > 4)
+ return;
+
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id );
+
+ if(!talentInfo)
+ return;
+
+ TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+
+ if(!talentTabInfo)
+ return;
+
+ Player * player = GetPlayer();
+
+ // prevent learn talent for different class (cheating)
+ if( (player->getClassMask() & talentTabInfo->ClassMask) == 0 )
+ return;
+
+ // prevent skip talent ranks (cheating)
+ if(requested_rank > 0 && !player->HasSpell(talentInfo->RankID[requested_rank-1]))
+ return;
+
+ // Check if it requires another talent
+ if (talentInfo->DependsOn > 0)
+ {
+ if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
+ {
+ bool hasEnoughRank = false;
+ for (int i = talentInfo->DependsOnRank; i <= 4; i++)
+ {
+ if (depTalentInfo->RankID[i] != 0)
+ if (player->HasSpell(depTalentInfo->RankID[i]))
+ hasEnoughRank = true;
+ }
+ if (!hasEnoughRank)
+ return;
+ }
+ }
+
+ // Check if it requires spell
+ if( talentInfo->DependsOnSpell && !player->HasSpell(talentInfo->DependsOnSpell) )
+ return;
+
+ // Find out how many points we have in this field
+ uint32 spentPoints = 0;
+
+ uint32 tTab = talentInfo->TalentTab;
+ if (talentInfo->Row > 0)
+ {
+ unsigned int numRows = sTalentStore.GetNumRows();
+ for (unsigned int i = 0; i < numRows; i++) // Loop through all talents.
+ {
+ // Someday, someone needs to revamp
+ const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
+ if (tmpTalent) // the way talents are tracked
+ {
+ if (tmpTalent->TalentTab == tTab)
+ {
+ for (int j = 0; j <= 4; j++)
+ {
+ if (tmpTalent->RankID[j] != 0)
+ {
+ if (player->HasSpell(tmpTalent->RankID[j]))
+ {
+ spentPoints += j + 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // not have required min points spent in talent tree
+ if(spentPoints < (talentInfo->Row * 5))
+ return;
+
+ // spell not set in talent.dbc
+ uint32 spellid = talentInfo->RankID[requested_rank];
+ if( spellid == 0 )
+ {
+ sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talent_id, requested_rank);
+ return;
+ }
+
+ // already known
+ if(GetPlayer( )->HasSpell(spellid))
+ return;
+
+ // learn! (other talent ranks will unlearned at learning)
+ GetPlayer( )->learnSpell(spellid);
+ sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid);
+
+ // update free talent points
+ GetPlayer()->SetFreeTalentPoints(CurTalentPoints - 1);
+}
+
+void WorldSession::HandleTalentWipeOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDetail("MSG_TALENT_WIPE_CONFIRM");
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TRAINER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTalentWipeOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if(!(_player->resetTalents()))
+ {
+ WorldPacket data( MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent
+ data << uint64(0);
+ data << uint32(0);
+ SendPacket( &data );
+ return;
+ }
+
+ unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect"
+}
+
+void WorldSession::HandleUnlearnSkillOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 skill_id;
+ recv_data >> skill_id;
+ GetPlayer()->SetSkill(skill_id, 0, 0);
+}
diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp
new file mode 100644
index 00000000000..85afb48108a
--- /dev/null
+++ b/src/game/SocialMgr.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "SocialMgr.h"
+#include "Policies/SingletonImp.h"
+#include "Database/DatabaseEnv.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1( SocialMgr );
+
+PlayerSocial::PlayerSocial()
+{
+ m_playerGUID = 0;
+}
+
+PlayerSocial::~PlayerSocial()
+{
+ m_playerSocialMap.clear();
+}
+
+bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore)
+{
+ // prevent list (client-side) overflow
+ if(m_playerSocialMap.size() >= (255-1))
+ return false;
+
+ uint32 flag = SOCIAL_FLAG_FRIEND;
+ if(ignore)
+ flag = SOCIAL_FLAG_IGNORED;
+
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr != m_playerSocialMap.end())
+ {
+ CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags | %u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid);
+ m_playerSocialMap[friend_guid].Flags |= flag;
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_social (guid, friend, flags) VALUES ('%u', '%u', '%u')", GetPlayerGUID(), friend_guid, flag);
+ FriendInfo fi;
+ fi.Flags |= flag;
+ m_playerSocialMap[friend_guid] = fi;
+ }
+ return true;
+}
+
+void PlayerSocial::RemoveFromSocialList(uint32 friend_guid, bool ignore)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr == m_playerSocialMap.end()) // not exist
+ return;
+
+ uint32 flag = SOCIAL_FLAG_FRIEND;
+ if(ignore)
+ flag = SOCIAL_FLAG_IGNORED;
+
+ itr->second.Flags &= ~flag;
+ if(itr->second.Flags == 0)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' AND friend = '%u'", GetPlayerGUID(), friend_guid);
+ m_playerSocialMap.erase(itr);
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags & ~%u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid);
+ }
+}
+
+void PlayerSocial::SetFriendNote(uint32 friend_guid, std::string note)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr == m_playerSocialMap.end()) // not exist
+ return;
+
+ utf8truncate(note,48); // DB and client size limitation
+
+ CharacterDatabase.escape_string(note);
+ CharacterDatabase.PExecute("UPDATE character_social SET note = '%s' WHERE guid = '%u' AND friend = '%u'", note.c_str(), GetPlayerGUID(), friend_guid);
+ m_playerSocialMap[friend_guid].Note = note;
+}
+
+void PlayerSocial::SendSocialList()
+{
+ Player *plr = objmgr.GetPlayer(GetPlayerGUID());
+ if(!plr)
+ return;
+
+ uint32 size = m_playerSocialMap.size();
+
+ WorldPacket data(SMSG_CONTACT_LIST, (4+4+size*25)); // just can guess size
+ data << uint32(7); // unk flag (0x1, 0x2, 0x4), 0x7 if it include ignore list
+ data << uint32(size); // friends count
+
+ for(PlayerSocialMap::iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr)
+ {
+ sSocialMgr.GetFriendInfo(plr, itr->first, itr->second);
+
+ data << uint64(itr->first); // player guid
+ data << uint32(itr->second.Flags); // player flag (0x1-friend?, 0x2-ignored?, 0x4-muted?)
+ data << itr->second.Note; // string note
+ if(itr->second.Flags & SOCIAL_FLAG_FRIEND) // if IsFriend()
+ {
+ data << uint8(itr->second.Status); // online/offline/etc?
+ if(itr->second.Status) // if online
+ {
+ data << uint32(itr->second.Area); // player area
+ data << uint32(itr->second.Level); // player level
+ data << uint32(itr->second.Class); // player class
+ }
+ }
+ }
+
+ plr->GetSession()->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent SMSG_CONTACT_LIST");
+}
+
+bool PlayerSocial::HasFriend(uint32 friend_guid)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid);
+ if(itr != m_playerSocialMap.end())
+ return itr->second.Flags & SOCIAL_FLAG_FRIEND;
+ return false;
+}
+
+bool PlayerSocial::HasIgnore(uint32 ignore_guid)
+{
+ PlayerSocialMap::iterator itr = m_playerSocialMap.find(ignore_guid);
+ if(itr != m_playerSocialMap.end())
+ return itr->second.Flags & SOCIAL_FLAG_IGNORED;
+ return false;
+}
+
+SocialMgr::SocialMgr()
+{
+
+}
+
+SocialMgr::~SocialMgr()
+{
+
+}
+
+void SocialMgr::RemovePlayerSocial(uint32 guid)
+{
+ SocialMap::iterator itr = m_socialMap.find(guid);
+ if(itr != m_socialMap.end())
+ m_socialMap.erase(itr);
+}
+
+void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo)
+{
+ if(!player)
+ return;
+
+ Player *pFriend = ObjectAccessor::FindPlayer(friendGUID);
+
+ uint32 team = player->GetTeam();
+ uint32 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;
+
+ // 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) )))
+ {
+ friendInfo.Status = FRIEND_STATUS_ONLINE;
+ if(pFriend->isAFK())
+ friendInfo.Status = FRIEND_STATUS_AFK;
+ if(pFriend->isDND())
+ friendInfo.Status = FRIEND_STATUS_DND;
+ friendInfo.Area = pFriend->GetZoneId();
+ friendInfo.Level = pFriend->getLevel();
+ friendInfo.Class = pFriend->getClass();
+ }
+ else
+ {
+ friendInfo.Status = FRIEND_STATUS_OFFLINE;
+ friendInfo.Area = 0;
+ friendInfo.Level = 0;
+ friendInfo.Class = 0;
+ }
+}
+
+void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket *data)
+{
+ data->Initialize(SMSG_FRIEND_STATUS, 5);
+ *data << uint8(result);
+ *data << uint64(guid);
+}
+
+void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast)
+{
+ FriendInfo fi;
+
+ WorldPacket data;
+ MakeFriendStatusPacket(result, friend_guid, &data);
+ switch(result)
+ {
+ case FRIEND_ONLINE:
+ GetFriendInfo(player, friend_guid, fi);
+ data << uint8(fi.Status);
+ data << uint32(fi.Area);
+ data << uint32(fi.Level);
+ data << uint32(fi.Class);
+ break;
+ case FRIEND_ADDED_ONLINE:
+ GetFriendInfo(player, friend_guid, fi);
+ data << name;
+ data << uint8(fi.Status);
+ data << uint32(fi.Area);
+ data << uint32(fi.Level);
+ data << uint32(fi.Class);
+ break;
+ case FRIEND_ADDED_OFFLINE:
+ data << name;
+ break;
+ }
+
+ if(broadcast)
+ BroadcastToFriendListers(player, &data);
+ else
+ player->GetSession()->SendPacket(&data);
+}
+
+void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet)
+{
+ if(!player)
+ return;
+
+ uint32 team = player->GetTeam();
+ uint32 security = player->GetSession()->GetSecurity();
+ uint32 guid = player->GetGUIDLow();
+ bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST);
+ bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
+
+ for(SocialMap::iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr)
+ {
+ PlayerSocialMap::iterator itr2 = itr->second.m_playerSocialMap.find(guid);
+ if(itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND))
+ {
+ Player *pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ // 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) )))
+ {
+ pFriend->GetSession()->SendPacket(packet);
+ }
+ }
+ }
+}
+
+PlayerSocial *SocialMgr::LoadFromDB(QueryResult *result, uint32 guid)
+{
+ PlayerSocial *social = &m_socialMap[guid];
+ social->SetPlayerGUID(guid);
+
+ if(!result)
+ return social;
+
+ uint32 friend_guid = 0;
+ uint32 flags = 0;
+ std::string note = "";
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ friend_guid = fields[0].GetUInt32();
+ flags = fields[1].GetUInt32();
+ note = fields[2].GetCppString();
+
+ social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note);
+
+ // prevent list (client-side) overflow
+ if(social->m_playerSocialMap.size() >= 255)
+ break;
+ }
+ while( result->NextRow() );
+ delete result;
+ return social;
+}
diff --git a/src/game/SocialMgr.h b/src/game/SocialMgr.h
new file mode 100644
index 00000000000..332ddafa51d
--- /dev/null
+++ b/src/game/SocialMgr.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __MANGOS_SOCIALMGR_H
+#define __MANGOS_SOCIALMGR_H
+
+#include "Policies/Singleton.h"
+#include "Database/DatabaseEnv.h"
+#include "Common.h"
+
+class SocialMgr;
+class PlayerSocial;
+class Player;
+class WorldPacket;
+
+enum FriendStatus
+{
+ FRIEND_STATUS_OFFLINE = 0,
+ FRIEND_STATUS_ONLINE = 1,
+ FRIEND_STATUS_AFK = 2,
+ FRIEND_STATUS_UNK3 = 3,
+ FRIEND_STATUS_DND = 4
+};
+
+enum SocialFlag
+{
+ SOCIAL_FLAG_FRIEND = 0x01,
+ SOCIAL_FLAG_IGNORED = 0x02,
+ SOCIAL_FLAG_MUTED = 0x04 // guessed
+};
+
+struct FriendInfo
+{
+ FriendStatus Status;
+ uint32 Flags;
+ uint32 Area;
+ uint32 Level;
+ uint32 Class;
+ std::string Note;
+
+ FriendInfo()
+ {
+ Status = FRIEND_STATUS_OFFLINE;
+ Flags = 0;
+ Area = 0;
+ Level = 0;
+ Class = 0;
+ Note = "";
+ }
+
+ FriendInfo(uint32 flags, std::string note)
+ {
+ Status = FRIEND_STATUS_OFFLINE;
+ Flags = flags;
+ Area = 0;
+ Level = 0;
+ Class = 0;
+ Note = note;
+ }
+};
+
+typedef std::map<uint32, FriendInfo> PlayerSocialMap;
+typedef std::map<uint32, PlayerSocial> SocialMap;
+
+/// Results of friend related commands
+enum FriendsResult
+{
+ FRIEND_DB_ERROR = 0x00,
+ FRIEND_LIST_FULL = 0x01,
+ FRIEND_ONLINE = 0x02,
+ FRIEND_OFFLINE = 0x03,
+ FRIEND_NOT_FOUND = 0x04,
+ FRIEND_REMOVED = 0x05,
+ FRIEND_ADDED_ONLINE = 0x06,
+ FRIEND_ADDED_OFFLINE = 0x07,
+ FRIEND_ALREADY = 0x08,
+ FRIEND_SELF = 0x09,
+ FRIEND_ENEMY = 0x0A,
+ FRIEND_IGNORE_FULL = 0x0B,
+ FRIEND_IGNORE_SELF = 0x0C,
+ FRIEND_IGNORE_NOT_FOUND = 0x0D,
+ FRIEND_IGNORE_ALREADY = 0x0E,
+ FRIEND_IGNORE_ADDED = 0x0F,
+ FRIEND_IGNORE_REMOVED = 0x10,
+ FRIEND_IGNORE_AMBIGUOUS = 0x11, // That name is ambiguous, type more of the player's server name
+ FRIEND_MUTE_FULL = 0x12,
+ FRIEND_MUTE_SELF = 0x13,
+ FRIEND_MUTE_NOT_FOUND = 0x14,
+ FRIEND_MUTE_ALREADY = 0x15,
+ FRIEND_MUTE_ADDED = 0x16,
+ FRIEND_MUTE_REMOVED = 0x17,
+ FRIEND_MUTE_AMBIGUOUS = 0x18, // That name is ambiguous, type more of the player's server name
+ FRIEND_UNK7 = 0x19, // no message at client
+ FRIEND_UNKNOWN = 0x1A // Unknown friend response from server
+};
+
+class PlayerSocial
+{
+ friend class SocialMgr;
+ public:
+ PlayerSocial();
+ ~PlayerSocial();
+ // adding/removing
+ bool AddToSocialList(uint32 friend_guid, bool ignore);
+ void RemoveFromSocialList(uint32 friend_guid, bool ignore);
+ void SetFriendNote(uint32 friend_guid, std::string note);
+ // Packet send's
+ void SendSocialList();
+ // Misc
+ bool HasFriend(uint32 friend_guid);
+ bool HasIgnore(uint32 ignore_guid);
+ uint32 GetPlayerGUID() { return m_playerGUID; }
+ void SetPlayerGUID(uint32 guid) { m_playerGUID = guid; }
+ private:
+ PlayerSocialMap m_playerSocialMap;
+ uint32 m_playerGUID;
+};
+
+class SocialMgr
+{
+ public:
+ SocialMgr();
+ ~SocialMgr();
+ // Misc
+ void RemovePlayerSocial(uint32 guid);
+ void GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo);
+ // Packet management
+ void MakeFriendStatusPacket(FriendsResult result, uint32 friend_guid, WorldPacket *data);
+ void SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast);
+ void BroadcastToFriendListers(Player *player, WorldPacket *packet);
+ // Loading
+ PlayerSocial *LoadFromDB(QueryResult *result, uint32 guid);
+ private:
+ SocialMap m_socialMap;
+};
+
+#define sSocialMgr MaNGOS::Singleton<SocialMgr>::Instance()
+#endif
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
new file mode 100644
index 00000000000..95e9ac1b745
--- /dev/null
+++ b/src/game/Spell.cpp
@@ -0,0 +1,5115 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Pet.h"
+#include "Unit.h"
+#include "Spell.h"
+#include "DynamicObject.h"
+#include "SpellAuras.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "CellImpl.h"
+#include "Policies/SingletonImp.h"
+#include "SharedDefines.h"
+#include "Tools.h"
+#include "LootMgr.h"
+#include "VMapFactory.h"
+#include "BattleGround.h"
+#include "Util.h"
+
+#define SPELL_CHANNEL_UPDATE_INTERVAL 1000
+
+extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
+
+bool IsQuestTameSpell(uint32 spellId)
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ return spellproto->Effect[0] == SPELL_EFFECT_THREAT
+ && spellproto->Effect[1] == SPELL_EFFECT_APPLY_AURA && spellproto->EffectApplyAuraName[1] == SPELL_AURA_DUMMY;
+}
+
+SpellCastTargets::SpellCastTargets()
+{
+ m_unitTarget = NULL;
+ m_itemTarget = NULL;
+ m_GOTarget = NULL;
+
+ m_unitTargetGUID = 0;
+ m_GOTargetGUID = 0;
+ m_CorpseTargetGUID = 0;
+ m_itemTargetGUID = 0;
+ m_itemTargetEntry = 0;
+
+ m_srcX = m_srcY = m_srcZ = m_destX = m_destY = m_destZ = 0;
+ m_strTarget = "";
+ m_targetMask = 0;
+}
+
+SpellCastTargets::~SpellCastTargets()
+{
+}
+
+void SpellCastTargets::setUnitTarget(Unit *target)
+{
+ if (!target)
+ return;
+
+ m_destX = target->GetPositionX();
+ m_destY = target->GetPositionY();
+ m_destZ = target->GetPositionZ();
+ m_unitTarget = target;
+ m_unitTargetGUID = target->GetGUID();
+ m_targetMask |= TARGET_FLAG_UNIT;
+}
+
+void SpellCastTargets::setDestination(float x, float y, float z)
+{
+ m_destX = x;
+ m_destY = y;
+ m_destZ = z;
+ m_targetMask |= TARGET_FLAG_DEST_LOCATION;
+}
+
+void SpellCastTargets::setGOTarget(GameObject *target)
+{
+ m_GOTarget = target;
+ m_GOTargetGUID = target->GetGUID();
+ // m_targetMask |= TARGET_FLAG_OBJECT;
+}
+
+void SpellCastTargets::setItemTarget(Item* item)
+{
+ if(!item)
+ return;
+
+ m_itemTarget = item;
+ m_itemTargetGUID = item->GetGUID();
+ m_itemTargetEntry = item->GetEntry();
+ m_targetMask |= TARGET_FLAG_ITEM;
+}
+
+void SpellCastTargets::setCorpseTarget(Corpse* corpse)
+{
+ m_CorpseTargetGUID = corpse->GetGUID();
+}
+
+void SpellCastTargets::Update(Unit* caster)
+{
+ m_GOTarget = m_GOTargetGUID ? ObjectAccessor::GetGameObject(*caster,m_GOTargetGUID) : NULL;
+ m_unitTarget = m_unitTargetGUID ?
+ ( m_unitTargetGUID==caster->GetGUID() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID) ) :
+ NULL;
+
+ m_itemTarget = NULL;
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(m_targetMask & TARGET_FLAG_ITEM)
+ m_itemTarget = ((Player*)caster)->GetItemByGuid(m_itemTargetGUID);
+ else
+ {
+ Player* pTrader = ((Player*)caster)->GetTrader();
+ if(pTrader && m_itemTargetGUID < TRADE_SLOT_COUNT)
+ m_itemTarget = pTrader->GetItemByPos(pTrader->GetItemPosByTradeSlot(m_itemTargetGUID));
+ }
+ if(m_itemTarget)
+ m_itemTargetEntry = m_itemTarget->GetEntry();
+ }
+}
+
+bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
+{
+ if(data->rpos()+4 > data->size())
+ return false;
+
+ *data >> m_targetMask;
+
+ if(m_targetMask == TARGET_FLAG_SELF)
+ {
+ m_destX = caster->GetPositionX();
+ m_destY = caster->GetPositionY();
+ m_destZ = caster->GetPositionZ();
+ m_unitTarget = caster;
+ m_unitTargetGUID = caster->GetGUID();
+ return true;
+ }
+ // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other?
+ if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) )
+ if(!readGUID(*data, m_unitTargetGUID))
+ return false;
+
+ if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ))
+ if(!readGUID(*data, m_GOTargetGUID))
+ return false;
+
+ if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER)
+ if(!readGUID(*data, m_itemTargetGUID))
+ return false;
+
+ if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
+ {
+ if(data->rpos()+4+4+4 > data->size())
+ return false;
+
+ *data >> m_srcX >> m_srcY >> m_srcZ;
+ if(!MaNGOS::IsValidMapCoord(m_srcX, m_srcY, m_srcZ))
+ return false;
+ }
+
+ if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ {
+ if(data->rpos()+4+4+4 > data->size())
+ return false;
+
+ *data >> m_destX >> m_destY >> m_destZ;
+ if(!MaNGOS::IsValidMapCoord(m_destX, m_destY, m_destZ))
+ return false;
+ }
+
+ if( m_targetMask & TARGET_FLAG_STRING )
+ {
+ if(data->rpos()+1 > data->size())
+ return false;
+
+ *data >> m_strTarget;
+ }
+
+ if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
+ if(!readGUID(*data, m_CorpseTargetGUID))
+ return false;
+
+ // find real units/GOs
+ Update(caster);
+ return true;
+}
+
+void SpellCastTargets::write ( WorldPacket * data )
+{
+ *data << uint32(m_targetMask);
+
+ if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_PVP_CORPSE | TARGET_FLAG_OBJECT | TARGET_FLAG_CORPSE | TARGET_FLAG_UNK2 ) )
+ {
+ if(m_targetMask & TARGET_FLAG_UNIT)
+ {
+ if(m_unitTarget)
+ data->append(m_unitTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+ else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) )
+ {
+ if(m_GOTarget)
+ data->append(m_GOTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+ else if( m_targetMask & ( TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
+ data->appendPackGUID(m_CorpseTargetGUID);
+ else
+ *data << uint8(0);
+ }
+
+ if( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM ) )
+ {
+ if(m_itemTarget)
+ data->append(m_itemTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+
+ if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
+ *data << m_srcX << m_srcY << m_srcZ;
+
+ if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ *data << m_destX << m_destY << m_destZ;
+
+ if( m_targetMask & TARGET_FLAG_STRING )
+ *data << m_strTarget;
+}
+
+Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID, Spell** triggeringContainer )
+{
+ ASSERT( Caster != NULL && info != NULL );
+ ASSERT( info == sSpellStore.LookupEntry( info->Id ) && "`info` must be pointer to sSpellStore element");
+
+ m_spellInfo = info;
+ m_caster = Caster;
+ m_selfContainer = NULL;
+ m_triggeringContainer = triggeringContainer;
+ m_deletable = true;
+ m_delayAtDamageCount = 0;
+
+ m_applyMultiplierMask = 0;
+
+ // Get data for type of attack
+ switch (m_spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE:
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
+ m_attackType = OFF_ATTACK;
+ else
+ m_attackType = BASE_ATTACK;
+ break;
+ case SPELL_DAMAGE_CLASS_RANGED:
+ m_attackType = RANGED_ATTACK;
+ break;
+ default:
+ // Wands
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND)
+ m_attackType = RANGED_ATTACK;
+ else
+ m_attackType = BASE_ATTACK;
+ break;
+ }
+
+ m_spellSchoolMask = GetSpellSchoolMask(info); // Can be override for some spell (wand shoot for example)
+
+ if(m_attackType == RANGED_ATTACK)
+ {
+ // wand case
+ if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK))
+ m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType);
+ }
+ }
+
+ if(originalCasterGUID)
+ m_originalCasterGUID = originalCasterGUID;
+ else
+ m_originalCasterGUID = m_caster->GetGUID();
+
+ if(m_originalCasterGUID==m_caster->GetGUID())
+ m_originalCaster = m_caster;
+ else
+ {
+ m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
+ if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
+ }
+
+ for(int i=0; i <3; ++i)
+ m_currentBasePoints[i] = m_spellInfo->EffectBasePoints[i];
+
+ m_spellState = SPELL_STATE_NULL;
+
+ m_castPositionX = m_castPositionY = m_castPositionZ = 0;
+ m_TriggerSpells.clear();
+ m_IsTriggeredSpell = triggered;
+ //m_AreaAura = false;
+ m_CastItem = NULL;
+
+ unitTarget = NULL;
+ itemTarget = NULL;
+ gameObjTarget = NULL;
+ focusObject = NULL;
+ m_cast_count = 0;
+ m_triggeredByAuraSpell = NULL;
+
+ //Auto Shot & Shoot
+ if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered )
+ m_autoRepeat = true;
+ else
+ m_autoRepeat = false;
+
+ m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before.
+ m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before.
+ m_timer = 0; // will set to castime in preper
+
+ m_needAliveTargetMask = 0;
+
+ // determine reflection
+ m_canReflect = false;
+
+ if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0)
+ {
+ for(int j=0;j<3;j++)
+ {
+ if (m_spellInfo->Effect[j]==0)
+ continue;
+
+ if(!IsPositiveTarget(m_spellInfo->EffectImplicitTargetA[j],m_spellInfo->EffectImplicitTargetB[j]))
+ m_canReflect = true;
+ else
+ m_canReflect = (m_spellInfo->AttributesEx & (1<<7)) ? true : false;
+
+ if(m_canReflect)
+ continue;
+ else
+ break;
+ }
+ }
+
+ CleanupTargetList();
+}
+
+Spell::~Spell()
+{
+}
+
+void Spell::FillTargetMap()
+{
+ // TODO: ADD the correct target FILLS!!!!!!
+
+ for(uint32 i=0;i<3;i++)
+ {
+ // not call for empty effect.
+ // Also some spells use not used effect targets for store targets for dummy effect in triggered spells
+ if(m_spellInfo->Effect[i]==0)
+ continue;
+
+ // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT filled in Spell::canCast call
+ if( m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT ||
+ m_spellInfo->EffectImplicitTargetB[i] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[i] != TARGET_SELF )
+ continue;
+
+ // TODO: find a way so this is not needed?
+ // for area auras always add caster as target (needed for totems for example)
+ if(IsAreaAuraEffect(m_spellInfo->Effect[i]))
+ AddUnitTarget(m_caster, i);
+
+ std::list<Unit*> tmpUnitMap;
+
+ // TargetA/TargetB dependent from each other, we not switch to full support this dependences
+ // but need it support in some know cases
+ switch(m_spellInfo->EffectImplicitTargetA[i])
+ {
+ case TARGET_ALL_AROUND_CASTER:
+ if( m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_PARTY ||
+ m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER ||
+ m_spellInfo->EffectImplicitTargetB[i]==TARGET_RANDOM_RAID_MEMBER )
+ {
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ }
+ // Note: this hack with search required until GO casting not implemented
+ // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
+ // currently each enemy selected explicitly and self cast damage
+ else if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA && m_spellInfo->Effect[i]==SPELL_EFFECT_ENVIRONMENTAL_DAMAGE)
+ {
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else
+ {
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ }
+ break;
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ // Only if target A, for target B (used in teleports) dest select in effect
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ break;
+ default:
+ switch(m_spellInfo->EffectImplicitTargetB[i])
+ {
+ case TARGET_SCRIPT_COORDINATES: // B case filled in canCast but we need fill unit list base at A case
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ break;
+ default:
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ break;
+ }
+ break;
+ }
+
+ if( (m_spellInfo->EffectImplicitTargetA[i]==0 || m_spellInfo->EffectImplicitTargetA[i]==TARGET_EFFECT_SELECT) &&
+ (m_spellInfo->EffectImplicitTargetB[i]==0 || m_spellInfo->EffectImplicitTargetB[i]==TARGET_EFFECT_SELECT) )
+ {
+ // add here custom effects that need default target.
+ // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!!
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_DUMMY:
+ {
+ switch(m_spellInfo->Id)
+ {
+ case 20577: // Cannibalize
+ {
+ // non-standard target selection
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float max_range = GetSpellMaxRange(srange);
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ WorldObject* result = NULL;
+
+ MaNGOS::CannibalizeObjectCheck u_check(m_caster, max_range);
+ MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck > searcher(result, u_check);
+
+ TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, GridTypeMapContainer > grid_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(!result)
+ {
+ TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, WorldTypeMapContainer > world_searcher(searcher);
+ cell_lock->Visit(cell_lock, world_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ if(result)
+ {
+ switch(result->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ case TYPEID_PLAYER:
+ tmpUnitMap.push_back((Unit*)result);
+ break;
+ case TYPEID_CORPSE:
+ m_targets.setCorpseTarget((Corpse*)result);
+ if(Player* owner = ObjectAccessor::FindPlayer(((Corpse*)result)->GetOwnerGUID()))
+ tmpUnitMap.push_back(owner);
+ break;
+ }
+ }
+ else
+ {
+ // clear cooldown at fail
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(m_spellInfo->Id);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+
+ SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES);
+ finish(false);
+ }
+ break;
+ }
+ default:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ break;
+ }
+ case SPELL_EFFECT_RESURRECT:
+ case SPELL_EFFECT_PARRY:
+ case SPELL_EFFECT_CREATE_ITEM:
+ case SPELL_EFFECT_TRIGGER_SPELL:
+ case SPELL_EFFECT_TRIGGER_MISSILE:
+ case SPELL_EFFECT_LEARN_SPELL:
+ case SPELL_EFFECT_SKILL_STEP:
+ case SPELL_EFFECT_PROFICIENCY:
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_OBJECT_WILD:
+ case SPELL_EFFECT_SELF_RESURRECT:
+ case SPELL_EFFECT_REPUTATION:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ case SPELL_EFFECT_SUMMON_PLAYER:
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetSelection())
+ {
+ Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
+ if(target)
+ tmpUnitMap.push_back(target);
+ }
+ break;
+ case SPELL_EFFECT_RESURRECT_NEW:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ if(m_targets.getCorpseTargetGUID())
+ {
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(corpse)
+ {
+ Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
+ if(owner)
+ tmpUnitMap.push_back(owner);
+ }
+ }
+ break;
+ case SPELL_EFFECT_SUMMON:
+ if(m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2)
+ {
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else
+ tmpUnitMap.push_back(m_caster);
+ break;
+ case SPELL_EFFECT_SUMMON_CHANGE_ITEM:
+ case SPELL_EFFECT_SUMMON_WILD:
+ case SPELL_EFFECT_SUMMON_GUARDIAN:
+ case SPELL_EFFECT_TRANS_DOOR:
+ case SPELL_EFFECT_ADD_FARSIGHT:
+ case SPELL_EFFECT_STUCK:
+ case SPELL_EFFECT_DESTROY_ALL_TOTEMS:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ case SPELL_EFFECT_SKILL:
+ tmpUnitMap.push_back(m_caster);
+ break;
+ case SPELL_EFFECT_LEARN_PET_SPELL:
+ if(Pet* pet = m_caster->GetPet())
+ tmpUnitMap.push_back(pet);
+ break;
+ case SPELL_EFFECT_ENCHANT_ITEM:
+ case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
+ case SPELL_EFFECT_DISENCHANT:
+ case SPELL_EFFECT_FEED_PET:
+ case SPELL_EFFECT_PROSPECTING:
+ if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ break;
+ case SPELL_EFFECT_APPLY_AURA:
+ switch(m_spellInfo->EffectApplyAuraName[i])
+ {
+ case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_SELF(1) (and present for other ranks for same spell for example)
+ case SPELL_AURA_ADD_PCT_MODIFIER:
+ tmpUnitMap.push_back(m_caster);
+ break;
+ default: // apply to target in other case
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
+ // AreaAura
+ if(m_spellInfo->Attributes == 0x9050000 || m_spellInfo->Attributes == 0x10000)
+ SetTargetMap(i,TARGET_AREAEFFECT_PARTY,tmpUnitMap);
+ break;
+ case SPELL_EFFECT_SKIN_PLAYER_CORPSE:
+ if(m_targets.getUnitTarget())
+ {
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else if (m_targets.getCorpseTargetGUID())
+ {
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(corpse)
+ {
+ Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
+ if(owner)
+ tmpUnitMap.push_back(owner);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty())
+ m_needAliveTargetMask |= (1<<i);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Player *me = (Player*)m_caster;
+ for (std::list<Unit*>::const_iterator itr = tmpUnitMap.begin(); itr != tmpUnitMap.end(); itr++)
+ {
+ Unit *owner = (*itr)->GetOwner();
+ Unit *u = owner ? owner : (*itr);
+ if(u!=m_caster && u->IsPvP() && (!me->duel || me->duel->opponent != u))
+ {
+ me->UpdatePvP(true);
+ me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ break;
+ }
+ }
+ }
+
+ for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();)
+ {
+ if(!CheckTarget(*itr, i, false ))
+ {
+ itr = tmpUnitMap.erase(itr);
+ continue;
+ }
+ else
+ ++itr;
+ }
+
+ for(std::list<Unit*>::iterator iunit= tmpUnitMap.begin();iunit != tmpUnitMap.end();++iunit)
+ AddUnitTarget((*iunit), i);
+ }
+}
+
+void Spell::CleanupTargetList()
+{
+ m_UniqueTargetInfo.clear();
+ m_UniqueGOTargetInfo.clear();
+ m_UniqueItemInfo.clear();
+ m_countOfHit = 0;
+ m_countOfMiss = 0;
+ m_delayMoment = 0;
+}
+
+void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ uint64 targetGUID = pVictim->GetGUID();
+
+ // Lookup target in already in list
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if (targetGUID == ihit->targetGUID) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target calculate data for him
+
+ // Get spell hit result on target
+ TargetInfo target;
+ target.targetGUID = targetGUID; // Store target GUID
+ target.effectMask = 1<<effIndex; // Store index of effect
+ target.processed = false; // Effects not apply on target
+
+ // Calculate hit result
+ target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect);
+ if (target.missCondition == SPELL_MISS_NONE)
+ ++m_countOfHit;
+ else
+ ++m_countOfMiss;
+
+ // Spell have speed - need calculate incoming time
+ if (m_spellInfo->speed > 0.0f)
+ {
+ // calculate spell incoming interval
+ float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
+ if (dist < 5.0f) dist = 5.0f;
+ target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
+
+ // Calculate minimum incoming time
+ if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
+ m_delayMoment = target.timeDelay;
+ }
+ else
+ target.timeDelay = 0LL;
+
+ // If target reflect spell back to caster
+ if (target.missCondition==SPELL_MISS_REFLECT)
+ {
+ // Calculate reflected spell result on caster
+ target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
+
+ if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
+ target.reflectResult = SPELL_MISS_PARRY;
+
+ // Increase time interval for reflected spells by 1.5
+ target.timeDelay+=target.timeDelay>>1;
+ }
+ else
+ target.reflectResult = SPELL_MISS_NONE;
+
+ // Add target to list
+ m_UniqueTargetInfo.push_back(target);
+}
+
+void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex)
+{
+ Unit* unit = m_caster->GetGUID()==unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID);
+ if (unit)
+ AddUnitTarget(unit, effIndex);
+}
+
+void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ uint64 targetGUID = pVictim->GetGUID();
+
+ // Lookup target in already in list
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ {
+ if (targetGUID == ihit->targetGUID) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target calculate data for him
+
+ GOTargetInfo target;
+ target.targetGUID = targetGUID;
+ target.effectMask = 1<<effIndex;
+ target.processed = false; // Effects not apply on target
+
+ // Spell have speed - need calculate incoming time
+ if (m_spellInfo->speed > 0.0f)
+ {
+ // calculate spell incoming interval
+ float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
+ if (dist < 5.0f) dist = 5.0f;
+ target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
+ if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
+ m_delayMoment = target.timeDelay;
+ }
+ else
+ target.timeDelay = 0LL;
+
+ ++m_countOfHit;
+
+ // Add target to list
+ m_UniqueGOTargetInfo.push_back(target);
+}
+
+void Spell::AddGOTarget(uint64 goGUID, uint32 effIndex)
+{
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, goGUID);
+ if (go)
+ AddGOTarget(go, effIndex);
+}
+
+void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ // Lookup target in already in list
+ for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
+ {
+ if (pitem == ihit->item) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target add data
+
+ ItemTargetInfo target;
+ target.item = pitem;
+ target.effectMask = 1<<effIndex;
+ m_UniqueItemInfo.push_back(target);
+}
+
+void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit)
+{
+ // Do triggers depends from hit result (triggers on hit do in effects)
+ // Set aura states depends from hit result
+ if (missInfo!=SPELL_MISS_NONE)
+ {
+ // Miss/dodge/parry/block only for melee based spells
+ // Resist only for magic based spells
+ switch (missInfo)
+ {
+ case SPELL_MISS_MISS:
+ if(m_caster->GetTypeId()== TYPEID_PLAYER)
+ ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK);
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_RESIST:
+ m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_DODGE:
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->UpdateDefense();
+
+ // Overpower
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR)
+ {
+ ((Player*) m_caster)->AddComboPoints(unitTarget, 1);
+ m_caster->StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ // Riposte
+ if (unitTarget->getClass() != CLASS_ROGUE)
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_PARRY:
+ // Update victim defense ?
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->UpdateDefense();
+ // Mongoose bite - set only Counterattack here
+ if (unitTarget->getClass() == CLASS_HUNTER)
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_BLOCK:
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ // Trigger from this events not supported
+ case SPELL_MISS_EVADE:
+ case SPELL_MISS_IMMUNE:
+ case SPELL_MISS_IMMUNE2:
+ case SPELL_MISS_DEFLECT:
+ case SPELL_MISS_ABSORB:
+ // Trigger from reflects need do after get reflect result
+ case SPELL_MISS_REFLECT:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Spell::DoAllEffectOnTarget(TargetInfo *target)
+{
+ if (target->processed) // Check target
+ return;
+ target->processed = true; // Target checked in apply effects procedure
+
+ // Get mask of effects for target
+ uint32 mask = target->effectMask;
+ if (mask == 0) // No effects
+ return;
+
+ Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
+ if (!unit)
+ return;
+
+ SpellMissInfo missInfo = target->missCondition;
+ // Need init unitTarget by default unit (can changed in code on reflect)
+ // Or on missInfo!=SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem)
+ unitTarget = unit;
+
+ if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
+ DoSpellHitOnUnit(unit, mask);
+ else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
+ {
+ if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him
+ DoSpellHitOnUnit(m_caster, mask);
+ }
+
+ // Do triggers only on miss/resist/parry/dodge
+ if (missInfo!=SPELL_MISS_NONE)
+ doTriggers(missInfo);
+
+ // Call scripted function for AI if this spell is casted upon a creature (except pets)
+ if(IS_CREATURE_GUID(target->targetGUID))
+ {
+ // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
+ ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
+
+ if(((Creature*)unit)->AI())
+ ((Creature*)unit)->AI()->SpellHit(m_caster ,m_spellInfo);
+ }
+}
+
+void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
+{
+ if(!unit || !effectMask)
+ return;
+
+ // Recheck immune (only for delayed spells)
+ if( m_spellInfo->speed && (
+ unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) ||
+ unit->IsImmunedToSpell(m_spellInfo,true) ))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE);
+ return;
+ }
+
+ if( m_caster != unit )
+ {
+ if( !m_caster->IsFriendlyTo(unit) )
+ {
+ // for delayed spells ignore not visible explicit target
+ if(m_spellInfo->speed > 0.0f && unit==m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster,false))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
+ return;
+ }
+
+ // exclude Arcane Missiles Dummy Aura aura for now (attack on hit)
+ // TODO: find way to not need this?
+ if(!(m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
+ m_spellInfo->SpellFamilyFlags & 0x800LL))
+ {
+ unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ {
+ if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNDED))
+ unit->SetStandState(PLAYER_STATE_NONE);
+
+ if(!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI())
+ ((Creature*)unit)->AI()->AttackStart(m_caster);
+
+ unit->SetInCombatWith(m_caster);
+ m_caster->SetInCombatWith(unit);
+
+ if(Player *attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself())
+ {
+ m_caster->SetContestedPvP(attackedPlayer);
+ }
+ unit->AddThreat(m_caster, 0.0f);
+ }
+ }
+ }
+ else
+ {
+ // for delayed spells ignore negative spells (after duel end) for friendly targets
+ if(m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
+ return;
+ }
+
+ // assisting case, healing and resurrection
+ if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ m_caster->SetContestedPvP();
+ if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ {
+ m_caster->SetInCombatState(unit->GetCombatTimer() > 0);
+ unit->getHostilRefManager().threatAssist(m_caster, 0.0f);
+ }
+ }
+ }
+
+ // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add
+ m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo,m_triggeredByAuraSpell);
+ m_diminishLevel = unit->GetDiminishing(m_diminishGroup);
+ // Increase Diminishing on unit, current informations for actually casts will use values above
+ if((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL)
+ unit->IncrDiminishing(m_diminishGroup);
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ {
+ if (effectMask & (1<<effectNumber))
+ {
+ HandleEffects(unit,NULL,NULL,effectNumber,m_damageMultipliers[effectNumber]);
+ if ( m_applyMultiplierMask & (1 << effectNumber) )
+ {
+ // Get multiplier
+ float multiplier = m_spellInfo->DmgMultiplier[effectNumber];
+ // Apply multiplier mods
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier,this);
+ m_damageMultipliers[effectNumber] *= multiplier;
+ }
+ }
+ }
+}
+
+void Spell::DoAllEffectOnTarget(GOTargetInfo *target)
+{
+ if (target->processed) // Check target
+ return;
+ target->processed = true; // Target checked in apply effects procedure
+
+ uint32 effectMask = target->effectMask;
+ if(!effectMask)
+ return;
+
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
+ if(!go)
+ return;
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ if (effectMask & (1<<effectNumber))
+ HandleEffects(NULL,NULL,go,effectNumber);
+
+ // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
+ ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
+}
+
+void Spell::DoAllEffectOnTarget(ItemTargetInfo *target)
+{
+ uint32 effectMask = target->effectMask;
+ if(!target->item || !effectMask)
+ return;
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ if (effectMask & (1<<effectNumber))
+ HandleEffects(NULL, target->item, NULL, effectNumber);
+}
+
+bool Spell::IsAliveUnitPresentInTargetList()
+{
+ // Not need check return true
+ if (m_needAliveTargetMask == 0)
+ return true;
+
+ uint8 needAliveTargetMask = m_needAliveTargetMask;
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask) )
+ {
+ Unit *unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+
+ if (unit && unit->isAlive())
+ needAliveTargetMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target
+ }
+ }
+
+ // is all effects from m_needAliveTargetMask have alive targets
+ return needAliveTargetMask==0;
+}
+
+// Helper for Chain Healing
+// Spell target first
+// Raidmates then descending by injury suffered (MaxHealth - Health)
+// Other players/mobs then descending by injury suffered (MaxHealth - Health)
+struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*, bool>
+{
+ const Unit* MainTarget;
+ ChainHealingOrder(Unit const* Target) : MainTarget(Target) {};
+ // functor for operator ">"
+ bool operator()(Unit const* _Left, Unit const* _Right) const
+ {
+ return (ChainHealingHash(_Left) < ChainHealingHash(_Right));
+ }
+ int32 ChainHealingHash(Unit const* Target) const
+ {
+ if (Target == MainTarget)
+ return 0;
+ else if (Target->GetTypeId() == TYPEID_PLAYER && MainTarget->GetTypeId() == TYPEID_PLAYER &&
+ ((Player const*)Target)->IsInSameRaidWith((Player const*)MainTarget))
+ {
+ if (Target->GetHealth() == Target->GetMaxHealth())
+ return 40000;
+ else
+ return 20000 - Target->GetMaxHealth() + Target->GetHealth();
+ }
+ else
+ return 40000 - Target->GetMaxHealth() + Target->GetHealth();
+ }
+};
+
+class ChainHealingFullHealth: std::unary_function<const Unit*, bool>
+{
+ public:
+ const Unit* MainTarget;
+ ChainHealingFullHealth(const Unit* Target) : MainTarget(Target) {};
+
+ bool operator()(const Unit* Target)
+ {
+ return (Target != MainTarget && Target->GetHealth() == Target->GetMaxHealth());
+ }
+};
+
+// Helper for targets nearest to the spell target
+// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case)
+struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit, bool>
+{
+ const Unit* MainTarget;
+ TargetDistanceOrder(const Unit* Target) : MainTarget(Target) {};
+ // functor for operator ">"
+ bool operator()(const Unit* _Left, const Unit* _Right) const
+ {
+ return (MainTarget->GetDistance(_Left) < MainTarget->GetDistance(_Right));
+ }
+};
+
+void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
+{
+ float radius;
+ if (m_spellInfo->EffectRadiusIndex[i])
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ else
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this);
+
+ uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i];
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
+
+ uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
+ switch(cur)
+ {
+ case TARGET_TOTEM_EARTH:
+ case TARGET_TOTEM_WATER:
+ case TARGET_TOTEM_AIR:
+ case TARGET_TOTEM_FIRE:
+ case TARGET_SELF:
+ case TARGET_SELF2:
+ case TARGET_DYNAMIC_OBJECT:
+ case TARGET_AREAEFFECT_CUSTOM:
+ case TARGET_AREAEFFECT_CUSTOM_2:
+ case TARGET_SUMMON:
+ {
+ TagUnitMap.push_back(m_caster);
+ break;
+ }
+ case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA:
+ {
+ m_targets.m_targetMask = 0;
+ unMaxTargets = EffectChainTarget;
+ float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Unit *> tempUnitMap;
+
+ {
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(m_caster, m_caster, max_range);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ if(tempUnitMap.empty())
+ break;
+
+ tempUnitMap.sort(TargetDistanceOrder(m_caster));
+
+ //Now to get us a random target that's in the initial range of the spell
+ uint32 t = 0;
+ std::list<Unit *>::iterator itr = tempUnitMap.begin();
+ while(itr!= tempUnitMap.end() && (*itr)->GetDistance(m_caster) < radius)
+ ++t, ++itr;
+
+ if(!t)
+ break;
+
+ itr = tempUnitMap.begin();
+ std::advance(itr, rand()%t);
+ Unit *pUnitTarget = *itr;
+ TagUnitMap.push_back(pUnitTarget);
+
+ tempUnitMap.erase(itr);
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }break;
+ case TARGET_PET:
+ {
+ Pet* tmpUnit = m_caster->GetPet();
+ if (!tmpUnit) break;
+ TagUnitMap.push_back(tmpUnit);
+ break;
+ }
+ case TARGET_CHAIN_DAMAGE:
+ {
+ if (EffectChainTarget <= 1)
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }
+ else
+ {
+ Unit* pUnitTarget = m_targets.getUnitTarget();
+ if(!pUnitTarget)
+ break;
+
+ unMaxTargets = EffectChainTarget;
+
+ float max_range;
+ if(m_spellInfo->DmgClass==SPELL_DAMAGE_CLASS_MELEE)
+ max_range = radius; //
+ else
+ //FIXME: This very like horrible hack and wrong for most spells
+ max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Unit* originalCaster = GetOriginalCaster();
+ if(originalCaster)
+ {
+ std::list<Unit *> tempUnitMap;
+
+ {
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(pUnitTarget, originalCaster, max_range);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ if(tempUnitMap.empty())
+ break;
+
+ if(*tempUnitMap.begin() == pUnitTarget)
+ tempUnitMap.erase(tempUnitMap.begin());
+
+ TagUnitMap.push_back(pUnitTarget);
+ uint32 t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }
+ }
+ }break;
+ case TARGET_ALL_ENEMY_IN_AREA:
+ {
+ }break;
+ case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
+ {
+ // targets the ground, not the units in the area
+ if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ // exclude caster (this can be important if this not original caster)
+ TagUnitMap.remove(m_caster);
+ }
+ }break;
+ case TARGET_DUELVSPLAYER_COORDINATES:
+ {
+ if(Unit* currentTarget = m_targets.getUnitTarget())
+ {
+ m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
+ TagUnitMap.push_back(currentTarget);
+ }
+ }break;
+ case TARGET_ALL_PARTY_AROUND_CASTER:
+ case TARGET_ALL_PARTY_AROUND_CASTER_2:
+ case TARGET_ALL_PARTY:
+ {
+ Player *pTarget = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself();
+ Group *pGroup = pTarget ? pTarget->GetGroup() : NULL;
+
+ if(pGroup)
+ {
+ uint8 subgroup = pTarget->GetSubGroup();
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target) )
+ {
+ if( m_caster->IsWithinDistInMap(Target, radius) )
+ TagUnitMap.push_back(Target);
+
+ if(Pet* pet = Target->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }
+ }
+ else
+ {
+ Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf();
+ if(ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius))
+ TagUnitMap.push_back(ownerOrSelf);
+ if(Pet* pet = ownerOrSelf->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }break;
+ case TARGET_RANDOM_RAID_MEMBER:
+ {
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(Player* target = ((Player*)m_caster)->GetNextRandomRaidMember(radius))
+ TagUnitMap.push_back(target);
+ }break;
+ case TARGET_SINGLE_FRIEND:
+ case TARGET_SINGLE_FRIEND_2:
+ {
+ if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ }break;
+ case TARGET_NONCOMBAT_PET:
+ {
+ if(Unit* target = m_targets.getUnitTarget())
+ if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET)
+ TagUnitMap.push_back(target);
+ }break;
+ case TARGET_ALL_AROUND_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_ALL_FRIENDLY_UNITS_IN_AREA:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ // TARGET_SINGLE_PARTY means that the spells can only be casted on a party member and not on the caster (some sceals, fire shield from imp, etc..)
+ case TARGET_SINGLE_PARTY:
+ {
+ Unit *target = m_targets.getUnitTarget();
+ // Thoses spells apparently can't be casted on the caster.
+ if( target && target != m_caster)
+ {
+ // Can only be casted on group's members or its pets
+ Group *pGroup = NULL;
+
+ Unit* owner = m_caster->GetCharmerOrOwner();
+ Unit *targetOwner = target->GetCharmerOrOwner();
+ if(owner)
+ {
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( target == owner )
+ {
+ TagUnitMap.push_back(target);
+ break;
+ }
+ pGroup = ((Player*)owner)->GetGroup();
+ }
+ }
+ else if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( targetOwner == m_caster && target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isPet())
+ {
+ TagUnitMap.push_back(target);
+ break;
+ }
+ pGroup = ((Player*)m_caster)->GetGroup();
+ }
+
+ if(pGroup)
+ {
+ // Our target can also be a player's pet who's grouped with us or our pet. But can't be controlled player
+ if(targetOwner)
+ {
+ if( targetOwner->GetTypeId() == TYPEID_PLAYER &&
+ target->GetTypeId()==TYPEID_UNIT && (((Creature*)target)->isPet()) &&
+ target->GetOwnerGUID()==targetOwner->GetGUID() &&
+ pGroup->IsMember(((Player*)targetOwner)->GetGUID()))
+ {
+ TagUnitMap.push_back(target);
+ }
+ }
+ // 1Our target can be a player who is on our group
+ else if (target->GetTypeId() == TYPEID_PLAYER && pGroup->IsMember(((Player*)target)->GetGUID()))
+ {
+ TagUnitMap.push_back(target);
+ }
+ }
+ }
+ }break;
+ case TARGET_GAMEOBJECT:
+ {
+ if(m_targets.getGOTarget())
+ AddGOTarget(m_targets.getGOTarget(), i);
+ }break;
+ case TARGET_IN_FRONT_OF_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ bool inFront = m_spellInfo->SpellVisual != 3879;
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, inFront ? PUSH_IN_FRONT : PUSH_IN_BACK,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_DUELVSPLAYER:
+ {
+ Unit *target = m_targets.getUnitTarget();
+ if(target)
+ {
+ if(m_caster->IsFriendlyTo(target))
+ {
+ TagUnitMap.push_back(target);
+ }
+ else
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }
+ }
+ }break;
+ case TARGET_GAMEOBJECT_ITEM:
+ {
+ if(m_targets.getGOTargetGUID())
+ AddGOTarget(m_targets.getGOTarget(), i);
+ else if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ break;
+ }
+ case TARGET_MASTER:
+ {
+ if(Unit* owner = m_caster->GetCharmerOrOwner())
+ TagUnitMap.push_back(owner);
+ break;
+ }
+ case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
+ {
+ // targets the ground, not the units in the area
+ if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }break;
+ case TARGET_MINION:
+ {
+ if(m_spellInfo->Effect[i] != SPELL_EFFECT_DUEL)
+ TagUnitMap.push_back(m_caster);
+ }break;
+ case TARGET_SINGLE_ENEMY:
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }break;
+ case TARGET_AREAEFFECT_PARTY:
+ {
+ Unit* owner = m_caster->GetCharmerOrOwner();
+ Player *pTarget = NULL;
+
+ if(owner)
+ {
+ TagUnitMap.push_back(m_caster);
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ pTarget = (Player*)owner;
+ }
+ else if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(Unit* target = m_targets.getUnitTarget())
+ {
+ if( target->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(((Creature*)target)->isPet())
+ {
+ Unit *targetOwner = target->GetOwner();
+ if(targetOwner->GetTypeId() == TYPEID_PLAYER)
+ pTarget = (Player*)targetOwner;
+ }
+ }
+ else
+ pTarget = (Player*)target;
+ }
+ }
+
+ Group* pGroup = pTarget ? pTarget->GetGroup() : NULL;
+
+ if(pGroup)
+ {
+ uint8 subgroup = pTarget->GetSubGroup();
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if(Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target))
+ {
+ if( pTarget->IsWithinDistInMap(Target, radius) )
+ TagUnitMap.push_back(Target);
+
+ if(Pet* pet = Target->GetPet())
+ if( pTarget->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }
+ }
+ else if (owner)
+ {
+ if(m_caster->IsWithinDistInMap(owner, radius))
+ TagUnitMap.push_back(owner);
+ }
+ else if(pTarget)
+ {
+ TagUnitMap.push_back(pTarget);
+
+ if(Pet* pet = pTarget->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+
+ }break;
+ case TARGET_SCRIPT:
+ {
+ if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ }break;
+ case TARGET_SELF_FISHING:
+ {
+ TagUnitMap.push_back(m_caster);
+ }break;
+ case TARGET_CHAIN_HEAL:
+ {
+ Unit* pUnitTarget = m_targets.getUnitTarget();
+ if(!pUnitTarget)
+ break;
+
+ if (EffectChainTarget <= 1)
+ TagUnitMap.push_back(pUnitTarget);
+ else
+ {
+ unMaxTargets = EffectChainTarget;
+ float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ std::list<Unit *> tempUnitMap;
+
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, tempUnitMap, max_range, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ }
+
+ if(m_caster != pUnitTarget && std::find(tempUnitMap.begin(),tempUnitMap.end(),m_caster) == tempUnitMap.end() )
+ tempUnitMap.push_front(m_caster);
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ if(tempUnitMap.empty())
+ break;
+
+ if(*tempUnitMap.begin() == pUnitTarget)
+ tempUnitMap.erase(tempUnitMap.begin());
+
+ TagUnitMap.push_back(pUnitTarget);
+ uint32 t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ if((*next)->GetHealth() == (*next)->GetMaxHealth())
+ {
+ next = tempUnitMap.erase(next);
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }
+ }break;
+ case TARGET_CURRENT_ENEMY_COORDINATES:
+ {
+ Unit* currentTarget = m_targets.getUnitTarget();
+ if(currentTarget)
+ {
+ TagUnitMap.push_back(currentTarget);
+ m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
+ if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA_INSTANT)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(currentTarget->GetPositionX(), currentTarget->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_TARGET_CENTER, SPELL_TARGETS_AOE_DAMAGE);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }
+ }break;
+ case TARGET_AREAEFFECT_PARTY_AND_CLASS:
+ {
+ Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER
+ ? (Player*)m_targets.getUnitTarget() : NULL;
+
+ Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL;
+ if(pGroup)
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && targetPlayer->IsWithinDistInMap(Target, radius) &&
+ targetPlayer->getClass() == Target->getClass() &&
+ !m_caster->IsHostileTo(Target) )
+ {
+ TagUnitMap.push_back(Target);
+ }
+ }
+ }
+ else if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ {
+ SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
+ if(st)
+ {
+ if (st->target_mapId == m_caster->GetMapId())
+ m_targets.setDestination(st->target_X, st->target_Y, st->target_Z);
+
+ // if B==TARGET_TABLE_X_Y_Z_COORDINATES then A already fill all required targets
+ if (m_spellInfo->EffectImplicitTargetB[i] && m_spellInfo->EffectImplicitTargetB[i]!=TARGET_TABLE_X_Y_Z_COORDINATES)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ SpellTargets targetB = SPELL_TARGETS_AOE_DAMAGE;
+ // Select friendly targets for positive effect
+ if (IsPositiveEffect(m_spellInfo->Id, i))
+ targetB = SPELL_TARGETS_FRIENDLY;
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_DEST_CENTER, targetB);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }
+ else
+ sLog.outError( "SPELL: unknown target coordinates for spell ID %u\n", m_spellInfo->Id );
+ }break;
+ case TARGET_BEHIND_VICTIM:
+ {
+ Unit *pTarget = m_caster->getVictim();
+ if(!pTarget && m_caster->GetTypeId() == TYPEID_PLAYER)
+ pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+
+ if(pTarget)
+ {
+ float _target_x, _target_y, _target_z;
+ pTarget->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectSize(), CONTACT_DISTANCE, M_PI);
+ if(pTarget->IsWithinLOS(_target_x,_target_y,_target_z))
+ m_targets.setDestination(_target_x, _target_y, _target_z);
+ }
+ }break;
+ default:
+ break;
+ }
+
+ if (unMaxTargets && TagUnitMap.size() > unMaxTargets)
+ {
+ // make sure one unit is always removed per iteration
+ uint32 removed_utarget = 0;
+ for (std::list<Unit*>::iterator itr = TagUnitMap.begin(), next; itr != TagUnitMap.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+ if (!*itr) continue;
+ if ((*itr) == m_targets.getUnitTarget())
+ {
+ TagUnitMap.erase(itr);
+ removed_utarget = 1;
+ // break;
+ }
+ }
+ // remove random units from the map
+ while (TagUnitMap.size() > unMaxTargets - removed_utarget)
+ {
+ uint32 poz = urand(0, TagUnitMap.size()-1);
+ for (std::list<Unit*>::iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end(); ++itr, --poz)
+ {
+ if (!*itr) continue;
+ if (!poz)
+ {
+ TagUnitMap.erase(itr);
+ break;
+ }
+ }
+ }
+ // the player's target will always be added to the map
+ if (removed_utarget && m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ }
+}
+
+void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
+{
+ m_targets = *targets;
+
+ m_spellState = SPELL_STATE_PREPARING;
+
+ m_castPositionX = m_caster->GetPositionX();
+ m_castPositionY = m_caster->GetPositionY();
+ m_castPositionZ = m_caster->GetPositionZ();
+ m_castOrientation = m_caster->GetOrientation();
+
+ if(triggeredByAura)
+ m_triggeredByAuraSpell = triggeredByAura->GetSpellProto();
+
+ // create and add update event for this spell
+ SpellEvent* Event = new SpellEvent(this);
+ m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1));
+
+ //Prevent casting at cast another spell (ServerSide check)
+ if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count)
+ {
+ SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS);
+ finish(false);
+ return;
+ }
+
+ // Fill cost data
+ m_powerCost = CalculatePowerCost();
+
+ uint8 result = CanCast(true);
+ if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
+ {
+ if(triggeredByAura)
+ {
+ SendChannelUpdate(0);
+ triggeredByAura->SetAuraDuration(0);
+ }
+ SendCastResult(result);
+ finish(false);
+ return;
+ }
+
+ // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail)
+ m_casttime = GetSpellCastTime(m_spellInfo, this);
+
+ // set timer base at cast time
+ ReSetTimer();
+
+ // 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 ( !m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) )
+ {
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+ }
+
+ if(m_IsTriggeredSpell)
+ cast(true);
+ else
+ {
+ m_caster->SetCurrentCastedSpell( this );
+ m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]);
+ SendSpellStart();
+ }
+}
+
+void Spell::cancel()
+{
+ if(m_spellState == SPELL_STATE_FINISHED)
+ return;
+
+ m_autoRepeat = false;
+ switch (m_spellState)
+ {
+ case SPELL_STATE_PREPARING:
+ case SPELL_STATE_DELAYED:
+ {
+ SendInterrupted(0);
+ SendCastResult(SPELL_FAILED_INTERRUPTED);
+ } break;
+
+ case SPELL_STATE_CASTING:
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition == SPELL_MISS_NONE )
+ {
+ Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if( unit && unit->isAlive() )
+ unit->RemoveAurasDueToSpell(m_spellInfo->Id);
+ }
+ }
+
+ m_caster->RemoveAurasDueToSpell(m_spellInfo->Id);
+ SendChannelUpdate(0);
+ SendInterrupted(0);
+ SendCastResult(SPELL_FAILED_INTERRUPTED);
+ } break;
+
+ default:
+ {
+ } break;
+ }
+
+ finish(false);
+ m_caster->RemoveDynObject(m_spellInfo->Id);
+ m_caster->RemoveGameObject(m_spellInfo->Id,true);
+}
+
+void Spell::cast(bool skipCheck)
+{
+ uint8 castResult = 0;
+
+ // update pointers base at GUIDs to prevent access to non-existed already object
+ UpdatePointers();
+
+ // cancel at lost main target unit
+ if(!m_targets.getUnitTarget() && m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID())
+ {
+ cancel();
+ return;
+ }
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster)
+ m_caster->SetInFront(m_targets.getUnitTarget());
+
+ castResult = CheckPower();
+ if(castResult != 0)
+ {
+ SendCastResult(castResult);
+ finish(false);
+ return;
+ }
+
+ // triggered cast called from Spell::prepare where it was already checked
+ if(!skipCheck)
+ {
+ castResult = CanCast(false);
+ if(castResult != 0)
+ {
+ SendCastResult(castResult);
+ finish(false);
+ return;
+ }
+ }
+
+ // Conflagrate - consumes immolate
+ if ((m_spellInfo->TargetAuraState == AURA_STATE_IMMOLATE) && m_targets.getUnitTarget())
+ {
+ // for caster applied auras only
+ Unit::AuraList const &mPeriodic = m_targets.getUnitTarget()->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i)
+ {
+ if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags & 4) &&
+ (*i)->GetCasterGUID()==m_caster->GetGUID() )
+ {
+ m_targets.getUnitTarget()->RemoveAura((*i)->GetId(), (*i)->GetEffIndex());
+ break;
+ }
+ }
+ }
+
+ // traded items have trade slot instead of guid in m_itemTargetGUID
+ // set to real guid to be sent later to the client
+ m_targets.updateTradeSlotItem();
+
+ // CAST SPELL
+ SendSpellCooldown();
+
+ TakePower();
+ TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
+ FillTargetMap();
+
+ if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap
+ return;
+
+ SendCastResult(castResult);
+ SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
+
+ // Pass cast spell event to handler (not send triggered by aura spells)
+ if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_triggeredByAuraSpell)
+ {
+ m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_CAST_SPELL, PROC_FLAG_NONE, 0, SPELL_SCHOOL_MASK_NONE, m_spellInfo, m_IsTriggeredSpell);
+
+ // update pointers base at GUIDs to prevent access to non-existed already object
+ UpdatePointers(); // pointers can be invalidate at triggered spell casting
+ }
+
+ // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
+ if (m_spellInfo->speed > 0.0f)
+ {
+
+ // Remove used for cast item if need (it can be already NULL after TakeReagents call
+ // in case delayed spell remove item at cast delay start
+ TakeCastItem();
+
+ // Okay, maps created, now prepare flags
+ m_immediateHandled = false;
+ m_spellState = SPELL_STATE_DELAYED;
+ SetDelayStart(0);
+ }
+ else
+ {
+ // Immediate spell, no big deal
+ handle_immediate();
+ }
+}
+
+void Spell::handle_immediate()
+{
+ // start channeling if applicable
+ if(IsChanneledSpell(m_spellInfo))
+ {
+ m_spellState = SPELL_STATE_CASTING;
+ SendChannelStart(GetSpellDuration(m_spellInfo));
+ }
+
+ // process immediate effects (items, ground, etc.) also initialize some variables
+ _handle_immediate_phase();
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ // spell is finished, perform some last features of the spell here
+ _handle_finish_phase();
+
+ // Remove used for cast item if need (it can be already NULL after TakeReagents call
+ TakeCastItem();
+
+ if(m_spellState != SPELL_STATE_CASTING)
+ finish(true); // successfully finish spell cast (not last in case autorepeat or channel spell)
+}
+
+uint64 Spell::handle_delayed(uint64 t_offset)
+{
+ uint64 next_time = 0;
+
+ if (!m_immediateHandled)
+ {
+ _handle_immediate_phase();
+ m_immediateHandled = true;
+ }
+
+ // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases)
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if (ihit->processed == false)
+ {
+ if ( ihit->timeDelay <= t_offset )
+ DoAllEffectOnTarget(&(*ihit));
+ else if( next_time == 0 || ihit->timeDelay < next_time )
+ next_time = ihit->timeDelay;
+ }
+ }
+
+ // now recheck gameobject targeting correctness
+ for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end();++ighit)
+ {
+ if (ighit->processed == false)
+ {
+ if ( ighit->timeDelay <= t_offset )
+ DoAllEffectOnTarget(&(*ighit));
+ else if( next_time == 0 || ighit->timeDelay < next_time )
+ next_time = ighit->timeDelay;
+ }
+ }
+ // All targets passed - need finish phase
+ if (next_time == 0)
+ {
+ // spell is finished, perform some last features of the spell here
+ _handle_finish_phase();
+
+ finish(true); // successfully finish spell cast
+
+ // return zero, spell is finished now
+ return 0;
+ }
+ else
+ {
+ // spell is unfinished, return next execution time
+ return next_time;
+ }
+}
+
+void Spell::_handle_immediate_phase()
+{
+ // handle some immediate features of the spell here
+ HandleThreatSpells(m_spellInfo->Id);
+
+ m_needSpellLog = IsNeedSendToClient();
+ for(uint32 j = 0;j<3;j++)
+ {
+ if(m_spellInfo->Effect[j]==0)
+ continue;
+
+ // apply Send Event effect to ground in case empty target lists
+ if( m_spellInfo->Effect[j] == SPELL_EFFECT_SEND_EVENT && !HaveTargetsForEffect(j) )
+ {
+ HandleEffects(NULL,NULL,NULL, j);
+ continue;
+ }
+
+ // Don't do spell log, if is school damage spell
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || m_spellInfo->Effect[j] == 0)
+ m_needSpellLog = false;
+
+ uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[j];
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
+
+ // initialize multipliers
+ m_damageMultipliers[j] = 1.0f;
+ if( (m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_HEAL) &&
+ (EffectChainTarget > 1) )
+ m_applyMultiplierMask |= 1 << j;
+ }
+
+ // initialize Diminishing Returns Data
+ m_diminishLevel = DIMINISHING_LEVEL_1;
+ m_diminishGroup = DIMINISHING_NONE;
+
+ // process items
+ for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ // process ground
+ for(uint32 j = 0;j<3;j++)
+ {
+ // persistent area auras target only the ground
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ HandleEffects(NULL,NULL,NULL, j);
+ }
+}
+
+void Spell::_handle_finish_phase()
+{
+ // spell log
+ if(m_needSpellLog)
+ SendLogExecute();
+}
+
+void Spell::SendSpellCooldown()
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* _player = (Player*)m_caster;
+ // Add cooldown for max (disable spell)
+ // Cooldown started on SendCooldownEvent call
+ if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ {
+ _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1);
+ return;
+ }
+
+ // init cooldown values
+ uint32 cat = 0;
+ int32 rec = -1;
+ int32 catrec = -1;
+
+ // some special item spells without correct cooldown in SpellInfo
+ // cooldown information stored in item prototype
+ // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
+
+ if(m_CastItem)
+ {
+ ItemPrototype const* proto = m_CastItem->GetProto();
+ if(proto)
+ {
+ for(int idx = 0; idx < 5; ++idx)
+ {
+ if(proto->Spells[idx].SpellId == m_spellInfo->Id)
+ {
+ cat = proto->Spells[idx].SpellCategory;
+ rec = proto->Spells[idx].SpellCooldown;
+ catrec = proto->Spells[idx].SpellCategoryCooldown;
+ break;
+ }
+ }
+ }
+ }
+
+ // if no cooldown found above then base at DBC data
+ if(rec < 0 && catrec < 0)
+ {
+ cat = m_spellInfo->Category;
+ rec = m_spellInfo->RecoveryTime;
+ catrec = m_spellInfo->CategoryRecoveryTime;
+ }
+
+ // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
+ // prevent 0 cooldowns set by another way
+ if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351))
+ rec = _player->GetAttackTime(RANGED_ATTACK);
+
+ // Now we have cooldown data (if found any), time to apply mods
+ if(rec > 0)
+ _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this);
+
+ if(catrec > 0)
+ _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this);
+
+ // replace negative cooldowns by 0
+ if (rec < 0) rec = 0;
+ if (catrec < 0) catrec = 0;
+
+ // no cooldown after applying spell mods
+ if( rec == 0 && catrec == 0)
+ return;
+
+ time_t curTime = time(NULL);
+
+ time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs
+ time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs
+
+ // self spell cooldown
+ if(recTime > 0)
+ _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime);
+
+ // category spells
+ if (catrec > 0)
+ {
+ SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
+ if(i_scstore != sSpellCategoryStore.end())
+ {
+ for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
+ {
+ if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above
+ continue;
+
+ _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime);
+ }
+ }
+ }
+}
+
+void Spell::update(uint32 difftime)
+{
+ // update pointers based at it's GUIDs
+ UpdatePointers();
+
+ if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget())
+ {
+ cancel();
+ return;
+ }
+
+ // check if the player caster has moved before the spell finished
+ if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
+ (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) &&
+ (m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)))
+ {
+ // always cancel for channeled spells
+ if( m_spellState == SPELL_STATE_CASTING )
+ cancel();
+ // don't cancel for melee, autorepeat, triggered and instant spells
+ else if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
+ cancel();
+ }
+
+ switch(m_spellState)
+ {
+ case SPELL_STATE_PREPARING:
+ {
+ if(m_timer)
+ {
+ if(difftime >= m_timer)
+ m_timer = 0;
+ else
+ m_timer -= difftime;
+ }
+
+ if(m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat())
+ cast();
+ } break;
+ case SPELL_STATE_CASTING:
+ {
+ if(m_timer > 0)
+ {
+ if( m_caster->GetTypeId() == TYPEID_PLAYER )
+ {
+ // check if player has jumped before the channeling finished
+ if(m_caster->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING))
+ cancel();
+
+ // check for incapacitating player states
+ if( m_caster->hasUnitState(UNIT_STAT_STUNDED | UNIT_STAT_CONFUSED))
+ cancel();
+
+ // check if player has turned if flag is set
+ if( m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_TURNING && m_castOrientation != m_caster->GetOrientation() )
+ cancel();
+ }
+
+ // check if there are alive targets left
+ if (!IsAliveUnitPresentInTargetList())
+ {
+ SendChannelUpdate(0);
+ finish();
+ }
+
+ if(difftime >= m_timer)
+ m_timer = 0;
+ else
+ m_timer -= difftime;
+ }
+
+ if(m_timer == 0)
+ {
+ SendChannelUpdate(0);
+
+ // channeled spell processed independently for quest targeting
+ // cast at creature (or GO) quest objectives update at successful cast channel finished
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() )
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ TargetInfo* target = &*ihit;
+ if(!IS_CREATURE_GUID(target->targetGUID))
+ continue;
+
+ Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
+ if (unit==NULL)
+ continue;
+
+ ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
+ }
+
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ {
+ GOTargetInfo* target = &*ihit;
+
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
+ if(!go)
+ continue;
+
+ ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
+ }
+ }
+
+ finish();
+ }
+ } break;
+ default:
+ {
+ }break;
+ }
+}
+
+void Spell::finish(bool ok)
+{
+ if(!m_caster)
+ return;
+
+ if(m_spellState == SPELL_STATE_FINISHED)
+ return;
+
+ m_spellState = SPELL_STATE_FINISHED;
+
+ //remove spell mods
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->RemoveSpellMods(this);
+
+ // other code related only to successfully finished spells
+ if(!ok)
+ return;
+
+ //handle SPELL_AURA_ADD_TARGET_TRIGGER auras
+ Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
+ for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
+ {
+ SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
+ uint32 auraSpellIdx = (*i)->GetEffIndex();
+ if (IsAffectedBy(auraSpellInfo, auraSpellIdx))
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if( ihit->effectMask & (1<<auraSpellIdx) )
+ {
+ // check m_caster->GetGUID() let load auras at login and speedup most often case
+ Unit *unit = m_caster->GetGUID()== ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if (unit && unit->isAlive())
+ {
+ // Calculate chance at that moment (can be depend for example from combo points)
+ int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(),unit);
+
+ if(roll_chance_i(chance))
+ m_caster->CastSpell(unit, auraSpellInfo->EffectTriggerSpell[auraSpellIdx], true, NULL, (*i));
+ }
+ }
+ }
+ }
+
+ if (IsMeleeAttackResetSpell())
+ {
+ m_caster->resetAttackTimer(BASE_ATTACK);
+ if(m_caster->haveOffhandWeapon())
+ m_caster->resetAttackTimer(OFF_ATTACK);
+ }
+
+ /*if (IsRangedAttackResetSpell())
+ m_caster->resetAttackTimer(RANGED_ATTACK);*/
+
+ // Clear combo at finish state
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && NeedsComboPoints(m_spellInfo))
+ ((Player*)m_caster)->ClearComboPoints();
+
+ // call triggered spell only at successful cast (after clear combo points -> for add some if need)
+ if(!m_TriggerSpells.empty())
+ TriggerSpell();
+
+ // Stop Attack for some spells
+ if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET )
+ m_caster->AttackStop();
+}
+
+void Spell::SendCastResult(uint8 result)
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time
+ return;
+
+ if(result != 0)
+ {
+ WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
+ data << uint32(m_spellInfo->Id);
+ data << uint8(result); // problem
+ data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
+ switch (result)
+ {
+ case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
+ data << uint32(m_spellInfo->RequiresSpellFocus);
+ break;
+ case SPELL_FAILED_REQUIRES_AREA:
+ // hardcode areas limitation case
+ if( m_spellInfo->Id==41618 || m_spellInfo->Id==41620 )
+ data << uint32(3842);
+ else if( m_spellInfo->Id==41617 || m_spellInfo->Id==41619 )
+ data << uint32(3905);
+ // normal case
+ else
+ data << uint32(m_spellInfo->AreaId);
+ break;
+ case SPELL_FAILED_TOTEMS:
+ if(m_spellInfo->Totem[0])
+ data << uint32(m_spellInfo->Totem[0]);
+ if(m_spellInfo->Totem[1])
+ data << uint32(m_spellInfo->Totem[1]);
+ break;
+ case SPELL_FAILED_TOTEM_CATEGORY:
+ if(m_spellInfo->TotemCategory[0])
+ data << uint32(m_spellInfo->TotemCategory[0]);
+ if(m_spellInfo->TotemCategory[1])
+ data << uint32(m_spellInfo->TotemCategory[1]);
+ break;
+ case SPELL_FAILED_EQUIPPED_ITEM_CLASS:
+ data << uint32(m_spellInfo->EquippedItemClass);
+ data << uint32(m_spellInfo->EquippedItemSubClassMask);
+ data << uint32(m_spellInfo->EquippedItemInventoryTypeMask);
+ break;
+ }
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ else
+ {
+ WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4));
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+}
+
+void Spell::SendSpellStart()
+{
+ if(!IsNeedSendToClient())
+ return;
+
+ sLog.outDebug("Sending SMSG_SPELL_START id=%u",m_spellInfo->Id);
+
+ uint16 castFlags = CAST_FLAG_UNKNOWN1;
+ if(IsRangedSpell())
+ castFlags |= CAST_FLAG_AMMO;
+
+ Unit * target;
+ if(!m_targets.getUnitTarget())
+ target = m_caster;
+ else
+ target = m_targets.getUnitTarget();
+
+ WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
+ if(m_CastItem)
+ data.append(m_CastItem->GetPackGUID());
+ else
+ data.append(m_caster->GetPackGUID());
+
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
+ data << uint16(castFlags);
+ data << uint32(m_timer);
+
+ m_targets.write(&data);
+
+ if( castFlags & CAST_FLAG_AMMO )
+ WriteAmmoToPacket(&data);
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendSpellGo()
+{
+ // not send invisible spell casting
+ if(!IsNeedSendToClient())
+ return;
+
+ sLog.outDebug("Sending SMSG_SPELL_GO id=%u",m_spellInfo->Id);
+
+ Unit * target;
+ if(!m_targets.getUnitTarget())
+ target = m_caster;
+ else
+ target = m_targets.getUnitTarget();
+
+ uint16 castFlags = CAST_FLAG_UNKNOWN3;
+ if(IsRangedSpell())
+ castFlags |= CAST_FLAG_AMMO;
+
+ WorldPacket data(SMSG_SPELL_GO, 50); // guess size
+ if(m_CastItem)
+ data.append(m_CastItem->GetPackGUID());
+ else
+ data.append(m_caster->GetPackGUID());
+
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ data << uint16(castFlags);
+ data << uint32(getMSTime()); // timestamp
+
+ WriteSpellGoTargets(&data);
+
+ m_targets.write(&data);
+
+ if( castFlags & CAST_FLAG_AMMO )
+ WriteAmmoToPacket(&data);
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::WriteAmmoToPacket( WorldPacket * data )
+{
+ uint32 ammoInventoryType = 0;
+ uint32 ammoDisplayID = 0;
+
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
+ if(pItem)
+ {
+ ammoInventoryType = pItem->GetProto()->InventoryType;
+ if( ammoInventoryType == INVTYPE_THROWN )
+ ammoDisplayID = pItem->GetProto()->DisplayInfoID;
+ else
+ {
+ uint32 ammoID = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
+ if(ammoID)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( ammoID );
+ if(pProto)
+ {
+ ammoDisplayID = pProto->DisplayInfoID;
+ ammoInventoryType = pProto->InventoryType;
+ }
+ }
+ else if(m_caster->GetDummyAura(46699)) // Requires No Ammo
+ {
+ ammoDisplayID = 5996; // normal arrow
+ ammoInventoryType = INVTYPE_AMMO;
+ }
+ }
+ }
+ }
+ // TODO: implement selection ammo data based at ranged weapon stored in equipmodel/equipinfo/equipslot fields
+
+ *data << uint32(ammoDisplayID);
+ *data << uint32(ammoInventoryType);
+}
+
+void Spell::WriteSpellGoTargets( WorldPacket * data )
+{
+ *data << (uint8)m_countOfHit;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits
+ *data << uint64(ihit->targetGUID);
+
+ for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit)
+ *data << uint64(ighit->targetGUID); // Always hits
+
+ *data << (uint8)m_countOfMiss;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss
+ {
+ *data << uint64(ihit->targetGUID);
+ *data << uint8(ihit->missCondition);
+ if( ihit->missCondition == SPELL_MISS_REFLECT )
+ *data << uint8(ihit->reflectResult);
+ }
+ }
+}
+
+void Spell::SendLogExecute()
+{
+ Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster;
+
+ WorldPacket data(SMSG_SPELLLOGEXECUTE, (8+4+4+4+4+8));
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ data.append(m_caster->GetPackGUID());
+ else
+ data.append(target->GetPackGUID());
+
+ data << uint32(m_spellInfo->Id);
+ uint32 count1 = 1;
+ data << uint32(count1); // count1 (effect count?)
+ for(uint32 i = 0; i < count1; ++i)
+ {
+ data << uint32(m_spellInfo->Effect[0]); // spell effect?
+ uint32 count2 = 1;
+ data << uint32(count2); // count2 (target count?)
+ for(uint32 j = 0; j < count2; ++j)
+ {
+ switch(m_spellInfo->Effect[0])
+ {
+ case SPELL_EFFECT_POWER_DRAIN:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << float(0);
+ break;
+ case SPELL_EFFECT_ADD_EXTRA_ATTACKS:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0); // count?
+ break;
+ case SPELL_EFFECT_INTERRUPT_CAST:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0); // spellid
+ break;
+ case SPELL_EFFECT_DURABILITY_DAMAGE:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0);
+ data << uint32(0);
+ break;
+ case SPELL_EFFECT_OPEN_LOCK:
+ case SPELL_EFFECT_OPEN_LOCK_ITEM:
+ if(Item *item = m_targets.getItemTarget())
+ data.append(item->GetPackGUID());
+ else
+ data << uint8(0);
+ break;
+ case SPELL_EFFECT_CREATE_ITEM:
+ data << uint32(m_spellInfo->EffectItemType[0]);
+ break;
+ case SPELL_EFFECT_SUMMON:
+ case SPELL_EFFECT_SUMMON_WILD:
+ case SPELL_EFFECT_SUMMON_GUARDIAN:
+ case SPELL_EFFECT_TRANS_DOOR:
+ case SPELL_EFFECT_SUMMON_PET:
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_TOTEM:
+ case SPELL_EFFECT_SUMMON_OBJECT_WILD:
+ case SPELL_EFFECT_CREATE_HOUSE:
+ case SPELL_EFFECT_DUEL:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT1:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT2:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT3:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT4:
+ case SPELL_EFFECT_SUMMON_PHANTASM:
+ case SPELL_EFFECT_SUMMON_CRITTER:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT1:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT2:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT3:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT4:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ case SPELL_EFFECT_150:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else if(m_targets.getItemTargetGUID())
+ data.appendPackGUID(m_targets.getItemTargetGUID());
+ else if(GameObject *go = m_targets.getGOTarget())
+ data.append(go->GetPackGUID());
+ else
+ data << uint8(0); // guid
+ break;
+ case SPELL_EFFECT_FEED_PET:
+ data << uint32(m_targets.getItemTargetEntry());
+ break;
+ case SPELL_EFFECT_DISMISS_PET:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendInterrupted(uint8 result)
+{
+ WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1));
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ data << result;
+ m_caster->SendMessageToSet(&data, true);
+
+ data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4));
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendChannelUpdate(uint32 time)
+{
+ if(time == 0)
+ {
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,0);
+ m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL,0);
+ }
+
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data( MSG_CHANNEL_UPDATE, 8+4 );
+ data.append(m_caster->GetPackGUID());
+ data << time;
+
+ ((Player*)m_caster)->GetSession()->SendPacket( &data );
+}
+
+void Spell::SendChannelStart(uint32 duration)
+{
+ WorldObject* target = NULL;
+
+ // select first not rsusted target from target list for _0_ effect
+ if(!m_UniqueTargetInfo.empty())
+ {
+ for(std::list<TargetInfo>::iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
+ {
+ if( (itr->effectMask & (1<<0)) && itr->reflectResult==SPELL_MISS_NONE && itr->targetGUID != m_caster->GetGUID())
+ {
+ target = ObjectAccessor::GetUnit(*m_caster, itr->targetGUID);
+ break;
+ }
+ }
+ }
+ else if(!m_UniqueGOTargetInfo.empty())
+ {
+ for(std::list<GOTargetInfo>::iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
+ {
+ if(itr->effectMask & (1<<0) )
+ {
+ target = ObjectAccessor::GetGameObject(*m_caster, itr->targetGUID);
+ break;
+ }
+ }
+ }
+
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data( MSG_CHANNEL_START, (8+4+4) );
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ data << duration;
+
+ ((Player*)m_caster)->GetSession()->SendPacket( &data );
+ }
+
+ m_timer = duration;
+ if(target)
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, target->GetGUID());
+ m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id);
+}
+
+void Spell::SendResurrectRequest(Player* target)
+{
+ WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4));
+ data << m_caster->GetGUID();
+ data << uint32(1) << uint16(0) << uint32(1);
+
+ target->GetSession()->SendPacket(&data);
+}
+
+void Spell::SendPlaySpellVisual(uint32 SpellID)
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12);
+ data << m_caster->GetGUID();
+ data << SpellID;
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+}
+
+void Spell::TakeCastItem()
+{
+ if(!m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // not remove cast item at triggered spell (equipping, weapon damage, etc)
+ if(m_IsTriggeredSpell)
+ return;
+
+ ItemPrototype const *proto = m_CastItem->GetProto();
+
+ if(!proto)
+ {
+ // This code is to avoid a crash
+ // I'm not sure, if this is really an error, but I guess every item needs a prototype
+ sLog.outError("Cast item has no item prototype highId=%d, lowId=%d",m_CastItem->GetGUIDHigh(), m_CastItem->GetGUIDLow());
+ return;
+ }
+
+ bool expendable = false;
+ bool withoutCharges = false;
+
+ for (int i = 0; i<5; i++)
+ {
+ if (proto->Spells[i].SpellId)
+ {
+ // item has limited charges
+ if (proto->Spells[i].SpellCharges)
+ {
+ if (proto->Spells[i].SpellCharges < 0)
+ expendable = true;
+
+ int32 charges = m_CastItem->GetSpellCharges(i);
+
+ // item has charges left
+ if (charges)
+ {
+ (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use
+ if (proto->Stackable < 2)
+ m_CastItem->SetSpellCharges(i, charges);
+ m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster);
+ }
+
+ // all charges used
+ withoutCharges = (charges == 0);
+ }
+ }
+ }
+
+ if (expendable && withoutCharges)
+ {
+ uint32 count = 1;
+ ((Player*)m_caster)->DestroyItemCount(m_CastItem, count, true);
+
+ // prevent crash at access to deleted m_targets.getItemTarget
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+ }
+}
+
+void Spell::TakePower()
+{
+ if(m_CastItem || m_triggeredByAuraSpell)
+ return;
+
+ // health as power used
+ if(m_spellInfo->powerType == POWER_HEALTH)
+ {
+ m_caster->ModifyHealth( -(int32)m_powerCost );
+ return;
+ }
+
+ if(m_spellInfo->powerType >= MAX_POWERS)
+ {
+ sLog.outError("Spell::TakePower: Unknown power type '%d'", m_spellInfo->powerType);
+ return;
+ }
+
+ Powers powerType = Powers(m_spellInfo->powerType);
+
+ m_caster->ModifyPower(powerType, -(int32)m_powerCost);
+
+ // Set the five second timer
+ if (powerType == POWER_MANA && m_powerCost > 0)
+ m_caster->SetLastManaUse(getMSTime());
+}
+
+void Spell::TakeReagents()
+{
+ if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
+ return;
+
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
+ m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ for(uint32 x=0;x<8;x++)
+ {
+ if(m_spellInfo->Reagent[x] <= 0)
+ continue;
+
+ uint32 itemid = m_spellInfo->Reagent[x];
+ uint32 itemcount = m_spellInfo->ReagentCount[x];
+
+ // if CastItem is also spell reagent
+ if (m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if( proto && proto->ItemId == itemid )
+ {
+ for(int s=0;s<5;s++)
+ {
+ // CastItem will be used up and does not count as reagent
+ int32 charges = m_CastItem->GetSpellCharges(s);
+ if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
+ {
+ ++itemcount;
+ break;
+ }
+ }
+
+ m_CastItem = NULL;
+ }
+ }
+
+ // if getItemTarget is also spell reagent
+ if (m_targets.getItemTargetEntry()==itemid)
+ m_targets.setItemTarget(NULL);
+
+ p_caster->DestroyItemCount(itemid, itemcount, true);
+ }
+}
+
+void Spell::HandleThreatSpells(uint32 spellId)
+{
+ if(!m_targets.getUnitTarget() || !spellId)
+ return;
+
+ if(!m_targets.getUnitTarget()->CanHaveThreatList())
+ return;
+
+ SpellThreatEntry const *threatSpell = sSpellThreatStore.LookupEntry<SpellThreatEntry>(spellId);
+ if(!threatSpell)
+ return;
+
+ m_targets.getUnitTarget()->AddThreat(m_caster, float(threatSpell->threat));
+
+ DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threatSpell->threat);
+}
+
+void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier)
+{
+ unitTarget = pUnitTarget;
+ itemTarget = pItemTarget;
+ gameObjTarget = pGOTarget;
+
+ uint8 eff = m_spellInfo->Effect[i];
+ uint32 mechanic = m_spellInfo->EffectMechanic[i];
+
+ damage = int32(CalculateDamage((uint8)i,unitTarget)*DamageMultiplier);
+
+ sLog.outDebug( "Spell: Effect : %u", eff);
+
+ //Simply return. Do not display "immune" in red text on client
+ if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic))
+ return;
+
+ if(eff<TOTAL_SPELL_EFFECTS)
+ {
+ //sLog.outDebug( "WORLD: Spell FX %d < TOTAL_SPELL_EFFECTS ", eff);
+ (*this.*SpellEffects[eff])(i);
+ }
+ /*
+ else
+ {
+ sLog.outDebug( "WORLD: Spell FX %d > TOTAL_SPELL_EFFECTS ", eff);
+ if (m_CastItem)
+ EffectEnchantItemTmp(i);
+ else
+ {
+ sLog.outError("SPELL: unknown effect %u spell id %u\n",
+ eff, m_spellInfo->Id);
+ }
+ }
+ */
+}
+
+void Spell::TriggerSpell()
+{
+ for(TriggerSpells::iterator si=m_TriggerSpells.begin(); si!=m_TriggerSpells.end(); ++si)
+ {
+ Spell* spell = new Spell(m_caster, (*si), true, m_originalCasterGUID, this->m_selfContainer);
+ spell->prepare(&m_targets); // use original spell original targets
+ }
+}
+
+uint8 Spell::CanCast(bool strict)
+{
+ // check cooldowns to prevent cheating
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && ((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
+ if( !m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(BattleGround * bg = ((Player*)m_caster)->GetBattleGround())
+ if(bg->GetStatus() == STATUS_WAIT_LEAVE)
+ return SPELL_FAILED_DONT_REPORT;
+
+ // only check at first call, Stealth auras are already removed at second call
+ // for now, ignore triggered spells
+ if( strict && !m_IsTriggeredSpell)
+ {
+ // Cannot be used in this stance/form
+ if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form))
+ return shapeError;
+
+ if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
+ return SPELL_FAILED_ONLY_STEALTHED;
+ }
+
+ // caster state requirements
+ if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState)))
+ return SPELL_FAILED_CASTER_AURASTATE;
+ if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot)))
+ return SPELL_FAILED_CASTER_AURASTATE;
+
+ // cancel autorepeat spells if cast start when moving
+ // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
+ if( m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->isMoving() )
+ {
+ // skip stuck spell to allow use it in falling case and apply spell limitations at movement
+ if( (!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) || m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK) &&
+ (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0) )
+ return SPELL_FAILED_MOVING;
+ }
+
+ Unit *target = m_targets.getUnitTarget();
+
+ if(target)
+ {
+ // target state requirements (not allowed state), apply to self also
+ if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot)))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+
+ if(target != m_caster)
+ {
+ // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds
+ if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState)))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+ // Not allow casting on flying player
+ if (target->isInFlight())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(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;
+ }
+ }
+ }
+
+ // check pet presents
+ for(int j=0;j<3;j++)
+ {
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET)
+ {
+ target = m_caster->GetPet();
+ if(!target)
+ {
+ if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_NO_PET;
+ }
+ break;
+ }
+ }
+
+ //check creature type
+ //ignore self casts (including area casts when caster selected as target)
+ if(target != m_caster)
+ {
+ if(!CheckTargetCreatureType(target))
+ {
+ if(target->GetTypeId()==TYPEID_PLAYER)
+ return SPELL_FAILED_TARGET_IS_PLAYER;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+
+ // 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())
+ {
+ // check correctness positive/negative cast target (pet cast real check and cheating check)
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(m_caster->IsHostileTo(target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else
+ {
+ if(m_caster->IsFriendlyTo(target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(target->IsImmunedToSpell(m_spellInfo,false))
+ return SPELL_FAILED_TARGET_AURASTATE;
+ }
+
+ //Must be behind the target.
+ if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) )
+ {
+ SendInterrupted(2);
+ return SPELL_FAILED_NOT_BEHIND;
+ }
+
+ //Target must be facing you.
+ if((m_spellInfo->Attributes == 0x150010) && !target->HasInArc(M_PI, m_caster) )
+ {
+ SendInterrupted(2);
+ return SPELL_FAILED_NOT_INFRONT;
+ }
+
+ // check if target is in combat
+ if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat())
+ {
+ return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
+ }
+ }
+ // Spell casted only on battleground
+ if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ if(!((Player*)m_caster)->InBattleGround())
+ return SPELL_FAILED_ONLY_BATTLEGROUNDS;
+
+ // do not allow spells to be cast in arenas
+ // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag
+ // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
+ if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
+ GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) )
+ if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
+ if(mapEntry->IsBattleArena())
+ return SPELL_FAILED_NOT_IN_ARENA;
+
+ // zone check
+ if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId()))
+ return SPELL_FAILED_REQUIRES_AREA;
+
+ // not let players cast spells at mount (and let do it to creatures)
+ if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell &&
+ !IsPassiveSpell(m_spellInfo->Id) && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED) )
+ {
+ if(m_caster->isInFlight())
+ return SPELL_FAILED_NOT_FLYING;
+ else
+ return SPELL_FAILED_NOT_MOUNTED;
+ }
+
+ // always (except passive spells) check items (focus object can be required for any type casts)
+ if(!IsPassiveSpell(m_spellInfo->Id))
+ if(uint8 castResult = CheckItems())
+ return castResult;
+
+ //ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
+ if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example)
+ {
+ for(uint8 j = 0; j < 3; j++)
+ {
+ if( m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[j] != TARGET_SELF ||
+ m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ bool okDoo = false;
+
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
+ if(lower==upper)
+ sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT or TARGET_SCRIPT_COORDINATES, but does not have record in `spell_script_target`",m_spellInfo->Id);
+
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+
+ Creature* creatureScriptTarget = NULL;
+ GameObject* goScriptTarget = NULL;
+
+ for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST)
+ {
+ switch(i_spellST->second.type)
+ {
+ case SPELL_TARGET_TYPE_GAMEOBJECT:
+ {
+ GameObject* p_GameObject = NULL;
+
+ if(i_spellST->second.targetEntry)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, 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));
+
+ if(p_GameObject)
+ {
+ // remember found target and range, next attempt will find more near target with another entry
+ creatureScriptTarget = NULL;
+ goScriptTarget = p_GameObject;
+ range = go_check.GetLastRange();
+ }
+ }
+ else if( focusObject ) //Focus Object
+ {
+ float frange = m_caster->GetDistance(focusObject);
+ if(range >= frange)
+ {
+ creatureScriptTarget = NULL;
+ goScriptTarget = focusObject;
+ range = frange;
+ }
+ }
+ break;
+ }
+ case SPELL_TARGET_TYPE_CREATURE:
+ case SPELL_TARGET_TYPE_DEAD:
+ default:
+ {
+ Creature *p_Creature = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate(); // Really don't know what is that???
+
+ MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
+ MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(p_Creature )
+ {
+ creatureScriptTarget = p_Creature;
+ goScriptTarget = NULL;
+ range = u_check.GetLastRange();
+ }
+ break;
+ }
+ }
+ }
+
+ if(creatureScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(creatureScriptTarget->GetPositionX(),creatureScriptTarget->GetPositionY(),creatureScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ else if(goScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(goScriptTarget->GetPositionX(),goScriptTarget->GetPositionY(),goScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddGOTarget(goScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddGOTarget(goScriptTarget, j);
+ }
+ //Missing DB Entry or targets for this spellEffect.
+ else
+ {
+ // not report target not existence for triggered spells
+ if(m_triggeredByAuraSpell || m_IsTriggeredSpell)
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+ }
+ }
+
+ if(uint8 castResult = CheckRange(strict))
+ return castResult;
+
+ {
+ if(uint8 castResult = CheckPower())
+ return castResult;
+ }
+
+ if(!m_triggeredByAuraSpell) // triggered spell not affected by stun/etc
+ if(uint8 castResult = CheckCasterAuras())
+ return castResult;
+
+ for (int i = 0; i < 3; i++)
+ {
+ // for effects of spells that have only one target
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_DUMMY:
+ {
+ // Execute
+ if(m_spellInfo->SpellIconID == 1648)
+ {
+ if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else if (m_spellInfo->Id == 51582)
+ {
+ if(m_caster->IsInWater())
+ return SPELL_FAILED_ONLY_ABOVEWATER;
+ }
+ break;
+ }
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ {
+ // Hammer of Wrath
+ if(m_spellInfo->SpellVisual == 7250)
+ {
+ if (!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if(m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ break;
+ }
+ case SPELL_EFFECT_TAMECREATURE:
+ {
+ if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel())
+ return SPELL_FAILED_HIGHLEVEL;
+
+ CreatureInfo const *cinfo = ((Creature*)m_targets.getUnitTarget())->GetCreatureInfo();
+ if( cinfo->type != CREATURE_TYPE_BEAST )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // use SMSG_PET_TAME_FAILURE?
+ if( !(cinfo->flag1 & 1) || !(cinfo->family) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_LEARN_SPELL:
+ {
+ if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET)
+ break;
+
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+
+ if(!learn_spellproto)
+ return SPELL_FAILED_NOT_KNOWN;
+
+ if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
+ return SPELL_FAILED_TOO_MANY_SKILLS;
+
+ if(m_spellInfo->spellLevel > pet->getLevel())
+ return SPELL_FAILED_LOWLEVEL;
+
+ if(!pet->HasTPForSpell(learn_spellproto->Id))
+ return SPELL_FAILED_TRAINING_POINTS;
+
+ break;
+ }
+ case SPELL_EFFECT_LEARN_PET_SPELL:
+ {
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+
+ if(!learn_spellproto)
+ return SPELL_FAILED_NOT_KNOWN;
+
+ if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
+ return SPELL_FAILED_TOO_MANY_SKILLS;
+
+ if(m_spellInfo->spellLevel > pet->getLevel())
+ return SPELL_FAILED_LOWLEVEL;
+
+ if(!pet->HasTPForSpell(learn_spellproto->Id))
+ return SPELL_FAILED_TRAINING_POINTS;
+
+ break;
+ }
+ case SPELL_EFFECT_FEED_PET:
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto()))
+ return SPELL_FAILED_WRONG_PET_FOOD;
+
+ if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel))
+ return SPELL_FAILED_FOOD_LOWLEVEL;
+
+ if(m_caster->isInCombat() || pet->isInCombat())
+ return SPELL_FAILED_AFFECTING_COMBAT;
+
+ break;
+ }
+ case SPELL_EFFECT_POWER_BURN:
+ case SPELL_EFFECT_POWER_DRAIN:
+ {
+ // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects)
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(Unit* target = m_targets.getUnitTarget())
+ if(target!=m_caster && target->getPowerType()!=m_spellInfo->EffectMiscValue[i])
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ case SPELL_EFFECT_CHARGE:
+ {
+ if (m_caster->hasUnitState(UNIT_STAT_ROOT))
+ return SPELL_FAILED_ROOTED;
+
+ break;
+ }
+ case SPELL_EFFECT_SKINNING:
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if( !(m_targets.getUnitTarget()->GetUInt32Value(UNIT_FIELD_FLAGS) & UNIT_FLAG_SKINNABLE) )
+ return SPELL_FAILED_TARGET_UNSKINNABLE;
+
+ Creature* creature = (Creature*)m_targets.getUnitTarget();
+ if ( creature->GetCreatureType() != CREATURE_TYPE_CRITTER && ( !creature->lootForBody || !creature->loot.empty() ) )
+ {
+ return SPELL_FAILED_TARGET_NOT_LOOTED;
+ }
+
+ uint32 skill;
+ if(creature->GetCreatureInfo()->flag1 & 256)
+ skill = SKILL_HERBALISM; // special case
+ else if(creature->GetCreatureInfo()->flag1 & 512)
+ skill = SKILL_MINING; // special case
+ else
+ skill = SKILL_SKINNING; // normal case
+
+ int32 skillValue = ((Player*)m_caster)->GetSkillValue(skill);
+ int32 TargetLevel = m_targets.getUnitTarget()->getLevel();
+ int32 ReqValue = (skillValue < 100 ? (TargetLevel-10)*10 : TargetLevel*5);
+ if (ReqValue > skillValue)
+ return SPELL_FAILED_LOW_CASTLEVEL;
+
+ // chance for fail at orange skinning attempt
+ if( (m_selfContainer && (*m_selfContainer) == this) &&
+ skillValue < sWorld.GetConfigMaxSkillValue() &&
+ (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue-25, skillValue+37) )
+ return SPELL_FAILED_TRY_AGAIN;
+
+ break;
+ }
+ case SPELL_EFFECT_OPEN_LOCK_ITEM:
+ case SPELL_EFFECT_OPEN_LOCK:
+ {
+ if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT &&
+ m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT_ITEM )
+ break;
+
+ if( m_caster->GetTypeId() != TYPEID_PLAYER // only players can open locks, gather etc.
+ // we need a go target in case of TARGET_GAMEOBJECT
+ || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT && !m_targets.getGOTarget()
+ // we need a go target, or an openable item target in case of TARGET_GAMEOBJECT_ITEM
+ || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_ITEM && !m_targets.getGOTarget() &&
+ (!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // get the lock entry
+ LockEntry const *lockInfo = NULL;
+ if (GameObject* go=m_targets.getGOTarget())
+ lockInfo = sLockStore.LookupEntry(go->GetLockId());
+ else if(Item* itm=m_targets.getItemTarget())
+ lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID);
+
+ // check lock compatibility
+ if (lockInfo)
+ {
+ // check for lock - key pair (checked by client also, just prevent cheating
+ bool ok_key = false;
+ for(int it = 0; it < 5; ++it)
+ {
+ switch(lockInfo->keytype[it])
+ {
+ case LOCK_KEY_NONE:
+ break;
+ case LOCK_KEY_ITEM:
+ {
+ if(lockInfo->key[it])
+ {
+ if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
+ ok_key =true;
+ break;
+ }
+ }
+ case LOCK_KEY_SKILL:
+ {
+ if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it])
+ break;
+
+ switch(lockInfo->key[it])
+ {
+ case LOCKTYPE_HERBALISM:
+ if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM))
+ ok_key =true;
+ break;
+ case LOCKTYPE_MINING:
+ if(((Player*)m_caster)->HasSkill(SKILL_MINING))
+ ok_key =true;
+ break;
+ default:
+ ok_key =true;
+ break;
+ }
+ }
+ }
+ if(ok_key)
+ break;
+ }
+
+ if(!ok_key)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+
+ // chance for fail at orange mining/herb/LockPicking gathering attempt
+ if (!m_selfContainer || ((*m_selfContainer) != this))
+ break;
+
+ // get the skill value of the player
+ int32 SkillValue = 0;
+ bool canFailAtMax = true;
+ if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM)
+ {
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM);
+ canFailAtMax = false;
+ }
+ else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING)
+ {
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING);
+ canFailAtMax = false;
+ }
+ else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING);
+
+ // castitem check: rogue using skeleton keys. the skill values should not be added in this case.
+ if(m_CastItem)
+ SkillValue = 0;
+
+ // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
+ SkillValue += m_currentBasePoints[i]+1;
+
+ // get the required lock value
+ int32 ReqValue=0;
+ if (lockInfo)
+ {
+ // check for lock - key pair
+ bool ok = false;
+ for(int it = 0; it < 5; ++it)
+ {
+ if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
+ {
+ // if so, we're good to go
+ ok = true;
+ break;
+ }
+ }
+ if(ok)
+ break;
+
+ if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
+ ReqValue = lockInfo->requiredlockskill;
+ else
+ ReqValue = lockInfo->requiredminingskill;
+ }
+
+ // skill doesn't meet the required value
+ if (ReqValue > SkillValue)
+ return SPELL_FAILED_LOW_CASTLEVEL;
+
+ // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
+ if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37))
+ return SPELL_FAILED_TRY_AGAIN;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_DEAD_PET:
+ {
+ Creature *pet = m_caster->GetPet();
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ if(pet->isAlive())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ break;
+ }
+ // This is generic summon effect now and don't make this check for summon types similar
+ // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
+ // These won't show up in m_caster->GetPetGUID()
+ case SPELL_EFFECT_SUMMON:
+ {
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_POSESSED:
+ case SUMMON_TYPE_POSESSED2:
+ case SUMMON_TYPE_DEMON:
+ case SUMMON_TYPE_SUMMON:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+ break;
+ }
+ }
+ break;
+ }
+ // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
+ // These won't show up in m_caster->GetPetGUID()
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_PHANTASM:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_PET:
+ {
+ if(m_caster->GetPetGUID()) //let warlock do a replacement summon
+ {
+
+ Pet* pet = ((Player*)m_caster)->GetPet();
+
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK)
+ {
+ if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
+ pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
+ }
+ else
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+ }
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_PLAYER:
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+ if(!((Player*)m_caster)->GetSelection())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
+ if( !target || ((Player*)m_caster)==target || !target->IsInSameRaidWith((Player*)m_caster) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // check if our map is dungeon
+ if( sMapStore.LookupEntry(m_caster->GetMapId())->IsDungeon() )
+ {
+ InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(m_caster->GetMapId());
+ if(!instance)
+ return SPELL_FAILED_TARGET_NOT_IN_INSTANCE;
+ if ( instance->levelMin > target->getLevel() )
+ return SPELL_FAILED_LOWLEVEL;
+ if ( instance->levelMax && instance->levelMax < target->getLevel() )
+ return SPELL_FAILED_HIGHLEVEL;
+ }
+ break;
+ }
+ case SPELL_EFFECT_LEAP:
+ case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER:
+ {
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation());
+ float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation());
+ // teleport a bit above terrain level to avoid falling below it
+ float fz = MapManager::Instance().GetBaseMap(m_caster->GetMapId())->GetHeight(fx,fy,m_caster->GetPositionZ(),true);
+ if(fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled
+ return SPELL_FAILED_TRY_AGAIN;
+
+ float caster_pos_z = m_caster->GetPositionZ();
+ // Control the caster to not climb or drop when +-fz > 8
+ if(!(fz<=caster_pos_z+8 && fz>=caster_pos_z-8))
+ return SPELL_FAILED_TRY_AGAIN;
+
+ // not allow use this effect at battleground until battleground start
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround const *bg = ((Player*)m_caster)->GetBattleGround())
+ if(bg->GetStatus() != STATUS_IN_PROGRESS)
+ return SPELL_FAILED_TRY_AGAIN;
+ break;
+ }
+ case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF:
+ {
+ if (m_targets.getUnitTarget()==m_caster)
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ default:break;
+ }
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ switch(m_spellInfo->EffectApplyAuraName[i])
+ {
+ case SPELL_AURA_MOD_POSSESS:
+ case SPELL_AURA_MOD_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;
+
+ if(!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if(m_targets.getUnitTarget()->GetCharmerGUID())
+ return SPELL_FAILED_CHARMED;
+
+ if(int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(i,m_targets.getUnitTarget()))
+ return SPELL_FAILED_HIGHLEVEL;
+ };break;
+ case SPELL_AURA_MOUNTED:
+ {
+ if (m_caster->IsInWater())
+ return SPELL_FAILED_ONLY_ABOVEWATER;
+
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetTransport())
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId)
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ if (m_caster->GetAreaId()==35)
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ ShapeshiftForm form = m_caster->m_form;
+ if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL ||
+ form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR ||
+ form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT ||
+ form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN )
+ return SPELL_FAILED_NOT_SHAPESHIFT;
+
+ break;
+ }
+ case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
+ {
+ if(!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ // can be casted at non-friendly unit or own pet/charm
+ if(m_caster->IsFriendlyTo(m_targets.getUnitTarget()))
+ return SPELL_FAILED_TARGET_FRIENDLY;
+ };break;
+ case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED:
+ case SPELL_AURA_FLY:
+ {
+ // not allow cast fly spells at old maps by players (all spells is self target)
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if( !((Player*)m_caster)->isGameMaster() &&
+ GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530)
+ return SPELL_FAILED_NOT_HERE;
+ }
+ };break;
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ {
+ if (!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if (m_caster->GetTypeId()!=TYPEID_PLAYER || m_CastItem)
+ break;
+
+ if(m_targets.getUnitTarget()->getPowerType()!=POWER_MANA)
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ default:break;
+ }
+ }
+
+ // all ok
+ return 0;
+}
+
+int16 Spell::PetCanCast(Unit* target)
+{
+ if(!m_caster->isAlive())
+ return SPELL_FAILED_CASTER_DEAD;
+
+ if(m_caster->IsNonMeleeSpellCasted(false)) //prevent spellcast interuption by another spellcast
+ return SPELL_FAILED_SPELL_IN_PROGRESS;
+ if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
+ return SPELL_FAILED_AFFECTING_COMBAT;
+
+ if(m_caster->GetTypeId()==TYPEID_UNIT && (((Creature*)m_caster)->isPet() || m_caster->isCharmed()))
+ {
+ //dead owner (pets still alive when owners ressed?)
+ if(m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive())
+ return SPELL_FAILED_CASTER_DEAD;
+
+ if(!target && m_targets.getUnitTarget())
+ target = m_targets.getUnitTarget();
+
+ bool need = false;
+ for(uint32 i = 0;i<3;i++)
+ {
+ if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_FRIEND || m_spellInfo->EffectImplicitTargetA[i] == TARGET_DUELVSPLAYER || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_PARTY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_CURRENT_ENEMY_COORDINATES)
+ {
+ need = true;
+ if(!target)
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+ break;
+ }
+ }
+ if(need)
+ m_targets.setUnitTarget(target);
+
+ Unit* _target = m_targets.getUnitTarget();
+
+ if(_target) //for target dead/target not valid
+ {
+ if(!_target->isAlive())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(m_caster->IsHostileTo(_target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else
+ {
+ bool duelvsplayertar = false;
+ for(int j=0;j<3;j++)
+ {
+ //TARGET_DUELVSPLAYER is positive AND negative
+ duelvsplayertar |= (m_spellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER);
+ }
+ if(m_caster->IsFriendlyTo(target) && !duelvsplayertar)
+ {
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+ }
+ //cooldown
+ if(((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
+ return SPELL_FAILED_NOT_READY;
+ }
+
+ uint16 result = CanCast(true);
+ if(result != 0)
+ return result;
+ else
+ return -1; //this allows to check spell fail 0, in combat
+}
+
+uint8 Spell::CheckCasterAuras() const
+{
+ // Flag drop spells totally immuned to caster auras
+ // FIXME: find more nice check for all totally immuned spells
+ // AttributesEx3 & 0x10000000?
+ if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991)
+ return 0;
+
+ uint8 school_immune = 0;
+ uint32 mechanic_immune = 0;
+ uint32 dispel_immune = 0;
+
+ //Check if the spell grants school or mechanic immunity.
+ //We use bitmasks so the loop is done only once and not on every aura check below.
+ if ( m_spellInfo->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY )
+ {
+ for(int i = 0;i < 3; i ++)
+ {
+ if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_SCHOOL_IMMUNITY)
+ school_immune |= uint32(m_spellInfo->EffectMiscValue[i]);
+ else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MECHANIC_IMMUNITY)
+ mechanic_immune |= 1 << uint32(m_spellInfo->EffectMiscValue[i]);
+ else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DISPEL_IMMUNITY)
+ dispel_immune |= GetDispellMask(DispelType(m_spellInfo->EffectMiscValue[i]));
+ }
+ //immune movement impairement and loss of control
+ if(m_spellInfo->Id==(uint32)42292)
+ mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
+ }
+
+ //Check whether the cast should be prevented by any state you might have.
+ uint8 prevented_reason = 0;
+ // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out
+ if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN))
+ prevented_reason = SPELL_FAILED_STUNNED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ prevented_reason = SPELL_FAILED_CONFUSED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ prevented_reason = SPELL_FAILED_FLEEING;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ prevented_reason = SPELL_FAILED_SILENCED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ prevented_reason = SPELL_FAILED_PACIFIED;
+
+ // Attr must make flag drop spell totally immuned from all effects
+ if(prevented_reason)
+ {
+ if(school_immune || mechanic_immune || dispel_immune)
+ {
+ //Checking auras is needed now, because you are prevented by some state but the spell grants immunity.
+ Unit::AuraMap const& auras = m_caster->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
+ {
+ if(itr->second)
+ {
+ if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune )
+ continue;
+ if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune )
+ continue;
+ if( (1<<(itr->second->GetSpellProto()->Dispel)) & dispel_immune)
+ continue;
+
+ //Make a second check for spell failed so the right SPELL_FAILED message is returned.
+ //That is needed when your casting is prevented by multiple states and you are only immune to some of them.
+ switch(itr->second->GetModifier()->m_auraname)
+ {
+ case SPELL_AURA_MOD_STUN:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
+ return SPELL_FAILED_STUNNED;
+ break;
+ case SPELL_AURA_MOD_CONFUSE:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ return SPELL_FAILED_CONFUSED;
+ break;
+ case SPELL_AURA_MOD_FEAR:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ return SPELL_FAILED_FLEEING;
+ break;
+ case SPELL_AURA_MOD_SILENCE:
+ case SPELL_AURA_MOD_PACIFY:
+ case SPELL_AURA_MOD_PACIFY_SILENCE:
+ if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ return SPELL_FAILED_PACIFIED;
+ else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ return SPELL_FAILED_SILENCED;
+ break;
+ }
+ }
+ }
+ }
+ //You are prevented from casting and the spell casted does not grant immunity. Return a failed error.
+ else
+ return prevented_reason;
+ }
+ return 0; // all ok
+}
+
+bool Spell::CanAutoCast(Unit* target)
+{
+ uint64 targetguid = target->GetGUID();
+
+ for(uint32 j = 0;j<3;j++)
+ {
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA)
+ {
+ if( m_spellInfo->StackAmount <= 1)
+ {
+ if( target->HasAura(m_spellInfo->Id, j) )
+ return false;
+ }
+ else
+ {
+ if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount)
+ return false;
+ }
+ }
+ else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] ))
+ {
+ if( target->HasAura(m_spellInfo->Id, j) )
+ return false;
+ }
+ }
+
+ int16 result = PetCanCast(target);
+
+ if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT)
+ {
+ FillTargetMap();
+ //check if among target units, our WANTED target is as well (->only self cast spells return false)
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if( ihit->targetGUID == targetguid )
+ return true;
+ }
+ return false; //target invalid
+}
+
+uint8 Spell::CheckRange(bool strict)
+{
+ float range_mod;
+
+ // self cast doesn't need range checking -- also for Starshards fix
+ if (m_spellInfo->rangeIndex == 1) return 0;
+
+ if (strict) //add radius of caster
+ range_mod = 1.25;
+ else //add radius of caster and ~5 yds "give"
+ range_mod = 6.25;
+
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float max_range = GetSpellMaxRange(srange) + range_mod;
+ float min_range = GetSpellMinRange(srange);
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
+
+ Unit *target = m_targets.getUnitTarget();
+
+ if(target && target != m_caster)
+ {
+ // distance from target center in checks
+ float dist = m_caster->GetDistance(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ());
+ if(dist > max_range)
+ return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
+ if(dist < min_range)
+ return SPELL_FAILED_TOO_CLOSE;
+ if( !m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER &&
+ !IsPositiveSpell(m_spellInfo->Id) && !m_caster->HasInArc( M_PI, target ) )
+ {
+ // Spell-Family Related Checks
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_PRIEST:
+ {
+ // Shadow Word: Death, castable without facing
+ if (m_spellInfo->SpellFamilyFlags & 0x200000000LL)
+ return 0; // this is not TARGET_FLAG_DEST_LOCATION so we can return safely
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Holy Shock, require facing
+ if (m_spellInfo->SpellFamilyFlags & 0x200000LL)
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Charge, require facing
+ if (m_spellInfo->SpellFamilyFlags & 1)
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+ break;
+ }
+ }
+
+ // Ranged Weapon
+ if (IsRangedSpell())
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+
+ // Melee Combat
+ if (m_spellInfo->rangeIndex == 2)
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+
+ // Missile Effect
+ if (m_spellInfo->speed > 0)
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+
+ // Channeled Spells need facing
+ if (IsChanneledSpell(m_spellInfo))
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+
+ // Direct Damage and charge effects
+ for (uint8 i=0;i<3;++i)
+ {
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_SCHOOL_DAMAGE ||
+ m_spellInfo->Effect[i] == SPELL_EFFECT_POWER_BURN ||
+ m_spellInfo->Effect[i] == SPELL_EFFECT_HEALTH_LEECH ||
+ m_spellInfo->Effect[i] == SPELL_EFFECT_CHARGE)
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+ }
+ }
+ }
+
+ if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0)
+ {
+ float dist = m_caster->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ);
+ if(dist > max_range)
+ return SPELL_FAILED_OUT_OF_RANGE;
+ if(dist < min_range)
+ return SPELL_FAILED_TOO_CLOSE;
+ }
+
+ return 0; // 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:
+ // case POWER_RUNES:
+ powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100;
+ 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;
+}
+
+uint8 Spell::CheckPower()
+{
+ // item cast not used power
+ if(m_CastItem)
+ return 0;
+
+ // health as power used - need check health amount
+ if(m_spellInfo->powerType == POWER_HEALTH)
+ {
+ if(m_caster->GetHealth() <= m_powerCost)
+ return SPELL_FAILED_CASTER_AURASTATE;
+ return 0;
+ }
+ // Check valid power type
+ if( m_spellInfo->powerType >= MAX_POWERS )
+ {
+ sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType);
+ return SPELL_FAILED_UNKNOWN;
+ }
+ // Check power amount
+ Powers powerType = Powers(m_spellInfo->powerType);
+ if(m_caster->GetPower(powerType) < m_powerCost)
+ return SPELL_FAILED_NO_POWER;
+ else
+ return 0;
+}
+
+uint8 Spell::CheckItems()
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return 0;
+
+ uint32 itemid, itemcount;
+ Player* p_caster = (Player*)m_caster;
+
+ if(m_CastItem)
+ {
+ itemid = m_CastItem->GetEntry();
+ if( !p_caster->HasItemCount(itemid,1) )
+ return SPELL_FAILED_ITEM_NOT_READY;
+ else
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(!proto)
+ return SPELL_FAILED_ITEM_NOT_READY;
+
+ for (int i = 0; i<5; i++)
+ {
+ if (proto->Spells[i].SpellCharges)
+ {
+ if(m_CastItem->GetSpellCharges(i)==0)
+ return SPELL_FAILED_NO_CHARGES_REMAIN;
+ }
+ }
+
+ uint32 ItemClass = proto->Class;
+ if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster
+ if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET)
+ continue;
+
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
+ if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH;
+
+ // Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
+ {
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
+ }
+ }
+ }
+ }
+ }
+
+ if(m_targets.getItemTargetGUID())
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_ITEM_GONE;
+
+ if(!m_targets.getItemTarget()->IsFitToSpellRequirements(m_spellInfo))
+ return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+ }
+ // if not item target then required item must be equipped
+ else
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->HasItemFitToSpellReqirements(m_spellInfo))
+ return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+ }
+
+ if(m_spellInfo->RequiresSpellFocus)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ GameObject* ok = NULL;
+ MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(ok,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::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));
+
+ if(!ok)
+ return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS;
+
+ focusObject = ok; // game object found in range
+ }
+
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
+ m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)))
+ {
+ for(uint32 i=0;i<8;i++)
+ {
+ if(m_spellInfo->Reagent[i] <= 0)
+ continue;
+
+ itemid = m_spellInfo->Reagent[i];
+ itemcount = m_spellInfo->ReagentCount[i];
+
+ // if CastItem is also spell reagent
+ if( m_CastItem && m_CastItem->GetEntry() == itemid )
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(!proto)
+ return SPELL_FAILED_ITEM_NOT_READY;
+ for(int s=0;s<5;s++)
+ {
+ // CastItem will be used up and does not count as reagent
+ int32 charges = m_CastItem->GetSpellCharges(s);
+ if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
+ {
+ ++itemcount;
+ break;
+ }
+ }
+ }
+ if( !p_caster->HasItemCount(itemid,itemcount) )
+ return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54
+ }
+ }
+
+ uint32 totems = 2;
+ for(int i=0;i<2;++i)
+ {
+ if(m_spellInfo->Totem[i] != 0)
+ {
+ if( p_caster->HasItemCount(m_spellInfo->Totem[i],1) )
+ {
+ totems -= 1;
+ continue;
+ }
+ }else
+ totems -= 1;
+ }
+ if(totems != 0)
+ return (uint8)SPELL_FAILED_TOTEMS; //0x7C
+
+ //Check items for TotemCategory
+ uint32 TotemCategory = 2;
+ for(int i=0;i<2;++i)
+ {
+ if(m_spellInfo->TotemCategory[i] != 0)
+ {
+ if( p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]) )
+ {
+ TotemCategory -= 1;
+ continue;
+ }
+ }
+ else
+ TotemCategory -= 1;
+ }
+ if(TotemCategory != 0)
+ return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B
+
+ for(int i = 0; i < 3; i++)
+ {
+ switch (m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_CREATE_ITEM:
+ {
+ if (!m_IsTriggeredSpell && m_spellInfo->EffectItemType[i])
+ {
+ ItemPosCountVec dest;
+ uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 );
+ if (msg != EQUIP_ERR_OK )
+ {
+ p_caster->SendEquipError( msg, NULL, NULL );
+ return SPELL_FAILED_DONT_REPORT;
+ }
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_ITEM:
+ {
+ Item* targetItem = m_targets.getItemTarget();
+ if(!targetItem)
+ return SPELL_FAILED_ITEM_NOT_FOUND;
+
+ if( targetItem->GetProto()->ItemLevel < m_spellInfo->baseLevel )
+ return SPELL_FAILED_LOWLEVEL;
+ // Not allow enchant in trade slot for some enchant type
+ if( targetItem->GetOwner() != m_caster )
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return SPELL_FAILED_ERROR;
+ if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
+ return SPELL_FAILED_NOT_TRADEABLE;
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
+ {
+ Item *item = m_targets.getItemTarget();
+ if(!item)
+ return SPELL_FAILED_ITEM_NOT_FOUND;
+ // Not allow enchant in trade slot for some enchant type
+ if( item->GetOwner() != m_caster )
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return SPELL_FAILED_ERROR;
+ if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
+ return SPELL_FAILED_NOT_TRADEABLE;
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_HELD_ITEM:
+ // check item existence in effect code (not output errors at offhand hold item effect to main hand for example
+ break;
+ case SPELL_EFFECT_DISENCHANT:
+ {
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ // prevent disenchanting in trade slot
+ if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ ItemPrototype const* itemProto = m_targets.getItemTarget()->GetProto();
+ if(!itemProto)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ uint32 item_quality = itemProto->Quality;
+ // 2.0.x addon: Check player enchanting level agains the item desenchanting requirements
+ uint32 item_disenchantskilllevel = itemProto->RequiredDisenchantSkill;
+ if (item_disenchantskilllevel == uint32(-1))
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if (item_disenchantskilllevel > p_caster->GetSkillValue(SKILL_ENCHANTING))
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ if(item_quality > 4 || item_quality < 2)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if(itemProto->Class != ITEM_CLASS_WEAPON && itemProto->Class != ITEM_CLASS_ARMOR)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if (!itemProto->DisenchantID)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ break;
+ }
+ case SPELL_EFFECT_PROSPECTING:
+ {
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //ensure item is a prospectable ore
+ if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS)
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //prevent prospecting in trade slot
+ if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //Check for enough skill in jewelcrafting
+ uint32 item_prospectingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank;
+ if(item_prospectingskilllevel >p_caster->GetSkillValue(SKILL_JEWELCRAFTING))
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ //make sure the player has the required ores in inventory
+ if(m_targets.getItemTarget()->GetCount() < 5)
+ return SPELL_FAILED_PROSPECT_NEED_MORE;
+
+ if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry()))
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+
+ break;
+ }
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER;
+ if( m_attackType != RANGED_ATTACK )
+ break;
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(m_attackType);
+ if(!pItem || pItem->IsBroken())
+ return SPELL_FAILED_EQUIPPED_ITEM;
+
+ switch(pItem->GetProto()->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_THROWN:
+ {
+ uint32 ammo = pItem->GetEntry();
+ if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
+ return SPELL_FAILED_NO_AMMO;
+ }; break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ {
+ uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
+ if(!ammo)
+ {
+ // Requires No Ammo
+ if(m_caster->GetDummyAura(46699))
+ break; // skip other checks
+
+ return SPELL_FAILED_NO_AMMO;
+ }
+
+ ItemPrototype const *ammoProto = objmgr.GetItemPrototype( ammo );
+ if(!ammoProto)
+ return SPELL_FAILED_NO_AMMO;
+
+ if(ammoProto->Class != ITEM_CLASS_PROJECTILE)
+ return SPELL_FAILED_NO_AMMO;
+
+ // check ammo ws. weapon compatibility
+ switch(pItem->GetProto()->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ if(ammoProto->SubClass!=ITEM_SUBCLASS_ARROW)
+ return SPELL_FAILED_NO_AMMO;
+ break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ if(ammoProto->SubClass!=ITEM_SUBCLASS_BULLET)
+ return SPELL_FAILED_NO_AMMO;
+ break;
+ default:
+ return SPELL_FAILED_NO_AMMO;
+ }
+
+ if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
+ return SPELL_FAILED_NO_AMMO;
+ }; break;
+ case ITEM_SUBCLASS_WEAPON_WAND:
+ default:
+ break;
+ }
+ break;
+ }
+ default:break;
+ }
+ }
+
+ return uint8(0);
+}
+
+void Spell::Delayed()
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (m_spellState == SPELL_STATE_DELAYED)
+ return; // spell is active and can't be time-backed
+
+ // spells not loosing casting time ( slam, dynamites, bombs.. )
+ if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE))
+ return;
+
+ //check resist chance
+ int32 resistChance = 100; //must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
+ resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
+ if (roll_chance_i(resistChance))
+ return;
+
+ int32 delaytime = GetNextDelayAtDamageMsTime();
+
+ if(int32(m_timer) + delaytime > m_casttime)
+ {
+ delaytime = m_casttime - m_timer;
+ m_timer = m_casttime;
+ }
+ else
+ m_timer += delaytime;
+
+ sLog.outDetail("Spell %u partially interrupted for (%d) ms at damage",m_spellInfo->Id,delaytime);
+
+ WorldPacket data(SMSG_SPELL_DELAYED, 8+4);
+ data.append(m_caster->GetPackGUID());
+ data << uint32(delaytime);
+
+ m_caster->SendMessageToSet(&data,true);
+}
+
+void Spell::DelayedChannel()
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING)
+ return;
+
+ //check resist chance
+ int32 resistChance = 100; //must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
+ resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
+ if (roll_chance_i(resistChance))
+ return;
+
+ int32 delaytime = GetNextDelayAtDamageMsTime();
+
+ if(int32(m_timer) < delaytime)
+ {
+ delaytime = m_timer;
+ m_timer = 0;
+ }
+ else
+ m_timer -= delaytime;
+
+ sLog.outDebug("Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer);
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if ((*ihit).missCondition == SPELL_MISS_NONE)
+ {
+ Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if (unit)
+ {
+ for (int j=0;j<3;j++)
+ if( ihit->effectMask & (1<<j) )
+ unit->DelayAura(m_spellInfo->Id, j, delaytime);
+ }
+
+ }
+ }
+
+ for(int j = 0; j < 3; j++)
+ {
+ // partially interrupt persistent area auras
+ DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, j);
+ if(dynObj)
+ dynObj->Delay(delaytime);
+ }
+
+ SendChannelUpdate(m_timer);
+}
+
+void Spell::UpdatePointers()
+{
+ if(m_originalCasterGUID==m_caster->GetGUID())
+ m_originalCaster = m_caster;
+ else
+ {
+ m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
+ if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
+ }
+
+ m_targets.Update(m_caster);
+}
+
+bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId)
+{
+ return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]);
+}
+
+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 == 0x0200000000LL)
+ {
+ // not allow cast at player
+ if(target->GetTypeId()==TYPEID_PLAYER)
+ return false;
+
+ spellCreatureTargetMask = 0x7FF;
+ }
+
+ // Dismiss Pet and Taming Lesson skipped
+ if(m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356)
+ spellCreatureTargetMask = 0;
+
+ if (spellCreatureTargetMask)
+ {
+ uint32 TargetCreatureType = target->GetCreatureTypeMask();
+
+ return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType);
+ }
+ return true;
+}
+
+CurrentSpellTypes Spell::GetCurrentContainer()
+{
+ if (IsNextMeleeSwingSpell())
+ return(CURRENT_MELEE_SPELL);
+ else if (IsAutoRepeat())
+ return(CURRENT_AUTOREPEAT_SPELL);
+ else if (IsChanneledSpell(m_spellInfo))
+ return(CURRENT_CHANNELED_SPELL);
+ else
+ return(CURRENT_GENERIC_SPELL);
+}
+
+bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
+{
+ // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets)
+ if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_SELF )
+ {
+ if (!CheckTargetCreatureType(target))
+ return false;
+ }
+
+ // Check targets for not_selectable unit flag and remove
+ // A player can cast spells on his pet (or other controlled unit) though in any state
+ if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
+ {
+ // any unattackable target skipped
+ if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
+ return false;
+
+ // unselectable targets skipped in all cases except TARGET_SCRIPT targeting
+ // in case TARGET_SCRIPT target selected by server always and can't be cheated
+ if( target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) &&
+ m_spellInfo->EffectImplicitTargetA[eff] != TARGET_SCRIPT &&
+ m_spellInfo->EffectImplicitTargetB[eff] != TARGET_SCRIPT )
+ return false;
+ }
+
+ //Check player targets and remove if in GM mode or GM invisibility (for not self casting case)
+ if( target != m_caster && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)target)->GetVisibility()==VISIBILITY_OFF)
+ return false;
+
+ if(((Player*)target)->isGameMaster() && !IsPositiveSpell(m_spellInfo->Id))
+ return false;
+ }
+
+ //Check targets for LOS visibility (except spells without range limitations )
+ switch(m_spellInfo->Effect[eff])
+ {
+ case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere
+ break;
+ case SPELL_EFFECT_DUMMY:
+ if(m_spellInfo->Id!=20577) // Cannibalize
+ break;
+ //fall through
+ case SPELL_EFFECT_RESURRECT_NEW:
+ // player far away, maybe his corpse near?
+ if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
+ {
+ if(!m_targets.getCorpseTargetGUID())
+ return false;
+
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(!corpse)
+ return false;
+
+ if(target->GetGUID()!=corpse->GetOwnerGUID())
+ return false;
+
+ if(!corpse->IsWithinLOSInMap(m_caster))
+ return false;
+ }
+
+ // all ok by some way or another, skip normal check
+ break;
+ default: // normal case
+ if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+Unit* Spell::SelectMagnetTarget()
+{
+ Unit* target = m_targets.getUnitTarget();
+
+ if(target && target->HasAuraType(SPELL_AURA_SPELL_MAGNET) && !(m_spellInfo->Attributes & 0x10))
+ {
+ Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
+ for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
+ {
+ if(Unit* magnet = (*itr)->GetCaster())
+ {
+ if(magnet->IsWithinLOSInMap(m_caster))
+ {
+ target = magnet;
+ m_targets.setUnitTarget(target);
+ break;
+ }
+ }
+ }
+ }
+
+ return target;
+}
+
+bool Spell::IsNeedSendToClient() const
+{
+ return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) ||
+ m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell;
+}
+
+bool Spell::HaveTargetsForEffect( uint8 effect ) const
+{
+ for(std::list<TargetInfo>::const_iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ for(std::list<GOTargetInfo>::const_iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ for(std::list<ItemTargetInfo>::const_iterator itr= m_UniqueItemInfo.begin();itr != m_UniqueItemInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ return false;
+}
+
+SpellEvent::SpellEvent(Spell* spell) : BasicEvent()
+{
+ m_Spell = spell;
+}
+
+SpellEvent::~SpellEvent()
+{
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->cancel();
+
+ if (m_Spell->IsDeletable())
+ {
+ delete m_Spell;
+ }
+ else
+ {
+ sLog.outError("~SpellEvent: %s %u tried to delete non-deletable spell %u. Was not deleted, causes memory leak.",
+ (m_Spell->GetCaster()->GetTypeId()==TYPEID_PLAYER?"Player":"Creature"), m_Spell->GetCaster()->GetGUIDLow(),m_Spell->m_spellInfo->Id);
+ }
+}
+
+bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
+{
+ // update spell if it is not finished
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->update(p_time);
+
+ // check spell state to process
+ switch (m_Spell->getState())
+ {
+ case SPELL_STATE_FINISHED:
+ {
+ // spell was finished, check deletable state
+ if (m_Spell->IsDeletable())
+ {
+ // check, if we do have unfinished triggered spells
+
+ return(true); // spell is deletable, finish event
+ }
+ // event will be re-added automatically at the end of routine)
+ } break;
+
+ case SPELL_STATE_CASTING:
+ {
+ // this spell is in channeled state, process it on the next update
+ // event will be re-added automatically at the end of routine)
+ } break;
+
+ case SPELL_STATE_DELAYED:
+ {
+ // first, check, if we have just started
+ if (m_Spell->GetDelayStart() != 0)
+ {
+ // no, we aren't, do the typical update
+ // check, if we have channeled spell on our hands
+ if (IsChanneledSpell(m_Spell->m_spellInfo))
+ {
+ // evented channeled spell is processed separately, casted once after delay, and not destroyed till finish
+ // check, if we have casting anything else except this channeled spell and autorepeat
+ if (m_Spell->GetCaster()->IsNonMeleeSpellCasted(false, true, true))
+ {
+ // another non-melee non-delayed spell is casted now, abort
+ m_Spell->cancel();
+ }
+ else
+ {
+ // do the action (pass spell to channeling state)
+ m_Spell->handle_immediate();
+ }
+ // event will be re-added automatically at the end of routine)
+ }
+ else
+ {
+ // run the spell handler and think about what we can do next
+ uint64 t_offset = e_time - m_Spell->GetDelayStart();
+ uint64 n_offset = m_Spell->handle_delayed(t_offset);
+ if (n_offset)
+ {
+ // re-add us to the queue
+ m_Spell->GetCaster()->m_Events.AddEvent(this, m_Spell->GetDelayStart() + n_offset, false);
+ return(false); // event not complete
+ }
+ // event complete
+ // finish update event will be re-added automatically at the end of routine)
+ }
+ }
+ else
+ {
+ // delaying had just started, record the moment
+ m_Spell->SetDelayStart(e_time);
+ // re-plan the event for the delay moment
+ m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + m_Spell->GetDelayMoment(), false);
+ return(false); // event not complete
+ }
+ } break;
+
+ default:
+ {
+ // all other states
+ // event will be re-added automatically at the end of routine)
+ } break;
+ }
+
+ // spell processing not complete, plan event on the next update interval
+ m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + 1, false);
+ return(false); // event not complete
+}
+
+void SpellEvent::Abort(uint64 /*e_time*/)
+{
+ // oops, the spell we try to do is aborted
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->cancel();
+}
diff --git a/src/game/Spell.h b/src/game/Spell.h
new file mode 100644
index 00000000000..898d74de0f2
--- /dev/null
+++ b/src/game/Spell.h
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __SPELL_H
+#define __SPELL_H
+
+#include "GridDefines.h"
+
+class WorldSession;
+class Unit;
+class DynamicObj;
+class Player;
+class GameObject;
+class Group;
+class Aura;
+
+enum SpellCastTargetFlags
+{
+ /*TARGET_FLAG_NONE = 0x0000,
+ TARGET_FLAG_SWIMMER = 0x0002,
+ TARGET_FLAG_ITEM = 0x0010,
+ TARGET_FLAG_SOURCE_AREA = 0x0020,
+ TARGET_FLAG_DEST_AREA = 0x0040,
+ TARGET_FLAG_UNKNOWN = 0x0080,
+ TARGET_FLAG_SELF = 0x0100,
+ TARGET_FLAG_PVP_CORPSE = 0x0200,
+ TARGET_FLAG_MASS_SPIRIT_HEAL = 0x0400,
+ TARGET_FLAG_BEAST_CORPSE = 0x0402,
+ TARGET_FLAG_OBJECT = 0x4000,
+ TARGET_FLAG_RESURRECTABLE = 0x8000*/
+
+ TARGET_FLAG_SELF = 0x00000000,
+ TARGET_FLAG_UNIT = 0x00000002, // pguid
+ TARGET_FLAG_ITEM = 0x00000010, // pguid
+ TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float
+ TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float
+ TARGET_FLAG_OBJECT_UNK = 0x00000080, // ?
+ TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid
+ TARGET_FLAG_OBJECT = 0x00000800, // pguid
+ TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid
+ TARGET_FLAG_STRING = 0x00002000, // string
+ TARGET_FLAG_UNK1 = 0x00004000, // ?
+ TARGET_FLAG_CORPSE = 0x00008000, // pguid
+ TARGET_FLAG_UNK2 = 0x00010000 // pguid
+};
+
+enum SpellCastFlags
+{
+ CAST_FLAG_UNKNOWN1 = 0x00000002,
+ CAST_FLAG_UNKNOWN2 = 0x00000010,
+ CAST_FLAG_AMMO = 0x00000020,
+ CAST_FLAG_UNKNOWN3 = 0x00000100
+};
+
+enum SpellNotifyPushType
+{
+ PUSH_IN_FRONT,
+ PUSH_IN_BACK,
+ PUSH_SELF_CENTER,
+ PUSH_DEST_CENTER,
+ PUSH_TARGET_CENTER
+};
+
+bool IsQuestTameSpell(uint32 spellId);
+
+namespace MaNGOS
+{
+ struct SpellNotifierPlayer;
+ struct SpellNotifierCreatureAndPlayer;
+}
+
+class SpellCastTargets
+{
+ public:
+ SpellCastTargets();
+ ~SpellCastTargets();
+
+ bool read ( WorldPacket * data, Unit *caster );
+ void write ( WorldPacket * data );
+
+ SpellCastTargets& operator=(const SpellCastTargets &target)
+ {
+ m_unitTarget = target.m_unitTarget;
+ m_itemTarget = target.m_itemTarget;
+ m_GOTarget = target.m_GOTarget;
+
+ m_unitTargetGUID = target.m_unitTargetGUID;
+ m_GOTargetGUID = target.m_GOTargetGUID;
+ m_CorpseTargetGUID = target.m_CorpseTargetGUID;
+ m_itemTargetGUID = target.m_itemTargetGUID;
+
+ m_itemTargetEntry = target.m_itemTargetEntry;
+
+ m_srcX = target.m_srcX;
+ m_srcY = target.m_srcY;
+ m_srcZ = target.m_srcZ;
+
+ m_destX = target.m_destX;
+ m_destY = target.m_destY;
+ m_destZ = target.m_destZ;
+
+ m_strTarget = target.m_strTarget;
+
+ m_targetMask = target.m_targetMask;
+
+ return *this;
+ }
+
+ uint64 getUnitTargetGUID() const { return m_unitTargetGUID; }
+ Unit *getUnitTarget() const { return m_unitTarget; }
+ void setUnitTarget(Unit *target);
+ void setDestination(float x, float y, float z);
+
+ uint64 getGOTargetGUID() const { return m_GOTargetGUID; }
+ GameObject *getGOTarget() const { return m_GOTarget; }
+ void setGOTarget(GameObject *target);
+
+ uint64 getCorpseTargetGUID() const { return m_CorpseTargetGUID; }
+ void setCorpseTarget(Corpse* corpse);
+ uint64 getItemTargetGUID() const { return m_itemTargetGUID; }
+ Item* getItemTarget() const { return m_itemTarget; }
+ uint32 getItemTargetEntry() const { return m_itemTargetEntry; }
+ void setItemTarget(Item* item);
+ void updateTradeSlotItem()
+ {
+ if(m_itemTarget && (m_targetMask & TARGET_FLAG_TRADE_ITEM))
+ {
+ m_itemTargetGUID = m_itemTarget->GetGUID();
+ m_itemTargetEntry = m_itemTarget->GetEntry();
+ }
+ }
+
+ bool IsEmpty() const { return m_GOTargetGUID==0 && m_unitTargetGUID==0 && m_itemTarget==0 && m_CorpseTargetGUID==0; }
+
+ void Update(Unit* caster);
+
+ float m_srcX, m_srcY, m_srcZ;
+ float m_destX, m_destY, m_destZ;
+ std::string m_strTarget;
+
+ uint32 m_targetMask;
+ private:
+ // objects (can be used at spell creating and after Update at casting
+ Unit *m_unitTarget;
+ GameObject *m_GOTarget;
+ Item *m_itemTarget;
+
+ // object GUID/etc, can be used always
+ uint64 m_unitTargetGUID;
+ uint64 m_GOTargetGUID;
+ uint64 m_CorpseTargetGUID;
+ uint64 m_itemTargetGUID;
+ uint32 m_itemTargetEntry;
+};
+
+enum SpellState
+{
+ SPELL_STATE_NULL = 0,
+ SPELL_STATE_PREPARING = 1,
+ SPELL_STATE_CASTING = 2,
+ SPELL_STATE_FINISHED = 3,
+ SPELL_STATE_IDLE = 4,
+ SPELL_STATE_DELAYED = 5
+};
+
+#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL 1000
+
+typedef std::multimap<uint64, uint64> SpellTargetTimeMap;
+
+class Spell
+{
+ friend struct MaNGOS::SpellNotifierPlayer;
+ friend struct MaNGOS::SpellNotifierCreatureAndPlayer;
+ public:
+
+ void EffectNULL(uint32 );
+ void EffectUnused(uint32 );
+ void EffectDistract(uint32 i);
+ void EffectPull(uint32 i);
+ void EffectSchoolDMG(uint32 i);
+ void EffectEnvirinmentalDMG(uint32 i);
+ void EffectInstaKill(uint32 i);
+ void EffectDummy(uint32 i);
+ void EffectTeleportUnits(uint32 i);
+ void EffectApplyAura(uint32 i);
+ void EffectSendEvent(uint32 i);
+ void EffectPowerBurn(uint32 i);
+ void EffectPowerDrain(uint32 i);
+ void EffectHeal(uint32 i);
+ void EffectHealthLeech(uint32 i);
+ void EffectQuestComplete(uint32 i);
+ void EffectCreateItem(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);
+ void EffectSummon(uint32 i);
+ void EffectLearnSpell(uint32 i);
+ void EffectDispel(uint32 i);
+ void EffectDualWield(uint32 i);
+ void EffectPickPocket(uint32 i);
+ void EffectAddFarsight(uint32 i);
+ void EffectSummonWild(uint32 i);
+ void EffectSummonGuardian(uint32 i);
+ void EffectHealMechanical(uint32 i);
+ void EffectTeleUnitsFaceCaster(uint32 i);
+ void EffectLearnSkill(uint32 i);
+ void EffectAddHonor(uint32 i);
+ void EffectTradeSkill(uint32 i);
+ void EffectEnchantItemPerm(uint32 i);
+ void EffectEnchantItemTmp(uint32 i);
+ void EffectTameCreature(uint32 i);
+ void EffectSummonPet(uint32 i);
+ void EffectLearnPetSpell(uint32 i);
+ void EffectWeaponDmg(uint32 i);
+ void EffectForceCast(uint32 i);
+ void EffectTriggerSpell(uint32 i);
+ void EffectTriggerMissileSpell(uint32 i);
+ void EffectThreat(uint32 i);
+ void EffectHealMaxHealth(uint32 i);
+ void EffectInterruptCast(uint32 i);
+ void EffectSummonObjectWild(uint32 i);
+ void EffectScriptEffect(uint32 i);
+ void EffectSanctuary(uint32 i);
+ void EffectAddComboPoints(uint32 i);
+ void EffectDuel(uint32 i);
+ void EffectStuck(uint32 i);
+ void EffectSummonPlayer(uint32 i);
+ void EffectActivateObject(uint32 i);
+ void EffectSummonTotem(uint32 i);
+ void EffectEnchantHeldItem(uint32 i);
+ void EffectSummonObject(uint32 i);
+ void EffectResurrect(uint32 i);
+ void EffectParry(uint32 i);
+ void EffectMomentMove(uint32 i);
+ void EffectTransmitted(uint32 i);
+ void EffectDisEnchant(uint32 i);
+ void EffectInebriate(uint32 i);
+ void EffectFeedPet(uint32 i);
+ void EffectDismissPet(uint32 i);
+ void EffectReputation(uint32 i);
+ void EffectSelfResurrect(uint32 i);
+ void EffectSkinning(uint32 i);
+ void EffectCharge(uint32 i);
+ void EffectProspecting(uint32 i);
+ void EffectSendTaxi(uint32 i);
+ void EffectSummonCritter(uint32 i);
+ void EffectKnockBack(uint32 i);
+ void EffectPlayerPull(uint32 i);
+ void EffectDispelMechanic(uint32 i);
+ void EffectSummonDeadPet(uint32 i);
+ void EffectDestroyAllTotems(uint32 i);
+ void EffectDurabilityDamage(uint32 i);
+ void EffectSkill(uint32 i);
+ void EffectTaunt(uint32 i);
+ void EffectDurabilityDamagePCT(uint32 i);
+ void EffectModifyThreatPercent(uint32 i);
+ void EffectResurrectNew(uint32 i);
+ void EffectAddExtraAttacks(uint32 i);
+ void EffectSpiritHeal(uint32 i);
+ void EffectSkinPlayerCorpse(uint32 i);
+ void EffectSummonDemon(uint32 i);
+ void EffectStealBeneficialBuff(uint32 i);
+ void EffectUnlearnSpecialization(uint32 i);
+ void EffectHealPct(uint32 i);
+ void EffectEnergisePct(uint32 i);
+ void EffectTriggerSpellWithValue(uint32 i);
+ void EffectTriggerRitualOfSummoning(uint32 i);
+ void EffectKillCredit(uint32 i);
+ void EffectQuestFail(uint32 i);
+
+ Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL );
+ ~Spell();
+
+ void prepare(SpellCastTargets * targets, Aura* triggeredByAura = NULL);
+ void cancel();
+ void update(uint32 difftime);
+ void cast(bool skipCheck = false);
+ void finish(bool ok = true);
+ void TakePower();
+ void TakeReagents();
+ void TakeCastItem();
+ void TriggerSpell();
+ uint8 CanCast(bool strict);
+ int16 PetCanCast(Unit* target);
+ bool CanAutoCast(Unit* target);
+
+ // handlers
+ void handle_immediate();
+ uint64 handle_delayed(uint64 t_offset);
+ // handler helpers
+ void _handle_immediate_phase();
+ void _handle_finish_phase();
+
+ uint8 CheckItems();
+ uint8 CheckRange(bool strict);
+ uint8 CheckPower();
+ uint8 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();
+ void DelayedChannel();
+ inline uint32 getState() const { return m_spellState; }
+ void setState(uint32 state) { m_spellState = state; }
+
+ void DoCreateItem(uint32 i, uint32 itemtype);
+
+ void WriteSpellGoTargets( WorldPacket * data );
+ void WriteAmmoToPacket( WorldPacket * data );
+ void FillTargetMap();
+
+ void SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap);
+
+ Unit* SelectMagnetTarget();
+ bool CheckTarget( Unit* target, uint32 eff, bool hitPhase );
+
+ void SendCastResult(uint8 result);
+ void SendSpellStart();
+ void SendSpellGo();
+ void SendSpellCooldown();
+ void SendLogExecute();
+ void SendInterrupted(uint8 result);
+ void SendChannelUpdate(uint32 time);
+ void SendChannelStart(uint32 duration);
+ void SendResurrectRequest(Player* target);
+ void SendPlaySpellVisual(uint32 SpellID);
+
+ void HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier = 1.0);
+ void HandleThreatSpells(uint32 spellId);
+ //void HandleAddAura(Unit* Target);
+
+ SpellEntry const* m_spellInfo;
+ int32 m_currentBasePoints[3]; // cache SpellEntry::EffectBasePoints and use for set custom base points
+ Item* m_CastItem;
+ uint8 m_cast_count;
+ SpellCastTargets m_targets;
+
+ int32 GetCastTime() const { return m_casttime; }
+ bool IsAutoRepeat() const { return m_autoRepeat; }
+ void SetAutoRepeat(bool rep) { m_autoRepeat = rep; }
+ void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; }
+ bool IsNextMeleeSwingSpell() const
+ {
+ return m_spellInfo->Attributes & (SPELL_ATTR_ON_NEXT_SWING_1|SPELL_ATTR_ON_NEXT_SWING_2);
+ }
+ bool IsRangedSpell() const
+ {
+ return m_spellInfo->Attributes & SPELL_ATTR_RANGED;
+ }
+ bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; }
+ bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
+ bool IsRangedAttackResetSpell() const { return !m_IsTriggeredSpell && IsRangedSpell() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
+
+ bool IsDeletable() const { return m_deletable; }
+ void SetDeletable(bool deletable) { m_deletable = deletable; }
+ uint64 GetDelayStart() const { return m_delayStart; }
+ void SetDelayStart(uint64 m_time) { m_delayStart = m_time; }
+ uint64 GetDelayMoment() const { return m_delayMoment; }
+
+ bool IsNeedSendToClient() const;
+
+ CurrentSpellTypes GetCurrentContainer();
+
+ Unit* GetCaster() const { return m_caster; }
+ Unit* GetOriginalCaster() const { return m_originalCaster; }
+ int32 GetPowerCost() const { return m_powerCost; }
+
+ void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc)
+
+ bool IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId);
+
+ bool CheckTargetCreatureType(Unit* target) const;
+
+ void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); }
+
+ void CleanupTargetList();
+ protected:
+
+ void SendLoot(uint64 guid, LootType loottype);
+
+ Unit* m_caster;
+
+ uint64 m_originalCasterGUID; // real source of cast (aura caster/etc), used for spell targets selection
+ // e.g. damage around area spell trigered by victim aura and da,age emeies of aura caster
+ Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers()
+
+ Spell** m_selfContainer; // pointer to our spell container (if applicable)
+ Spell** m_triggeringContainer; // pointer to container with spell that has triggered us
+
+ //Spell data
+ SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example)
+ WeaponAttackType m_attackType; // For weapon based attack
+ int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare
+ int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare
+ bool m_canReflect; // can reflect this spell?
+ bool m_autoRepeat;
+
+ uint8 m_delayAtDamageCount;
+ int32 GetNextDelayAtDamageMsTime() { return m_delayAtDamageCount < 5 ? 1000 - (m_delayAtDamageCount++)* 200 : 200; }
+
+ // Delayed spells system
+ uint64 m_delayStart; // time of spell delay start, filled by event handler, zero = just started
+ uint64 m_delayMoment; // moment of next delay call, used internally
+ bool m_immediateHandled; // were immediate actions handled? (used by delayed spells only)
+
+ // These vars are used in both delayed spell system and modified immediate spell system
+ bool m_deletable; // is the spell pending deletion or must be updated till permitted to delete?
+ bool m_needSpellLog; // need to send spell log?
+ uint8 m_applyMultiplierMask; // by effect: damage multiplier needed?
+ float m_damageMultipliers[3]; // by effect: damage multiplier
+
+ // Current targets, to be used in SpellEffects (MUST BE USED ONLY IN SPELL EFFECTS)
+ Unit* unitTarget;
+ Item* itemTarget;
+ GameObject* gameObjTarget;
+ int32 damage;
+
+ // this is set in Spell Hit, but used in Apply Aura handler
+ DiminishingLevels m_diminishLevel;
+ DiminishingGroup m_diminishGroup;
+
+ // -------------------------------------------
+ GameObject* focusObject;
+
+ //******************************************
+ // Spell trigger system
+ //******************************************
+ void doTriggers(SpellMissInfo missInfo, uint32 damage=0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, uint32 block=0, uint32 absorb=0, bool crit=false);
+
+ //*****************************************
+ // Spell target subsystem
+ //*****************************************
+ // Targets store structures and data
+ uint32 m_countOfHit;
+ uint32 m_countOfMiss;
+ struct TargetInfo
+ {
+ uint64 targetGUID;
+ uint64 timeDelay;
+ SpellMissInfo missCondition:8;
+ SpellMissInfo reflectResult:8;
+ uint8 effectMask:8;
+ bool processed:1;
+ };
+ std::list<TargetInfo> m_UniqueTargetInfo;
+ uint8 m_needAliveTargetMask; // Mask req. alive targets
+
+ struct GOTargetInfo
+ {
+ uint64 targetGUID;
+ uint64 timeDelay;
+ uint8 effectMask:8;
+ bool processed:1;
+ };
+ std::list<GOTargetInfo> m_UniqueGOTargetInfo;
+
+ struct ItemTargetInfo
+ {
+ Item *item;
+ uint8 effectMask;
+ };
+ std::list<ItemTargetInfo> m_UniqueItemInfo;
+
+ void AddUnitTarget(Unit* target, uint32 effIndex);
+ void AddUnitTarget(uint64 unitGUID, uint32 effIndex);
+ void AddGOTarget(GameObject* target, uint32 effIndex);
+ void AddGOTarget(uint64 goGUID, uint32 effIndex);
+ void AddItemTarget(Item* target, uint32 effIndex);
+ void DoAllEffectOnTarget(TargetInfo *target);
+ void DoSpellHitOnUnit(Unit *unit, uint32 effectMask);
+ void DoAllEffectOnTarget(GOTargetInfo *target);
+ void DoAllEffectOnTarget(ItemTargetInfo *target);
+ bool IsAliveUnitPresentInTargetList();
+ // -------------------------------------------
+
+ //List For Triggered Spells
+ typedef std::list<SpellEntry const*> TriggerSpells;
+ TriggerSpells m_TriggerSpells;
+
+ uint32 m_spellState;
+ uint32 m_timer;
+
+ float m_castPositionX;
+ float m_castPositionY;
+ float m_castPositionZ;
+ float m_castOrientation;
+ bool m_IsTriggeredSpell;
+
+ // if need this can be replaced by Aura copy
+ // we can't store original aura link to prevent access to deleted auras
+ // and in same time need aura data and after aura deleting.
+ SpellEntry const* m_triggeredByAuraSpell;
+};
+
+enum ReplenishType
+{
+ REPLENISH_UNDEFINED = 0,
+ REPLENISH_HEALTH = 20,
+ REPLENISH_MANA = 21,
+ REPLENISH_RAGE = 22
+};
+
+enum SpellTargets
+{
+ SPELL_TARGETS_HOSTILE,
+ SPELL_TARGETS_NOT_FRIENDLY,
+ SPELL_TARGETS_NOT_HOSTILE,
+ SPELL_TARGETS_FRIENDLY,
+ SPELL_TARGETS_AOE_DAMAGE
+};
+
+namespace MaNGOS
+{
+ struct MANGOS_DLL_DECL SpellNotifierPlayer
+ {
+ std::list<Unit*> &i_data;
+ Spell &i_spell;
+ const uint32& i_index;
+ float i_radius;
+ Unit* i_originalCaster;
+
+ SpellNotifierPlayer(Spell &spell, std::list<Unit*> &data, const uint32 &i, float radius)
+ : i_data(data), i_spell(spell), i_index(i), i_radius(radius)
+ {
+ i_originalCaster = i_spell.GetOriginalCaster();
+ }
+
+ void Visit(PlayerMapType &m)
+ {
+ if(!i_originalCaster)
+ return;
+
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ Player * pPlayer = itr->getSource();
+ if( !pPlayer->isAlive() || pPlayer->isInFlight())
+ continue;
+
+ if( i_originalCaster->IsFriendlyTo(pPlayer) )
+ continue;
+
+ if( pPlayer->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius )
+ i_data.push_back(pPlayer);
+ }
+ }
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL SpellNotifierCreatureAndPlayer
+ {
+ std::list<Unit*> *i_data;
+ Spell &i_spell;
+ const uint32& i_push_type;
+ float i_radius;
+ SpellTargets i_TargetType;
+ Unit* i_originalCaster;
+
+ SpellNotifierCreatureAndPlayer(Spell &spell, std::list<Unit*> &data, float radius, const uint32 &type,
+ SpellTargets TargetType = SPELL_TARGETS_NOT_FRIENDLY)
+ : i_data(&data), i_spell(spell), i_push_type(type), i_radius(radius), i_TargetType(TargetType)
+ {
+ i_originalCaster = spell.GetOriginalCaster();
+ }
+
+ template<class T> inline void Visit(GridRefManager<T> &m)
+ {
+ assert(i_data);
+
+ if(!i_originalCaster)
+ return;
+
+ for(typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if( !itr->getSource()->isAlive() || (itr->getSource()->GetTypeId() == TYPEID_PLAYER && ((Player*)itr->getSource())->isInFlight()))
+ continue;
+
+ switch (i_TargetType)
+ {
+ case SPELL_TARGETS_HOSTILE:
+ if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsHostileTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_NOT_FRIENDLY:
+ if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsFriendlyTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_NOT_HOSTILE:
+ if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsHostileTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_FRIENDLY:
+ if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsFriendlyTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_AOE_DAMAGE:
+ {
+ if(itr->getSource()->GetTypeId()==TYPEID_UNIT && ((Creature*)itr->getSource())->isTotem())
+ continue;
+ if(!itr->getSource()->isTargetableForAttack())
+ continue;
+
+ Unit* check = i_originalCaster->GetCharmerOrOwnerOrSelf();
+
+ if( check->GetTypeId()==TYPEID_PLAYER )
+ {
+ if (check->IsFriendlyTo( itr->getSource() ))
+ continue;
+ }
+ else
+ {
+ if (!check->IsHostileTo( itr->getSource() ))
+ continue;
+ }
+ }
+ break;
+ default: continue;
+ }
+
+ switch(i_push_type)
+ {
+ case PUSH_IN_FRONT:
+ if(i_spell.GetCaster()->isInFront((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_IN_BACK:
+ if(i_spell.GetCaster()->isInBack((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_SELF_CENTER:
+ if(i_spell.GetCaster()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_DEST_CENTER:
+ if((itr->getSource()->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_TARGET_CENTER:
+ if(i_spell.m_targets.getUnitTarget()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
+ i_data->push_back(itr->getSource());
+ break;
+ }
+ }
+ }
+
+ #ifdef WIN32
+ template<> inline void Visit(CorpseMapType & ) {}
+ template<> inline void Visit(GameObjectMapType & ) {}
+ template<> inline void Visit(DynamicObjectMapType & ) {}
+ #endif
+ };
+
+ #ifndef WIN32
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(CorpseMapType& ) {}
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(GameObjectMapType& ) {}
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(DynamicObjectMapType& ) {}
+ #endif
+}
+
+typedef void(Spell::*pEffect)(uint32 i);
+
+class SpellEvent : public BasicEvent
+{
+ public:
+ SpellEvent(Spell* spell);
+ virtual ~SpellEvent();
+
+ virtual bool Execute(uint64 e_time, uint32 p_time);
+ virtual void Abort(uint64 e_time);
+ protected:
+ Spell* m_Spell;
+};
+#endif
diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h
new file mode 100644
index 00000000000..e9dbbb12adf
--- /dev/null
+++ b/src/game/SpellAuraDefines.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef MANGOS_SPELLAURADEFINES_H
+#define MANGOS_SPELLAURADEFINES_H
+
+#define MAX_AURAS 56
+#define MAX_POSITIVE_AURAS 40
+
+enum AURA_FLAGS
+{
+ AFLAG_NEGATIVE = 0x09,
+ AFLAG_POSITIVE = 0x1F,
+ AFLAG_MASK = 0xFF
+};
+
+//m_schoolAbsorb
+enum DAMAGE_ABSORB_TYPE
+{
+ ALL_DAMAGE_ABSORB = -2,
+ ONLY_MAGIC_ABSORB = -1,
+};
+
+enum AuraType
+{
+ SPELL_AURA_NONE = 0,
+ SPELL_AURA_BIND_SIGHT = 1,
+ SPELL_AURA_MOD_POSSESS = 2,
+ SPELL_AURA_PERIODIC_DAMAGE = 3,
+ SPELL_AURA_DUMMY = 4,
+ SPELL_AURA_MOD_CONFUSE = 5,
+ SPELL_AURA_MOD_CHARM = 6,
+ SPELL_AURA_MOD_FEAR = 7,
+ SPELL_AURA_PERIODIC_HEAL = 8,
+ SPELL_AURA_MOD_ATTACKSPEED = 9,
+ SPELL_AURA_MOD_THREAT = 10,
+ SPELL_AURA_MOD_TAUNT = 11,
+ SPELL_AURA_MOD_STUN = 12,
+ SPELL_AURA_MOD_DAMAGE_DONE = 13,
+ SPELL_AURA_MOD_DAMAGE_TAKEN = 14,
+ SPELL_AURA_DAMAGE_SHIELD = 15,
+ SPELL_AURA_MOD_STEALTH = 16,
+ SPELL_AURA_MOD_DETECT = 17,
+ SPELL_AURA_MOD_INVISIBILITY = 18,
+ SPELL_AURA_MOD_INVISIBILITY_DETECTION = 19,
+ SPELL_AURA_OBS_MOD_HEALTH = 20, //20,21 unofficial
+ SPELL_AURA_OBS_MOD_MANA = 21,
+ SPELL_AURA_MOD_RESISTANCE = 22,
+ SPELL_AURA_PERIODIC_TRIGGER_SPELL = 23,
+ SPELL_AURA_PERIODIC_ENERGIZE = 24,
+ SPELL_AURA_MOD_PACIFY = 25,
+ SPELL_AURA_MOD_ROOT = 26,
+ SPELL_AURA_MOD_SILENCE = 27,
+ SPELL_AURA_REFLECT_SPELLS = 28,
+ SPELL_AURA_MOD_STAT = 29,
+ SPELL_AURA_MOD_SKILL = 30,
+ SPELL_AURA_MOD_INCREASE_SPEED = 31,
+ SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED = 32,
+ SPELL_AURA_MOD_DECREASE_SPEED = 33,
+ SPELL_AURA_MOD_INCREASE_HEALTH = 34,
+ SPELL_AURA_MOD_INCREASE_ENERGY = 35,
+ SPELL_AURA_MOD_SHAPESHIFT = 36,
+ SPELL_AURA_EFFECT_IMMUNITY = 37,
+ SPELL_AURA_STATE_IMMUNITY = 38,
+ SPELL_AURA_SCHOOL_IMMUNITY = 39,
+ SPELL_AURA_DAMAGE_IMMUNITY = 40,
+ SPELL_AURA_DISPEL_IMMUNITY = 41,
+ SPELL_AURA_PROC_TRIGGER_SPELL = 42,
+ SPELL_AURA_PROC_TRIGGER_DAMAGE = 43,
+ SPELL_AURA_TRACK_CREATURES = 44,
+ SPELL_AURA_TRACK_RESOURCES = 45,
+ SPELL_AURA_MOD_PARRY_SKILL = 46,
+ SPELL_AURA_MOD_PARRY_PERCENT = 47,
+ SPELL_AURA_MOD_DODGE_SKILL = 48,
+ SPELL_AURA_MOD_DODGE_PERCENT = 49,
+ SPELL_AURA_MOD_BLOCK_SKILL = 50,
+ SPELL_AURA_MOD_BLOCK_PERCENT = 51,
+ SPELL_AURA_MOD_CRIT_PERCENT = 52,
+ SPELL_AURA_PERIODIC_LEECH = 53,
+ SPELL_AURA_MOD_HIT_CHANCE = 54,
+ SPELL_AURA_MOD_SPELL_HIT_CHANCE = 55,
+ SPELL_AURA_TRANSFORM = 56,
+ SPELL_AURA_MOD_SPELL_CRIT_CHANCE = 57,
+ SPELL_AURA_MOD_INCREASE_SWIM_SPEED = 58,
+ SPELL_AURA_MOD_DAMAGE_DONE_CREATURE = 59,
+ SPELL_AURA_MOD_PACIFY_SILENCE = 60,
+ SPELL_AURA_MOD_SCALE = 61,
+ SPELL_AURA_PERIODIC_HEALTH_FUNNEL = 62,
+ SPELL_AURA_PERIODIC_MANA_FUNNEL = 63,
+ SPELL_AURA_PERIODIC_MANA_LEECH = 64,
+ SPELL_AURA_MOD_CASTING_SPEED = 65,
+ SPELL_AURA_FEIGN_DEATH = 66,
+ SPELL_AURA_MOD_DISARM = 67,
+ SPELL_AURA_MOD_STALKED = 68,
+ SPELL_AURA_SCHOOL_ABSORB = 69,
+ SPELL_AURA_EXTRA_ATTACKS = 70,
+ SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL = 71,
+ SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT = 72,
+ SPELL_AURA_MOD_POWER_COST_SCHOOL = 73,
+ SPELL_AURA_REFLECT_SPELLS_SCHOOL = 74,
+ SPELL_AURA_MOD_LANGUAGE = 75,
+ SPELL_AURA_FAR_SIGHT = 76,
+ SPELL_AURA_MECHANIC_IMMUNITY = 77,
+ SPELL_AURA_MOUNTED = 78,
+ SPELL_AURA_MOD_DAMAGE_PERCENT_DONE = 79,
+ SPELL_AURA_MOD_PERCENT_STAT = 80,
+ SPELL_AURA_SPLIT_DAMAGE_PCT = 81,
+ SPELL_AURA_WATER_BREATHING = 82,
+ SPELL_AURA_MOD_BASE_RESISTANCE = 83,
+ SPELL_AURA_MOD_REGEN = 84,
+ SPELL_AURA_MOD_POWER_REGEN = 85,
+ SPELL_AURA_CHANNEL_DEATH_ITEM = 86,
+ SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN = 87,
+ SPELL_AURA_MOD_HEALTH_REGEN_PERCENT = 88,
+ SPELL_AURA_PERIODIC_DAMAGE_PERCENT = 89,
+ SPELL_AURA_MOD_RESIST_CHANCE = 90,
+ SPELL_AURA_MOD_DETECT_RANGE = 91,
+ SPELL_AURA_PREVENTS_FLEEING = 92,
+ SPELL_AURA_MOD_UNATTACKABLE = 93,
+ SPELL_AURA_INTERRUPT_REGEN = 94,
+ SPELL_AURA_GHOST = 95,
+ SPELL_AURA_SPELL_MAGNET = 96,
+ SPELL_AURA_MANA_SHIELD = 97,
+ SPELL_AURA_MOD_SKILL_TALENT = 98,
+ SPELL_AURA_MOD_ATTACK_POWER = 99,
+ SPELL_AURA_AURAS_VISIBLE = 100,
+ SPELL_AURA_MOD_RESISTANCE_PCT = 101,
+ SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS = 102,
+ SPELL_AURA_MOD_TOTAL_THREAT = 103,
+ SPELL_AURA_WATER_WALK = 104,
+ SPELL_AURA_FEATHER_FALL = 105,
+ SPELL_AURA_HOVER = 106,
+ SPELL_AURA_ADD_FLAT_MODIFIER = 107,
+ SPELL_AURA_ADD_PCT_MODIFIER = 108,
+ SPELL_AURA_ADD_TARGET_TRIGGER = 109,
+ SPELL_AURA_MOD_POWER_REGEN_PERCENT = 110,
+ SPELL_AURA_ADD_CASTER_HIT_TRIGGER = 111,
+ SPELL_AURA_OVERRIDE_CLASS_SCRIPTS = 112,
+ SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN = 113,
+ SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT = 114,
+ SPELL_AURA_MOD_HEALING = 115,
+ SPELL_AURA_MOD_REGEN_DURING_COMBAT = 116,
+ SPELL_AURA_MOD_MECHANIC_RESISTANCE = 117,
+ SPELL_AURA_MOD_HEALING_PCT = 118,
+ SPELL_AURA_SHARE_PET_TRACKING = 119,
+ SPELL_AURA_UNTRACKABLE = 120,
+ SPELL_AURA_EMPATHY = 121,
+ SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT = 122,
+ SPELL_AURA_MOD_TARGET_RESISTANCE = 123,
+ SPELL_AURA_MOD_RANGED_ATTACK_POWER = 124,
+ SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN = 125,
+ SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT = 126,
+ SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS = 127,
+ SPELL_AURA_MOD_POSSESS_PET = 128,
+ SPELL_AURA_MOD_SPEED_ALWAYS = 129,
+ SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS = 130,
+ SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS = 131,
+ SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT = 132,
+ SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT = 133,
+ SPELL_AURA_MOD_MANA_REGEN_INTERRUPT = 134,
+ SPELL_AURA_MOD_HEALING_DONE = 135,
+ SPELL_AURA_MOD_HEALING_DONE_PERCENT = 136,
+ SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE = 137,
+ SPELL_AURA_MOD_HASTE = 138,
+ SPELL_AURA_FORCE_REACTION = 139,
+ SPELL_AURA_MOD_RANGED_HASTE = 140,
+ SPELL_AURA_MOD_RANGED_AMMO_HASTE = 141,
+ SPELL_AURA_MOD_BASE_RESISTANCE_PCT = 142,
+ SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE = 143,
+ SPELL_AURA_SAFE_FALL = 144,
+ SPELL_AURA_CHARISMA = 145,
+ SPELL_AURA_PERSUADED = 146,
+ SPELL_AURA_ADD_CREATURE_IMMUNITY = 147,
+ SPELL_AURA_RETAIN_COMBO_POINTS = 148,
+ SPELL_AURA_RESIST_PUSHBACK = 149, // Resist Pushback
+ SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT = 150,
+ SPELL_AURA_TRACK_STEALTHED = 151, // Track Stealthed
+ SPELL_AURA_MOD_DETECTED_RANGE = 152, // Mod Detected Range
+ SPELL_AURA_SPLIT_DAMAGE_FLAT = 153, // Split Damage Flat
+ SPELL_AURA_MOD_STEALTH_LEVEL = 154, // Stealth Level Modifier
+ SPELL_AURA_MOD_WATER_BREATHING = 155, // Mod Water Breathing
+ SPELL_AURA_MOD_REPUTATION_GAIN = 156, // Mod Reputation Gain
+ SPELL_AURA_PET_DAMAGE_MULTI = 157, // Mod Pet Damage
+ SPELL_AURA_MOD_SHIELD_BLOCKVALUE = 158,
+ SPELL_AURA_NO_PVP_CREDIT = 159,
+ SPELL_AURA_MOD_AOE_AVOIDANCE = 160,
+ SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT = 161,
+ SPELL_AURA_POWER_BURN_MANA = 162,
+ SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE = 163,
+ SPELL_AURA_164 = 164,
+ SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS = 165,
+ SPELL_AURA_MOD_ATTACK_POWER_PCT = 166,
+ SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT = 167,
+ SPELL_AURA_MOD_DAMAGE_DONE_VERSUS = 168,
+ SPELL_AURA_MOD_CRIT_PERCENT_VERSUS = 169,
+ SPELL_AURA_DETECT_AMORE = 170,
+ SPELL_AURA_MOD_SPEED_NOT_STACK = 171,
+ SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK = 172,
+ SPELL_AURA_ALLOW_CHAMPION_SPELLS = 173,
+ SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT = 174, // by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT
+ SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT = 175,
+ SPELL_AURA_SPIRIT_OF_REDEMPTION = 176,
+ SPELL_AURA_AOE_CHARM = 177,
+ SPELL_AURA_MOD_DEBUFF_RESISTANCE = 178,
+ SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE = 179,
+ SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS = 180,
+ SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS = 181, // unused - possible flat spell crit damage versus
+ SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT = 182,
+ SPELL_AURA_MOD_CRITICAL_THREAT = 183,
+ SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE = 184,
+ SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE= 185,
+ SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE = 186,
+ SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE = 187,
+ SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE = 188,
+ SPELL_AURA_MOD_RATING = 189,
+ SPELL_AURA_MOD_FACTION_REPUTATION_GAIN = 190,
+ SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED = 191,
+ SPELL_AURA_HASTE_MELEE = 192,
+ SPELL_AURA_MELEE_SLOW = 193,
+ SPELL_AURA_MOD_DEPRICATED_1 = 194, // not used now, old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT
+ SPELL_AURA_MOD_DEPRICATED_2 = 195, // not used now, old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT
+ SPELL_AURA_MOD_COOLDOWN = 196, // only 24818 Noxious Breath
+ SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE = 197,
+ SPELL_AURA_MOD_ALL_WEAPON_SKILLS = 198,
+ SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT = 199,
+ SPELL_AURA_MOD_XP_PCT = 200,
+ SPELL_AURA_FLY = 201,
+ 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, // unused
+ SPELL_AURA_MOD_SPEED_MOUNTED = 206, // ? used in strange spells
+ SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED = 207,
+ SPELL_AURA_MOD_SPEED_FLIGHT = 208,
+ SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS = 209,
+ SPELL_AURA_210 = 210, // unused
+ SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK = 211,
+ SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT = 212,
+ SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT = 213,
+ SPELL_AURA_214 = 214,
+ SPELL_AURA_ARENA_PREPARATION = 215,
+ SPELL_AURA_HASTE_SPELLS = 216,
+ SPELL_AURA_217 = 217,
+ 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_222 = 222,
+ SPELL_AURA_223 = 223,
+ SPELL_AURA_224 = 224,
+ SPELL_AURA_PRAYER_OF_MENDING = 225,
+ SPELL_AURA_PERIODIC_DUMMY = 226,
+ SPELL_AURA_227 = 227,
+ SPELL_AURA_DETECT_STEALTH = 228,
+ SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE = 229,
+ SPELL_AURA_230 = 230,
+ SPELL_AURA_231 = 231,
+ SPELL_AURA_MECHANIC_DURATION_MOD = 232,
+ SPELL_AURA_233 = 233,
+ SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK = 234,
+ SPELL_AURA_MOD_DISPEL_RESIST = 235,
+ SPELL_AURA_236 = 236,
+ SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER = 237,
+ SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER = 238,
+ SPELL_AURA_MOD_SCALE_2 = 239,
+ SPELL_AURA_MOD_EXPERTISE = 240,
+ SPELL_AURA_FORCE_MOVE_FORWARD = 241,
+ SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING = 242,
+ SPELL_AURA_243 = 243,
+ SPELL_AURA_COMPREHEND_LANGUAGE = 244,
+ SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS = 245,
+ SPELL_AURA_246 = 246,
+ SPELL_AURA_247 = 247,
+ SPELL_AURA_MOD_COMBAT_RESULT_CHANCE = 248,
+ SPELL_AURA_249 = 249,
+ SPELL_AURA_MOD_INCREASE_HEALTH_2 = 250,
+ SPELL_AURA_MOD_ENEMY_DODGE = 251,
+ SPELL_AURA_252 = 252,
+ SPELL_AURA_253 = 253,
+ SPELL_AURA_254 = 254,
+ SPELL_AURA_255 = 255,
+ SPELL_AURA_256 = 256,
+ SPELL_AURA_257 = 257,
+ SPELL_AURA_258 = 258,
+ SPELL_AURA_259 = 259,
+ SPELL_AURA_260 = 260,
+ SPELL_AURA_261 = 261,
+ TOTAL_AURAS=262
+};
+
+enum AreaAuraType
+{
+ AREA_AURA_PARTY,
+ AREA_AURA_FRIEND,
+ AREA_AURA_ENEMY,
+ AREA_AURA_PET,
+ AREA_AURA_OWNER
+};
+#endif
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
new file mode 100644
index 00000000000..e5abe4d842f
--- /dev/null
+++ b/src/game/SpellAuras.cpp
@@ -0,0 +1,6360 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Unit.h"
+#include "Spell.h"
+#include "SpellAuras.h"
+#include "DynamicObject.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Policies/SingletonImp.h"
+#include "Totem.h"
+#include "Creature.h"
+#include "Formulas.h"
+#include "BattleGround.h"
+#include "CreatureAI.h"
+#include "Util.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+#define NULL_AURA_SLOT 0xFF
+
+pAuraHandler AuraHandler[TOTAL_AURAS]=
+{
+ &Aura::HandleNULL, // 0 SPELL_AURA_NONE
+ &Aura::HandleBindSight, // 1 SPELL_AURA_BIND_SIGHT
+ &Aura::HandleModPossess, // 2 SPELL_AURA_MOD_POSSESS
+ &Aura::HandlePeriodicDamage, // 3 SPELL_AURA_PERIODIC_DAMAGE
+ &Aura::HandleAuraDummy, // 4 SPELL_AURA_DUMMY
+ &Aura::HandleModConfuse, // 5 SPELL_AURA_MOD_CONFUSE
+ &Aura::HandleModCharm, // 6 SPELL_AURA_MOD_CHARM
+ &Aura::HandleModFear, // 7 SPELL_AURA_MOD_FEAR
+ &Aura::HandlePeriodicHeal, // 8 SPELL_AURA_PERIODIC_HEAL
+ &Aura::HandleModAttackSpeed, // 9 SPELL_AURA_MOD_ATTACKSPEED
+ &Aura::HandleModThreat, // 10 SPELL_AURA_MOD_THREAT
+ &Aura::HandleModTaunt, // 11 SPELL_AURA_MOD_TAUNT
+ &Aura::HandleAuraModStun, // 12 SPELL_AURA_MOD_STUN
+ &Aura::HandleModDamageDone, // 13 SPELL_AURA_MOD_DAMAGE_DONE
+ &Aura::HandleNoImmediateEffect, // 14 SPELL_AURA_MOD_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleNoImmediateEffect, // 15 SPELL_AURA_DAMAGE_SHIELD implemented in Unit::DoAttackDamage
+ &Aura::HandleModStealth, // 16 SPELL_AURA_MOD_STEALTH
+ &Aura::HandleNoImmediateEffect, // 17 SPELL_AURA_MOD_STEALTH_DETECT
+ &Aura::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY
+ &Aura::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION
+ &Aura::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH
+ &Aura::HandleAuraModTotalManaPercentRegen, // 21 SPELL_AURA_OBS_MOD_MANA
+ &Aura::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE
+ &Aura::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL
+ &Aura::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE
+ &Aura::HandleAuraModPacify, // 25 SPELL_AURA_MOD_PACIFY
+ &Aura::HandleAuraModRoot, // 26 SPELL_AURA_MOD_ROOT
+ &Aura::HandleAuraModSilence, // 27 SPELL_AURA_MOD_SILENCE
+ &Aura::HandleNoImmediateEffect, // 28 SPELL_AURA_REFLECT_SPELLS implement in Unit::SpellHitResult
+ &Aura::HandleAuraModStat, // 29 SPELL_AURA_MOD_STAT
+ &Aura::HandleAuraModSkill, // 30 SPELL_AURA_MOD_SKILL
+ &Aura::HandleAuraModIncreaseSpeed, // 31 SPELL_AURA_MOD_INCREASE_SPEED
+ &Aura::HandleAuraModIncreaseMountedSpeed, // 32 SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED
+ &Aura::HandleAuraModDecreaseSpeed, // 33 SPELL_AURA_MOD_DECREASE_SPEED
+ &Aura::HandleAuraModIncreaseHealth, // 34 SPELL_AURA_MOD_INCREASE_HEALTH
+ &Aura::HandleAuraModIncreaseEnergy, // 35 SPELL_AURA_MOD_INCREASE_ENERGY
+ &Aura::HandleAuraModShapeshift, // 36 SPELL_AURA_MOD_SHAPESHIFT
+ &Aura::HandleAuraModEffectImmunity, // 37 SPELL_AURA_EFFECT_IMMUNITY
+ &Aura::HandleAuraModStateImmunity, // 38 SPELL_AURA_STATE_IMMUNITY
+ &Aura::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY
+ &Aura::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY
+ &Aura::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY
+ &Aura::HandleAuraProcTriggerSpell, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell
+ &Aura::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor
+ &Aura::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES
+ &Aura::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES
+ &Aura::HandleUnused, // 46 SPELL_AURA_MOD_PARRY_SKILL obsolete?
+ &Aura::HandleAuraModParryPercent, // 47 SPELL_AURA_MOD_PARRY_PERCENT
+ &Aura::HandleUnused, // 48 SPELL_AURA_MOD_DODGE_SKILL obsolete?
+ &Aura::HandleAuraModDodgePercent, // 49 SPELL_AURA_MOD_DODGE_PERCENT
+ &Aura::HandleUnused, // 50 SPELL_AURA_MOD_BLOCK_SKILL obsolete?
+ &Aura::HandleAuraModBlockPercent, // 51 SPELL_AURA_MOD_BLOCK_PERCENT
+ &Aura::HandleAuraModCritPercent, // 52 SPELL_AURA_MOD_CRIT_PERCENT
+ &Aura::HandlePeriodicLeech, // 53 SPELL_AURA_PERIODIC_LEECH
+ &Aura::HandleModHitChance, // 54 SPELL_AURA_MOD_HIT_CHANCE
+ &Aura::HandleModSpellHitChance, // 55 SPELL_AURA_MOD_SPELL_HIT_CHANCE
+ &Aura::HandleAuraTransform, // 56 SPELL_AURA_TRANSFORM
+ &Aura::HandleModSpellCritChance, // 57 SPELL_AURA_MOD_SPELL_CRIT_CHANCE
+ &Aura::HandleAuraModIncreaseSwimSpeed, // 58 SPELL_AURA_MOD_INCREASE_SWIM_SPEED
+ &Aura::HandleNoImmediateEffect, // 59 SPELL_AURA_MOD_DAMAGE_DONE_CREATURE implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleAuraModPacifyAndSilence, // 60 SPELL_AURA_MOD_PACIFY_SILENCE
+ &Aura::HandleAuraModScale, // 61 SPELL_AURA_MOD_SCALE
+ &Aura::HandleNULL, // 62 SPELL_AURA_PERIODIC_HEALTH_FUNNEL
+ &Aura::HandleUnused, // 63 SPELL_AURA_PERIODIC_MANA_FUNNEL obsolete?
+ &Aura::HandlePeriodicManaLeech, // 64 SPELL_AURA_PERIODIC_MANA_LEECH
+ &Aura::HandleModCastingSpeed, // 65 SPELL_AURA_MOD_CASTING_SPEED
+ &Aura::HandleFeignDeath, // 66 SPELL_AURA_FEIGN_DEATH
+ &Aura::HandleAuraModDisarm, // 67 SPELL_AURA_MOD_DISARM
+ &Aura::HandleAuraModStalked, // 68 SPELL_AURA_MOD_STALKED
+ &Aura::HandleSchoolAbsorb, // 69 SPELL_AURA_SCHOOL_ABSORB implemented in Unit::CalcAbsorbResist
+ &Aura::HandleUnused, // 70 SPELL_AURA_EXTRA_ATTACKS Useless, used by only one spell that has only visual effect
+ &Aura::HandleModSpellCritChanceShool, // 71 SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
+ &Aura::HandleModPowerCostPCT, // 72 SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT
+ &Aura::HandleModPowerCost, // 73 SPELL_AURA_MOD_POWER_COST_SCHOOL
+ &Aura::HandleNoImmediateEffect, // 74 SPELL_AURA_REFLECT_SPELLS_SCHOOL implemented in Unit::SpellHitResult
+ &Aura::HandleNoImmediateEffect, // 75 SPELL_AURA_MOD_LANGUAGE
+ &Aura::HandleFarSight, // 76 SPELL_AURA_FAR_SIGHT
+ &Aura::HandleModMechanicImmunity, // 77 SPELL_AURA_MECHANIC_IMMUNITY
+ &Aura::HandleAuraMounted, // 78 SPELL_AURA_MOUNTED
+ &Aura::HandleModDamagePercentDone, // 79 SPELL_AURA_MOD_DAMAGE_PERCENT_DONE
+ &Aura::HandleModPercentStat, // 80 SPELL_AURA_MOD_PERCENT_STAT
+ &Aura::HandleNoImmediateEffect, // 81 SPELL_AURA_SPLIT_DAMAGE_PCT
+ &Aura::HandleWaterBreathing, // 82 SPELL_AURA_WATER_BREATHING
+ &Aura::HandleModBaseResistance, // 83 SPELL_AURA_MOD_BASE_RESISTANCE
+ &Aura::HandleModRegen, // 84 SPELL_AURA_MOD_REGEN
+ &Aura::HandleModPowerRegen, // 85 SPELL_AURA_MOD_POWER_REGEN
+ &Aura::HandleChannelDeathItem, // 86 SPELL_AURA_CHANNEL_DEATH_ITEM
+ &Aura::HandleNoImmediateEffect, // 87 SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus
+ &Aura::HandleNoImmediateEffect, // 88 SPELL_AURA_MOD_HEALTH_REGEN_PERCENT
+ &Aura::HandlePeriodicDamagePCT, // 89 SPELL_AURA_PERIODIC_DAMAGE_PERCENT
+ &Aura::HandleUnused, // 90 SPELL_AURA_MOD_RESIST_CHANCE Useless
+ &Aura::HandleNoImmediateEffect, // 91 SPELL_AURA_MOD_DETECT_RANGE implemented in Creature::GetAttackDistance
+ &Aura::HandlePreventFleeing, // 92 SPELL_AURA_PREVENTS_FLEEING
+ &Aura::HandleModUnattackable, // 93 SPELL_AURA_MOD_UNATTACKABLE
+ &Aura::HandleNoImmediateEffect, // 94 SPELL_AURA_INTERRUPT_REGEN implemented in Player::RegenerateAll
+ &Aura::HandleAuraGhost, // 95 SPELL_AURA_GHOST
+ &Aura::HandleNoImmediateEffect, // 96 SPELL_AURA_SPELL_MAGNET implemented in Spell::SelectMagnetTarget
+ &Aura::HandleManaShield, // 97 SPELL_AURA_MANA_SHIELD implemented in Unit::CalcAbsorbResist
+ &Aura::HandleAuraModSkill, // 98 SPELL_AURA_MOD_SKILL_TALENT
+ &Aura::HandleAuraModAttackPower, // 99 SPELL_AURA_MOD_ATTACK_POWER
+ &Aura::HandleUnused, //100 SPELL_AURA_AURAS_VISIBLE obsolete? all player can see all auras now
+ &Aura::HandleModResistancePercent, //101 SPELL_AURA_MOD_RESISTANCE_PCT
+ &Aura::HandleNoImmediateEffect, //102 SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModTotalThreat, //103 SPELL_AURA_MOD_TOTAL_THREAT
+ &Aura::HandleAuraWaterWalk, //104 SPELL_AURA_WATER_WALK
+ &Aura::HandleAuraFeatherFall, //105 SPELL_AURA_FEATHER_FALL
+ &Aura::HandleAuraHover, //106 SPELL_AURA_HOVER
+ &Aura::HandleAddModifier, //107 SPELL_AURA_ADD_FLAT_MODIFIER
+ &Aura::HandleAddModifier, //108 SPELL_AURA_ADD_PCT_MODIFIER
+ &Aura::HandleNoImmediateEffect, //109 SPELL_AURA_ADD_TARGET_TRIGGER
+ &Aura::HandleModPowerRegenPCT, //110 SPELL_AURA_MOD_POWER_REGEN_PERCENT
+ &Aura::HandleNULL, //111 SPELL_AURA_ADD_CASTER_HIT_TRIGGER
+ &Aura::HandleNoImmediateEffect, //112 SPELL_AURA_OVERRIDE_CLASS_SCRIPTS
+ &Aura::HandleNoImmediateEffect, //113 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //114 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraHealing, //115 SPELL_AURA_MOD_HEALING
+ &Aura::HandleNoImmediateEffect, //116 SPELL_AURA_MOD_REGEN_DURING_COMBAT
+ &Aura::HandleNoImmediateEffect, //117 SPELL_AURA_MOD_MECHANIC_RESISTANCE implemented in Unit::MagicSpellHitResult
+ &Aura::HandleAuraHealingPct, //118 SPELL_AURA_MOD_HEALING_PCT
+ &Aura::HandleUnused, //119 SPELL_AURA_SHARE_PET_TRACKING useless
+ &Aura::HandleAuraUntrackable, //120 SPELL_AURA_UNTRACKABLE
+ &Aura::HandleAuraEmpathy, //121 SPELL_AURA_EMPATHY
+ &Aura::HandleModOffhandDamagePercent, //122 SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT
+ &Aura::HandleModTargetResistance, //123 SPELL_AURA_MOD_TARGET_RESISTANCE
+ &Aura::HandleAuraModRangedAttackPower, //124 SPELL_AURA_MOD_RANGED_ATTACK_POWER
+ &Aura::HandleNoImmediateEffect, //125 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //126 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus
+ &Aura::HandleNoImmediateEffect, //127 SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleModPossessPet, //128 SPELL_AURA_MOD_POSSESS_PET
+ &Aura::HandleAuraModIncreaseSpeed, //129 SPELL_AURA_MOD_SPEED_ALWAYS
+ &Aura::HandleAuraModIncreaseMountedSpeed, //130 SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS
+ &Aura::HandleNoImmediateEffect, //131 SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModIncreaseEnergyPercent, //132 SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT
+ &Aura::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT
+ &Aura::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT
+ &Aura::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE
+ &Aura::HandleAuraHealingPct, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT implemented in Unit::SpellHealingBonus
+ &Aura::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE
+ &Aura::HandleHaste, //138 SPELL_AURA_MOD_HASTE
+ &Aura::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION
+ &Aura::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE
+ &Aura::HandleRangedAmmoHaste, //141 SPELL_AURA_MOD_RANGED_AMMO_HASTE
+ &Aura::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT
+ &Aura::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE
+ &Aura::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes
+ &Aura::HandleUnused, //145 SPELL_AURA_CHARISMA obsolete?
+ &Aura::HandleUnused, //146 SPELL_AURA_PERSUADED obsolete?
+ &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY
+ &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS
+ &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_PUSHBACK
+ &Aura::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT
+ &Aura::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED
+ &Aura::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAttackDistance
+ &Aura::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT
+ &Aura::HandleNoImmediateEffect, //154 SPELL_AURA_MOD_STEALTH_LEVEL
+ &Aura::HandleNoImmediateEffect, //155 SPELL_AURA_MOD_WATER_BREATHING
+ &Aura::HandleNoImmediateEffect, //156 SPELL_AURA_MOD_REPUTATION_GAIN
+ &Aura::HandleNULL, //157 SPELL_AURA_PET_DAMAGE_MULTI
+ &Aura::HandleShieldBlockValue, //158 SPELL_AURA_MOD_SHIELD_BLOCKVALUE
+ &Aura::HandleNoImmediateEffect, //159 SPELL_AURA_NO_PVP_CREDIT only for Honorless Target spell
+ &Aura::HandleNoImmediateEffect, //160 SPELL_AURA_MOD_AOE_AVOIDANCE implemended in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //161 SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT
+ &Aura::HandleAuraPowerBurn, //162 SPELL_AURA_POWER_BURN_MANA
+ &Aura::HandleNoImmediateEffect, //163 SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE
+ &Aura::HandleUnused, //164 useless, only one test spell
+ &Aura::HandleAuraAttackPowerAttacker, //165 SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus
+ &Aura::HandleAuraModAttackPowerPercent, //166 SPELL_AURA_MOD_ATTACK_POWER_PCT
+ &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 only for Detect Amore spell
+ &Aura::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK
+ &Aura::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK
+ &Aura::HandleUnused, //173 SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell
+ &Aura::HandleModSpellDamagePercentFromStat, //174 SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT implemented in Unit::SpellBaseDamageBonus (by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT)
+ &Aura::HandleModSpellHealingPercentFromStat, //175 SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT implemented in Unit::SpellBaseHealingBonus
+ &Aura::HandleSpiritOfRedemption, //176 SPELL_AURA_SPIRIT_OF_REDEMPTION only for Spirit of Redemption spell, die at aura end
+ &Aura::HandleNULL, //177 SPELL_AURA_AOE_CHARM
+ &Aura::HandleNoImmediateEffect, //178 SPELL_AURA_MOD_DEBUFF_RESISTANCE implemented in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //179 SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE implemented in Unit::SpellCriticalBonus
+ &Aura::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus
+ &Aura::HandleUnused, //181 SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS unused
+ &Aura::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT
+ &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT
+ &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
+ &Aura::HandleNoImmediateEffect, //187 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE implemended in Unit::GetUnitCriticalChance
+ &Aura::HandleNoImmediateEffect, //188 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance
+ &Aura::HandleModRating, //189 SPELL_AURA_MOD_RATING
+ &Aura::HandleNULL, //190 SPELL_AURA_MOD_FACTION_REPUTATION_GAIN
+ &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::HandleUnused, //194 SPELL_AURA_MOD_DEPRICATED_1 not used now (old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT)
+ &Aura::HandleUnused, //195 SPELL_AURA_MOD_DEPRICATED_2 not used now (old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT)
+ &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN
+ &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance
+ &Aura::HandleUnused, //198 SPELL_AURA_MOD_ALL_WEAPON_SKILLS
+ &Aura::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult
+ &Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP
+ &Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode...
+ &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::DoAttackDamage
+ &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::DoAttackDamage
+ &Aura::HandleNULL, //205 vulnerable to school dmg?
+ &Aura::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED
+ &Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED
+ &Aura::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_SPEED_FLIGHT, used only in spell: Flight Form (Passive)
+ &Aura::HandleAuraModIncreaseFlightSpeed, //209 SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS
+ &Aura::HandleNULL, //210 Commentator's Command
+ &Aura::HandleAuraModIncreaseFlightSpeed, //211 SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK
+ &Aura::HandleAuraModRangedAttackPowerOfStatPercent, //212 SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT
+ &Aura::HandleNoImmediateEffect, //213 SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT implemented in Player::RewardRage
+ &Aura::HandleNULL, //214 Tamed Pet Passive
+ &Aura::HandleArenaPreparation, //215 SPELL_AURA_ARENA_PREPARATION
+ &Aura::HandleModCastingSpeed, //216 SPELL_AURA_HASTE_SPELLS
+ &Aura::HandleUnused, //217 unused
+ &Aura::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED
+ &Aura::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT
+ &Aura::HandleNULL, //220 SPELL_AURA_MOD_RATING_FROM_STAT
+ &Aura::HandleNULL, //221 ignored
+ &Aura::HandleUnused, //222 unused
+ &Aura::HandleNULL, //223 Cold Stare
+ &Aura::HandleUnused, //224 unused
+ &Aura::HandleNoImmediateEffect, //225 SPELL_AURA_PRAYER_OF_MENDING
+ &Aura::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY
+ &Aura::HandleNULL, //227 periodic trigger spell
+ &Aura::HandleNoImmediateEffect, //228 stealth detection
+ &Aura::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE
+ &Aura::HandleAuraModIncreaseMaxHealth, //230 Commanding Shout
+ &Aura::HandleNULL, //231
+ &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 m_modifier.m_miscvalue
+ &Aura::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration
+ &Aura::HandleAuraModDispelResist, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult
+ &Aura::HandleUnused, //236 unused
+ &Aura::HandleModSpellDamagePercentFromAttackPower, //237 SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER implemented in Unit::SpellBaseDamageBonus
+ &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::HandleUnused, //243 used by two test spells
+ &Aura::HandleComprehendLanguage, //244 Comprehend language
+ &Aura::HandleUnused, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS
+ &Aura::HandleUnused, //246 unused
+ &Aura::HandleUnused, //247 unused
+ &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst
+ &Aura::HandleNULL, //249
+ &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2
+ &Aura::HandleNULL, //251 SPELL_AURA_MOD_ENEMY_DODGE
+ &Aura::HandleUnused, //252 unused
+ &Aura::HandleUnused, //253 unused
+ &Aura::HandleUnused, //254 unused
+ &Aura::HandleUnused, //255 unused
+ &Aura::HandleUnused, //256 unused
+ &Aura::HandleUnused, //257 unused
+ &Aura::HandleUnused, //258 unused
+ &Aura::HandleUnused, //259 unused
+ &Aura::HandleUnused, //260 unused
+ &Aura::HandleNULL //261 SPELL_AURA_261 some phased state (44856 spell)
+};
+
+Aura::Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) :
+m_procCharges(0), m_spellmod(NULL), m_effIndex(eff), m_caster_guid(0), m_target(target),
+m_timeCla(1000), m_castItemGuid(castItem?castItem->GetGUID():0), m_auraSlot(MAX_AURAS),
+m_positive(false), m_permanent(false), m_isPeriodic(false), m_isTrigger(false), m_isAreaAura(false),
+m_isPersistent(false), m_updated(false), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_isRemovedOnShapeLost(true), m_in_use(false),
+m_periodicTimer(0), m_PeriodicEventId(0), m_AuraDRGroup(DIMINISHING_NONE)
+{
+ assert(target);
+
+ assert(spellproto && spellproto == sSpellStore.LookupEntry( spellproto->Id ) && "`info` must be pointer to sSpellStore element");
+
+ m_spellProto = spellproto;
+
+ m_currentBasePoints = currentBasePoints ? *currentBasePoints : m_spellProto->EffectBasePoints[eff];
+
+ m_isPassive = IsPassiveSpell(GetId());
+ m_positive = IsPositiveEffect(GetId(), m_effIndex);
+
+ m_applyTime = time(NULL);
+
+ int32 damage;
+ if(!caster)
+ {
+ m_caster_guid = target->GetGUID();
+ damage = m_currentBasePoints+1; // stored value-1
+ m_maxduration = target->CalculateSpellDuration(m_spellProto, m_effIndex, target);
+ }
+ else
+ {
+ m_caster_guid = caster->GetGUID();
+
+ damage = caster->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,target);
+ m_maxduration = caster->CalculateSpellDuration(m_spellProto, m_effIndex, target);
+
+ if (!damage && castItem && castItem->GetItemSuffixFactor())
+ {
+ ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId()));
+ if(item_rand_suffix)
+ {
+ for (int k=0; k<3; k++)
+ {
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]);
+ if(pEnchant)
+ {
+ for (int t=0; t<3; t++)
+ if(pEnchant->spellid[t] == m_spellProto->Id)
+ {
+ damage = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+
+ if(damage)
+ break;
+ }
+ }
+ }
+ }
+
+ if(m_maxduration == -1 || m_isPassive && m_spellProto->DurationIndex == 0)
+ m_permanent = true;
+
+ Player* modOwner = caster ? caster->GetSpellModOwner() : NULL;
+
+ if(!m_permanent && modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration);
+
+ m_duration = m_maxduration;
+
+ if(modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_periodicTimer);
+
+ sLog.outDebug("Aura: construct Spellid : %u, Aura : %u Duration : %d Target : %d Damage : %d", m_spellProto->Id, m_spellProto->EffectApplyAuraName[eff], m_maxduration, m_spellProto->EffectImplicitTargetA[eff],damage);
+
+ m_effIndex = eff;
+ SetModifier(AuraType(m_spellProto->EffectApplyAuraName[eff]), damage, m_spellProto->EffectAmplitude[eff], m_spellProto->EffectMiscValue[eff]);
+
+ m_isDeathPersist = IsDeathPersistentSpell(m_spellProto);
+
+ if(m_spellProto->procCharges)
+ {
+ m_procCharges = m_spellProto->procCharges;
+
+ if(modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges);
+ }
+ else
+ m_procCharges = -1;
+
+ m_isRemovedOnShapeLost = (m_caster_guid==m_target->GetGUID() && m_spellProto->Stances &&
+ !(m_spellProto->AttributesEx2 & 0x80000) && !(m_spellProto->Attributes & 0x10000));
+}
+
+Aura::~Aura()
+{
+}
+
+AreaAura::AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ m_isAreaAura = true;
+
+ // caster==NULL in constructor args if target==caster in fact
+ Unit* caster_ptr = caster ? caster : target;
+
+ m_radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex]));
+ if(Player* modOwner = caster_ptr->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, m_radius);
+
+ switch(spellproto->Effect[eff])
+ {
+ case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
+ m_areaAuraType = AREA_AURA_PARTY;
+ if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isTotem())
+ m_modifier.m_auraname = SPELL_AURA_NONE;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+ m_areaAuraType = AREA_AURA_FRIEND;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
+ m_areaAuraType = AREA_AURA_ENEMY;
+ if(target == caster_ptr)
+ m_modifier.m_auraname = SPELL_AURA_NONE; // Do not do any effect on self
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_PET:
+ m_areaAuraType = AREA_AURA_PET;
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_OWNER:
+ m_areaAuraType = AREA_AURA_OWNER;
+ if(target == caster_ptr)
+ m_modifier.m_auraname = SPELL_AURA_NONE;
+ break;
+ default:
+ sLog.outError("Wrong spell effect in AreaAura constructor");
+ ASSERT(false);
+ break;
+ }
+}
+
+AreaAura::~AreaAura()
+{
+}
+
+PersistentAreaAura::PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ m_isPersistent = true;
+}
+
+PersistentAreaAura::~PersistentAreaAura()
+{
+}
+
+SingleEnemyTargetAura::SingleEnemyTargetAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target,
+Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem)
+{
+ if (caster)
+ m_casters_target_guid = caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)caster)->GetSelection() : caster->GetUInt64Value(UNIT_FIELD_TARGET);
+ else
+ m_casters_target_guid = 0;
+}
+
+SingleEnemyTargetAura::~SingleEnemyTargetAura()
+{
+}
+
+Unit* SingleEnemyTargetAura::GetTriggerTarget() const
+{
+ return ObjectAccessor::GetUnit(*m_target, m_casters_target_guid);
+}
+
+Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem)
+{
+ if (IsAreaAuraEffect(spellproto->Effect[eff]))
+ return new AreaAura(spellproto, eff, currentBasePoints, target, caster, castItem);
+
+ uint32 triggeredSpellId = spellproto->EffectTriggerSpell[eff];
+
+ SpellEntry const* triggredSpellInfo = sSpellStore.LookupEntry(triggeredSpellId);
+ if (triggredSpellInfo)
+ for (int i = 0; i < 3; ++i)
+ if (triggredSpellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_ENEMY)
+ return new SingleEnemyTargetAura(spellproto, eff, currentBasePoints, target, caster, castItem);
+
+ return new Aura(spellproto, eff, currentBasePoints, target, caster, castItem);
+}
+
+Unit* Aura::GetCaster() const
+{
+ if(m_caster_guid==m_target->GetGUID())
+ return m_target;
+
+ //return ObjectAccessor::GetUnit(*m_target,m_caster_guid);
+ //must return caster even if it's in another grid/map
+ Unit *unit = ObjectAccessor::GetObjectInWorld(m_caster_guid, (Unit*)NULL);
+ return unit && unit->IsInWorld() ? unit : NULL;
+}
+
+void Aura::SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue)
+{
+ m_modifier.m_auraname = t;
+ m_modifier.m_amount = a;
+ m_modifier.m_miscvalue = miscValue;
+ m_modifier.periodictime = pt;
+}
+
+void Aura::Update(uint32 diff)
+{
+ if (m_duration > 0)
+ {
+ m_duration -= diff;
+ if (m_duration < 0)
+ m_duration = 0;
+ m_timeCla -= diff;
+
+ // GetEffIndex()==0 prevent double/triple apply manaPerSecond/manaPerSecondPerLevel to same spell with many auras
+ // all spells with manaPerSecond/manaPerSecondPerLevel have aura in effect 0
+ if(GetEffIndex()==0 && m_timeCla <= 0)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Powers powertype = Powers(m_spellProto->powerType);
+ int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel();
+ m_timeCla = 1000;
+ if (manaPerSecond)
+ {
+ if(powertype==POWER_HEALTH)
+ caster->ModifyHealth(-manaPerSecond);
+ else
+ caster->ModifyPower(powertype,-manaPerSecond);
+ }
+ }
+ }
+ }
+
+ // Channeled aura required check distance from caster
+ if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID())
+ {
+ Unit* caster = GetCaster();
+ if(!caster)
+ {
+ m_target->RemoveAura(GetId(),GetEffIndex());
+ return;
+ }
+
+ // Get spell range
+ float radius;
+ SpellModOp mod;
+ if (m_spellProto->EffectRadiusIndex[GetEffIndex()])
+ {
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellProto->EffectRadiusIndex[GetEffIndex()]));
+ mod = SPELLMOD_RADIUS;
+ }
+ else
+ {
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex));
+ mod = SPELLMOD_RANGE;
+ }
+
+ if(Player* modOwner = caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), mod, radius,NULL);
+
+ if(!caster->IsWithinDistInMap(m_target,radius))
+ {
+ m_target->RemoveAura(GetId(),GetEffIndex());
+ return;
+ }
+ }
+
+ if(m_isPeriodic && (m_duration >= 0 || m_isPassive || m_permanent))
+ {
+ m_periodicTimer -= diff;
+ if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N
+ {
+ if( m_modifier.m_auraname == SPELL_AURA_MOD_REGEN ||
+ m_modifier.m_auraname == SPELL_AURA_MOD_POWER_REGEN ||
+ // Cannibalize, eating items and other spells
+ m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH ||
+ // Eating items and other spells
+ m_modifier.m_auraname == SPELL_AURA_OBS_MOD_MANA )
+ {
+ ApplyModifier(true);
+ return;
+ }
+ // update before applying (aura can be removed in TriggerSpell or PeriodicTick calls)
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_isTrigger)
+ TriggerSpell();
+ else
+ PeriodicTick();
+ }
+ }
+}
+
+void AreaAura::Update(uint32 diff)
+{
+ // update for the caster of the aura
+ if(m_caster_guid == m_target->GetGUID())
+ {
+ Unit* caster = m_target;
+
+ if( !caster->hasUnitState(UNIT_STAT_ISOLATED) )
+ {
+ Unit* owner = caster->GetCharmerOrOwner();
+ if (!owner)
+ owner = caster;
+ std::list<Unit *> targets;
+
+ switch(m_areaAuraType)
+ {
+ case AREA_AURA_PARTY:
+ {
+ Group *pGroup = NULL;
+
+ if (owner->GetTypeId() == TYPEID_PLAYER)
+ pGroup = ((Player*)owner)->GetGroup();
+
+ if( pGroup)
+ {
+ uint8 subgroup = ((Player*)owner)->GetSubGroup();
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+ if(Target && Target->isAlive() && Target->GetSubGroup()==subgroup && caster->IsFriendlyTo(Target))
+ {
+ if(caster->IsWithinDistInMap(Target, m_radius))
+ targets.push_back(Target);
+ Pet *pet = Target->GetPet();
+ if(pet && pet->isAlive() && caster->IsWithinDistInMap(pet, m_radius))
+ targets.push_back(pet);
+ }
+ }
+ }
+ else
+ {
+ // add owner
+ if( owner != caster && caster->IsWithinDistInMap(owner, m_radius) )
+ targets.push_back(owner);
+ // add caster's pet
+ Unit* pet = caster->GetPet();
+ if( pet && caster->IsWithinDistInMap(pet, m_radius))
+ targets.push_back(pet);
+ }
+ break;
+ }
+ case AREA_AURA_FRIEND:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyFriendlyUnitInObjectRangeCheck u_check(caster, owner, m_radius);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck> searcher(targets, u_check);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyFriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ break;
+ }
+ case AREA_AURA_ENEMY:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(caster, owner, m_radius); // No GetCharmer in searcher
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(targets, u_check);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(caster->GetMapId(), caster));
+ break;
+ }
+ case AREA_AURA_OWNER:
+ case AREA_AURA_PET:
+ {
+ if(owner != caster)
+ targets.push_back(owner);
+ break;
+ }
+ }
+
+ for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end(); tIter++)
+ {
+ if((*tIter)->HasAura(GetId(), m_effIndex))
+ continue;
+
+ if(SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(GetSpellProto(), (*tIter)->getLevel()))
+ {
+ int32 actualBasePoints = m_currentBasePoints;
+ // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?)
+ if(actualSpellInfo != GetSpellProto())
+ actualBasePoints = actualSpellInfo->EffectBasePoints[m_effIndex];
+ AreaAura *aur = new AreaAura(actualSpellInfo, m_effIndex, &actualBasePoints, (*tIter), caster, NULL);
+ (*tIter)->AddAura(aur);
+ }
+ }
+ }
+ Aura::Update(diff);
+ }
+ else // aura at non-caster
+ {
+ Unit * tmp_target = m_target;
+ Unit* caster = GetCaster();
+ uint32 tmp_spellId = GetId(), tmp_effIndex = m_effIndex;
+
+ // WARNING: the aura may get deleted during the update
+ // DO NOT access its members after update!
+ Aura::Update(diff);
+
+ // remove aura if out-of-range from caster (after teleport for example)
+ // or caster is isolated or caster no longer has the aura
+ // or caster is (no longer) friendly
+ bool needFriendly = (m_areaAuraType == AREA_AURA_ENEMY ? false : true);
+ if( !caster || caster->hasUnitState(UNIT_STAT_ISOLATED) ||
+ !caster->IsWithinDistInMap(tmp_target, m_radius) ||
+ !caster->HasAura(tmp_spellId, tmp_effIndex) ||
+ caster->IsFriendlyTo(tmp_target) != needFriendly
+ )
+ {
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ else if( m_areaAuraType == AREA_AURA_PARTY) // check if in same sub group
+ {
+ // not check group if target == owner or target == pet
+ if (caster->GetCharmerOrOwnerGUID() != tmp_target->GetGUID() && caster->GetGUID() != tmp_target->GetCharmerOrOwnerGUID())
+ {
+ Player* check = caster->GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ Group *pGroup = check ? check->GetGroup() : NULL;
+ if( pGroup )
+ {
+ Player* checkTarget = tmp_target->GetCharmerOrOwnerPlayerOrPlayerItself();
+ if(!checkTarget || !pGroup->SameSubGroup(check, checkTarget))
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ else
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ }
+ else if( m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER )
+ {
+ if( tmp_target->GetGUID() != caster->GetCharmerOrOwnerGUID() )
+ tmp_target->RemoveAura(tmp_spellId, tmp_effIndex);
+ }
+ }
+}
+
+void PersistentAreaAura::Update(uint32 diff)
+{
+ bool remove = false;
+
+ // remove the aura if its caster or the dynamic object causing it was removed
+ // or if the target moves too far from the dynamic object
+ Unit *caster = GetCaster();
+ if (caster)
+ {
+ DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex());
+ if (dynObj)
+ {
+ if (!m_target->IsWithinDistInMap(dynObj, dynObj->GetRadius()))
+ remove = true;
+ }
+ else
+ remove = true;
+ }
+ else
+ remove = true;
+
+ Unit *tmp_target = m_target;
+ uint32 tmp_id = GetId(), tmp_index = GetEffIndex();
+
+ // WARNING: the aura may get deleted during the update
+ // DO NOT access its members after update!
+ Aura::Update(diff);
+
+ if(remove)
+ tmp_target->RemoveAura(tmp_id, tmp_index);
+}
+
+void Aura::ApplyModifier(bool apply, bool Real)
+{
+ AuraType aura = m_modifier.m_auraname;
+
+ m_in_use = true;
+ if(aura<TOTAL_AURAS)
+ (*this.*AuraHandler [aura])(apply,Real);
+ m_in_use = false;
+}
+
+void Aura::UpdateAuraDuration()
+{
+ if(m_auraSlot >= MAX_AURAS || m_isPassive)
+ return;
+
+ if( m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5);
+ data << (uint8)m_auraSlot << (uint32)m_duration;
+ ((Player*)m_target)->SendDirectMessage(&data);
+
+ data.Initialize(SMSG_SET_EXTRA_AURA_INFO, (8+1+4+4+4));
+ data.append(m_target->GetPackGUID());
+ data << uint8(m_auraSlot);
+ data << uint32(GetId());
+ data << uint32(GetAuraMaxDuration());
+ data << uint32(GetAuraDuration());
+ ((Player*)m_target)->SendDirectMessage(&data);
+ }
+
+ // not send in case player loading (will not work anyway until player not added to map), sent in visibility change code
+ if(m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target)
+ SendAuraDurationForCaster((Player*)caster);
+}
+
+void Aura::SendAuraDurationForCaster(Player* caster)
+{
+ WorldPacket data(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE, (8+1+4+4+4));
+ data.append(m_target->GetPackGUID());
+ data << uint8(m_auraSlot);
+ data << uint32(GetId());
+ data << uint32(GetAuraMaxDuration()); // full
+ data << uint32(GetAuraDuration()); // remain
+ caster->GetSession()->SendPacket(&data);
+}
+
+void Aura::_AddAura()
+{
+ if (!GetId())
+ return;
+ if(!m_target)
+ return;
+
+ // we can found aura in NULL_AURA_SLOT and then need store state instead check slot != NULL_AURA_SLOT
+ bool samespell = false;
+ bool secondaura = false;
+ uint8 slot = NULL_AURA_SLOT;
+
+ for(uint8 i = 0; i < 3; i++)
+ {
+ Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i);
+ for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr)
+ {
+ // allow use single slot only by auras from same caster
+ if(itr->second->GetCasterGUID()==GetCasterGUID())
+ {
+ samespell = true;
+ if (m_effIndex > itr->second->GetEffIndex())
+ secondaura = true;
+ slot = itr->second->GetAuraSlot();
+ break;
+ }
+ }
+
+ if(samespell)
+ break;
+ }
+
+ // not call total regen auras at adding
+ switch (m_modifier.m_auraname)
+ {
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ case SPELL_AURA_OBS_MOD_MANA:
+ m_periodicTimer = m_modifier.periodictime;
+ break;
+ case SPELL_AURA_MOD_REGEN:
+ case SPELL_AURA_MOD_POWER_REGEN:
+ case SPELL_AURA_MOD_MANA_REGEN_FROM_STAT:
+ m_periodicTimer = 5000;
+ break;
+ }
+
+ // register aura
+ if (getDiminishGroup() != DIMINISHING_NONE )
+ m_target->ApplyDiminishingAura(getDiminishGroup(),true);
+
+ Unit* caster = GetCaster();
+
+ // passive auras (except totem auras) do not get placed in the slots
+ // area auras with SPELL_AURA_NONE are not shown on target
+ if((!m_isPassive || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) &&
+ (m_spellProto->Effect[GetEffIndex()] != SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || m_target != caster))
+ {
+ if(!samespell) // new slot need
+ {
+ if (IsPositive()) // empty positive slot
+ {
+ for (uint8 i = 0; i < MAX_POSITIVE_AURAS; i++)
+ {
+ if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0)
+ {
+ slot = i;
+ break;
+ }
+ }
+ }
+ else // empty negative slot
+ {
+ for (uint8 i = MAX_POSITIVE_AURAS; i < MAX_AURAS; i++)
+ {
+ if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0)
+ {
+ slot = i;
+ break;
+ }
+ }
+ }
+
+ SetAuraSlot( slot );
+
+ // Not update fields for not first spell's aura, all data already in fields
+ if(!secondaura)
+ {
+ if(slot < MAX_AURAS) // slot found
+ {
+ SetAura(slot, false);
+ SetAuraFlag(slot, true);
+ SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+ UpdateAuraCharges();
+
+ // update for out of range group members
+ m_target->UpdateAuraForGroup(slot);
+ }
+
+ UpdateAuraDuration();
+ }
+ }
+ else // use found slot
+ {
+ SetAuraSlot( slot );
+ // Not recalculate stack count for second aura of the same spell
+ if (!secondaura)
+ UpdateSlotCounterAndDuration(true);
+ }
+
+ // Update Seals information
+ if( IsSealSpell(GetSpellProto()) )
+ m_target->ModifyAuraState(AURA_STATE_JUDGEMENT, true);
+
+ // Conflagrate aura state
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4))
+ m_target->ModifyAuraState(AURA_STATE_IMMOLATE, true);
+
+ if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10))
+ {
+ m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, true);
+ }
+ }
+}
+
+void Aura::_RemoveAura()
+{
+ // Remove all triggered by aura spells vs unlimited duration
+ // except same aura replace case
+ if(m_removeMode!=AURA_REMOVE_BY_STACK)
+ CleanupTriggeredSpells();
+
+ Unit* caster = GetCaster();
+
+ if(caster && IsPersistent())
+ {
+ DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex());
+ if (dynObj)
+ dynObj->RemoveAffected(m_target);
+ }
+
+ // unregister aura
+ if (getDiminishGroup() != DIMINISHING_NONE )
+ m_target->ApplyDiminishingAura(getDiminishGroup(),false);
+
+ //passive auras do not get put in slots
+ // Note: but totem can be not accessible for aura target in time remove (to far for find in grid)
+ //if(m_isPassive && !(caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem()))
+ // return;
+
+ uint8 slot = GetAuraSlot();
+
+ if(slot >= MAX_AURAS) // slot not set
+ return;
+
+ if(m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + slot)) == 0)
+ return;
+
+ bool samespell = false;
+ bool sameaura = false;
+
+ // find other aura in same slot (current already removed from list)
+ for(uint8 i = 0; i < 3; i++)
+ {
+ Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i);
+ for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr)
+ {
+ if(itr->second->GetAuraSlot()==slot)
+ {
+ samespell = true;
+
+ if(GetEffIndex()==i)
+ sameaura = true;
+
+ break;
+ }
+ }
+ if(samespell)
+ break;
+ }
+
+ // only remove icon when the last aura of the spell is removed (current aura already removed from list)
+ if (!samespell)
+ {
+ SetAura(slot, true);
+ SetAuraFlag(slot, false);
+ SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
+
+ SetAuraApplication(slot, 0);
+ // update for out of range group members
+ m_target->UpdateAuraForGroup(slot);
+
+ if( IsSealSpell(GetSpellProto()) )
+ m_target->ModifyAuraState(AURA_STATE_JUDGEMENT,false);
+
+ // Conflagrate aura state
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4))
+ m_target->ModifyAuraState(AURA_STATE_IMMOLATE, false);
+
+ // Swiftmend aura state
+ if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10))
+ {
+ bool found = false;
+ Unit::AuraList const& RejorRegr = m_target->GetAurasByType(SPELL_AURA_PERIODIC_HEAL);
+ for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i)
+ {
+ if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, false);
+ }
+
+ // reset cooldown state for spells
+ if(caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE )
+ ((Player*)caster)->SendCooldownEvent(GetSpellProto());
+ }
+ }
+ else if(sameaura) // decrease count for spell, only for same aura effect, or this spell auras in remove proccess.
+ UpdateSlotCounterAndDuration(false);
+}
+
+void Aura::SetAuraFlag(uint32 slot, bool add)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAFLAGS + index);
+ val &= ~((uint32)AFLAG_MASK << byte);
+ if(add)
+ {
+ if (IsPositive())
+ val |= ((uint32)AFLAG_POSITIVE << byte);
+ else
+ val |= ((uint32)AFLAG_NEGATIVE << byte);
+ }
+ m_target->SetUInt32Value(UNIT_FIELD_AURAFLAGS + index, val);
+}
+
+void Aura::SetAuraLevel(uint32 slot,uint32 level)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURALEVELS + index);
+ val &= ~(0xFF << byte);
+ val |= (level << byte);
+ m_target->SetUInt32Value(UNIT_FIELD_AURALEVELS + index, val);
+}
+
+void Aura::SetAuraApplication(uint32 slot, int8 count)
+{
+ uint32 index = slot / 4;
+ uint32 byte = (slot % 4) * 8;
+ uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index);
+ val &= ~(0xFF << byte);
+ val |= ((uint8(count)) << byte);
+ m_target->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index, val);
+}
+
+void Aura::UpdateSlotCounterAndDuration(bool add)
+{
+ uint8 slot = GetAuraSlot();
+ if(slot >= MAX_AURAS)
+ return;
+
+ // calculate amount of similar auras by same effect index (similar different spells)
+ int8 count = 0;
+
+ // calculate auras and update durations in case aura adding
+ Unit::AuraList const& aura_list = m_target->GetAurasByType(GetModifier()->m_auraname);
+ for(Unit::AuraList::const_iterator i = aura_list.begin();i != aura_list.end(); ++i)
+ {
+ if( (*i)->GetId()==GetId() && (*i)->GetEffIndex()==m_effIndex &&
+ (*i)->GetCasterGUID()==GetCasterGUID() )
+ {
+ ++count;
+
+ if(add)
+ (*i)->SetAuraDuration(GetAuraDuration());
+ }
+ }
+
+ // at aura add aura not added yet, at aura remove aura already removed
+ // in field stored (count-1)
+ if(!add)
+ --count;
+
+ SetAuraApplication(slot, count);
+
+ UpdateAuraDuration();
+}
+
+/*********************************************************/
+/*** BASIC AURA FUNCTION ***/
+/*********************************************************/
+void Aura::HandleAddModifier(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER || !Real)
+ return;
+
+ SpellEntry const *spellInfo = GetSpellProto();
+ if(!spellInfo)
+ return;
+
+ if(m_modifier.m_miscvalue >= MAX_SPELLMOD)
+ return;
+
+ if (apply)
+ {
+ // Add custom charges for some mod aura
+ switch (m_spellProto->Id)
+ {
+ case 17941: // Shadow Trance
+ case 22008: // Netherwind Focus
+ case 34936: // Backlash
+ m_procCharges = 1;
+ break;
+ }
+
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SpellModOp(m_modifier.m_miscvalue);
+ mod->value = m_modifier.m_amount;
+ mod->type = SpellModType(m_modifier.m_auraname); // SpellModType value == spell aura types
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+
+ uint64 spellAffectMask = spellmgr.GetSpellAffectMask(GetId(), m_effIndex);
+
+ if (spellAffectMask)
+ mod->mask = spellAffectMask;
+ else
+ mod->mask = spellInfo->EffectItemType[m_effIndex];
+
+ if (m_procCharges > 0)
+ mod->charges = m_procCharges;
+ else
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ uint64 spellFamilyMask = m_spellmod->mask;
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+
+ // reapply some passive spells after add/remove related spellmods
+ if(spellInfo->SpellFamilyName==SPELLFAMILY_WARRIOR && (spellFamilyMask & 0x0000100000000000LL))
+ {
+ m_target->RemoveAurasDueToSpell(45471);
+
+ if(apply)
+ m_target->CastSpell(m_target,45471,true);
+ }
+}
+
+void Aura::TriggerSpell()
+{
+ Unit* caster = GetCaster();
+ Unit* target = GetTriggerTarget();
+
+ if(!caster || !target)
+ return;
+
+ // generic casting code with custom spells and target/caster customs
+ uint32 trigger_spell_id = GetSpellProto()->EffectTriggerSpell[m_effIndex];
+
+ uint64 originalCasterGUID = GetCasterGUID();
+
+ SpellEntry const *triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id);
+ SpellEntry const *auraSpellInfo = GetSpellProto();
+ uint32 auraId = auraSpellInfo->Id;
+
+ // specific code for cases with no trigger spell provided in field
+ if (triggredSpellInfo == NULL)
+ {
+ switch(auraSpellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch(auraId)
+ {
+ // Firestone Passive (1-5 rangs)
+ case 758:
+ case 17945:
+ case 17947:
+ case 17949:
+ case 27252:
+ {
+ if (caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+ Item* item = ((Player*)caster)->GetWeaponForAttack(BASE_ATTACK);
+ if (!item)
+ return;
+ uint32 enchant_id = 0;
+ switch (GetId())
+ {
+ case 758: enchant_id = 1803; break; // Rank 1
+ case 17945: enchant_id = 1823; break; // Rank 2
+ case 17947: enchant_id = 1824; break; // Rank 3
+ case 17949: enchant_id = 1825; break; // Rank 4
+ case 27252: enchant_id = 2645; break; // Rank 5
+ default:
+ return;
+ }
+ // remove old enchanting before applying new
+ ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, m_modifier.periodictime+1000, 0);
+ // add new enchanting
+ ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,true);
+ return;
+ }
+// // Periodic Mana Burn
+// case 812: break;
+// // Polymorphic Ray
+// case 6965: break;
+// // Fire Nova (1-7 Rangs)
+// 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:
+ {
+ int32 heal = caster->GetMaxHealth() / 10;
+ caster->ModifyHealth( heal );
+ caster->SendHealSpellLog(caster, 23493, heal);
+
+ int32 mana = caster->GetMaxPower(POWER_MANA);
+ if (mana)
+ {
+ mana /= 10;
+ caster->ModifyPower( POWER_MANA, mana );
+ caster->SendEnergizeSpellLog(caster, 23493, mana, POWER_MANA);
+ }
+ break;
+ }
+// // 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: break;
+// // Steam Tank Passive
+// case 27747: break;
+// // Frost Blast
+// case 27808: break;
+// // Detonate Mana
+// case 27819: break;
+// // 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: trigger_spell_id = 28713; 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:
+ {
+ // move loot to player inventory and despawn target
+ if(caster->GetTypeId() ==TYPEID_PLAYER &&
+ target->GetTypeId() == TYPEID_UNIT &&
+ ((Creature*)target)->GetCreatureInfo()->type == CREATURE_TYPE_GAS_CLOUD)
+ {
+ Player* player = (Player*)caster;
+ Creature* creature = (Creature*)target;
+ // missing lootid has been reported on startup - just return
+ if (!creature->GetCreatureInfo()->SkinLootId)
+ {
+ return;
+ }
+ Loot *loot = &creature->loot;
+ loot->clear();
+ loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, NULL);
+ for(uint8 i=0;i<loot->items.size();i++)
+ {
+ LootItem *item = loot->LootItemInSlot(i,player);
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count );
+ if ( msg == EQUIP_ERR_OK )
+ {
+ Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId);
+
+ player->SendNewItem(newitem, uint32(item->count), false, false, true);
+ }
+ else
+ player->SendEquipError( msg, NULL, NULL );
+ }
+ creature->setDeathState(JUST_DIED);
+ creature->RemoveCorpse();
+ creature->SetHealth(0); // just for nice GM-mode view
+ }
+ return;
+ break;
+ }
+ // 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:
+ {
+ m_target->CastSpell(m_target,31350,true);
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ return;
+ }
+ // Spellcloth
+ case 31373:
+ {
+ // Summon Elemental after create item
+ 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:
+ {
+ // cast 24 spells 34269-34289, 34314-34316
+ for(uint32 spell_id = 34269; spell_id != 34290; ++spell_id)
+ caster->CastSpell(m_target,spell_id,true);
+ for(uint32 spell_id = 34314; spell_id != 34317; ++spell_id)
+ caster->CastSpell(m_target,spell_id,true);
+ 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:
+ {
+ m_target->CastSpell(m_target, 38530, true);
+ return;
+ }
+ // Absorb Eye of Grillok (Zezzak's Shard)
+ case 38554:
+ {
+ if(m_target->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ caster->CastSpell(caster, 38495, true);
+
+ Creature* creatureTarget = (Creature*)m_target;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+ 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: 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
+ 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;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ switch(auraId)
+ {
+ // Invisibility
+ case 66:
+ {
+ if(!m_duration)
+ m_target->CastSpell(m_target, 32612, true, NULL, this);
+ return;
+ }
+ default:
+ break;
+ }
+ 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)
+ {
+ // Cat Form
+ // trigger_spell_id not set and unknown effect triggered in this case, ignoring for while
+ case 768:
+ return;
+ // Frenzied Regeneration
+ case 22842:
+ case 22895:
+ case 22896:
+ case 26999:
+ {
+ int32 LifePerRage = GetModifier()->m_amount;
+
+ int32 lRage = m_target->GetPower(POWER_RAGE);
+ if(lRage > 100) // rage stored as rage*10
+ lRage = 100;
+ m_target->ModifyPower(POWER_RAGE, -lRage);
+ int32 FRTriggerBasePoints = int32(lRage*LifePerRage/10);
+ m_target->CastCustomSpell(m_target,22845,&FRTriggerBasePoints,NULL,NULL,true,NULL,this);
+ return;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+
+// 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;
+// }
+ case SPELLFAMILY_SHAMAN:
+ {
+ switch(auraId)
+ {
+ // Lightning Shield (The Earthshatterer set trigger after cast Lighting Shield)
+ 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 & 0x0000000000000400L)
+ return;
+ }
+ target->RemoveAurasDueToSpell(28820);
+ return;
+ }
+ // Totemic Mastery (Skyshatter Regalia (Shaman Tier 6) - bonus)
+ case 38443:
+ {
+ bool all = true;
+ for(int i = 0; i < MAX_TOTEM; ++i)
+ {
+ if(!caster->m_TotemSlot[i])
+ {
+ all = false;
+ break;
+ }
+ }
+
+ if(all)
+ caster->CastSpell(caster,38437,true);
+ else
+ caster->RemoveAurasDueToSpell(38437);
+ return;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ // Reget trigger spell proto
+ triggredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id);
+ if(triggredSpellInfo == NULL)
+ {
+ sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex());
+ return;
+ }
+ }
+ else
+ {
+ // Spell exist but require costum 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 insteed stacking
+
+ // prevent cast by triggered auras
+ if(m_caster_guid == m_target->GetGUID())
+ return;
+
+ // stop triggering after each affected stats lost > 90
+ int32 intelectLoss = 0;
+ int32 spiritLoss = 0;
+
+ Unit::AuraList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT);
+ for(Unit::AuraList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i)
+ {
+ if ((*i)->GetId() == 1010)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case STAT_INTELLECT: intelectLoss += (*i)->GetModifier()->m_amount; break;
+ case STAT_SPIRIT: spiritLoss += (*i)->GetModifier()->m_amount; break;
+ default: break;
+ }
+ }
+ }
+
+ if(intelectLoss <= -90 && spiritLoss <= -90)
+ return;
+
+ caster = target;
+ originalCasterGUID = 0;
+ break;
+ }
+ // Mana Tide
+ case 16191:
+ {
+ caster->CastCustomSpell(target, trigger_spell_id, &m_modifier.m_amount, NULL, NULL, true, NULL, this, originalCasterGUID);
+ return;
+ }
+ }
+ }
+ // All ok cast by default case
+ Spell *spell = new Spell(caster, triggredSpellInfo, true, originalCasterGUID );
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( target );
+
+ // if spell create dynamic object extract area from it
+ if(DynamicObject* dynObj = caster->GetDynObject(GetId()))
+ targets.setDestination(dynObj->GetPositionX(),dynObj->GetPositionY(),dynObj->GetPositionZ());
+
+ spell->prepare(&targets, this);
+}
+
+/*********************************************************/
+/*** AURA EFFECTS ***/
+/*********************************************************/
+
+void Aura::HandleAuraDummy(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ Unit* caster = GetCaster();
+
+ // AT APPLY
+ if(apply)
+ {
+ switch(GetId())
+ {
+ case 1515: // Tame beast
+ // FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness
+ if( caster && m_target->CanHaveThreatList())
+ m_target->AddThreat(caster, 10.0f);
+ return;
+ case 13139: // net-o-matic
+ // root to self part of (root_target->charge->root_self sequence
+ if(caster)
+ caster->CastSpell(caster,13138,true,NULL,this);
+ return;
+ case 39850: // Rocket Blast
+ if(roll_chance_i(20)) // backfire stun
+ m_target->CastSpell(m_target, 51581, true, NULL, this);
+ return;
+ case 46354: // Blood Elf Illusion
+ if(caster)
+ {
+ switch(caster->getGender())
+ {
+ case GENDER_FEMALE:
+ caster->CastSpell(m_target,46356,true,NULL,this);
+ break;
+ case GENDER_MALE:
+ caster->CastSpell(m_target,46355,true,NULL,this);
+ break;
+ default:
+ break;
+ }
+ }
+ return;
+ case 46699: // Requires No Ammo
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use
+ return;
+ }
+
+ // Earth Shield
+ if ( caster && GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags & 0x40000000000LL))
+ {
+ // prevent double apply bonuses
+ if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
+ m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target);
+ return;
+ }
+ }
+ // AT REMOVE
+ else
+ {
+ if( m_target->GetTypeId() == TYPEID_PLAYER &&
+ ( GetSpellProto()->Effect[0]==72 || GetSpellProto()->Effect[0]==6 &&
+ ( GetSpellProto()->EffectApplyAuraName[0]==1 || GetSpellProto()->EffectApplyAuraName[0]==128 ) ) )
+ {
+ // spells with SpellEffect=72 and aura=4: 6196, 6197, 21171, 21425
+ m_target->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
+ ((Player*)m_target)->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ if( (IsQuestTameSpell(GetId())) && caster && caster->isAlive() && m_target->isAlive())
+ {
+ uint32 finalSpelId = 0;
+ switch(GetId())
+ {
+ case 19548: finalSpelId = 19597; break;
+ case 19674: finalSpelId = 19677; break;
+ case 19687: finalSpelId = 19676; break;
+ case 19688: finalSpelId = 19678; break;
+ case 19689: finalSpelId = 19679; break;
+ case 19692: finalSpelId = 19680; break;
+ case 19693: finalSpelId = 19684; break;
+ case 19694: finalSpelId = 19681; break;
+ case 19696: finalSpelId = 19682; break;
+ case 19697: finalSpelId = 19683; break;
+ case 19699: finalSpelId = 19685; break;
+ case 19700: finalSpelId = 19686; break;
+ case 30646: finalSpelId = 30647; break;
+ case 30653: finalSpelId = 30648; break;
+ case 30654: finalSpelId = 30652; break;
+ case 30099: finalSpelId = 30100; break;
+ case 30102: finalSpelId = 30103; break;
+ case 30105: finalSpelId = 30104; break;
+ }
+
+ if(finalSpelId)
+ caster->CastSpell(m_target,finalSpelId,true,NULL,this);
+ return;
+ }
+ // Dark Fiend
+ if(GetId()==45934)
+ {
+ // Kill target if dispeled
+ if (m_removeMode==AURA_REMOVE_BY_DISPEL)
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ return;
+ }
+
+ // Burning Winds
+ if(GetId()==46308) // casted only at creatures at spawn
+ {
+ m_target->CastSpell(m_target,47287,true,NULL,this);
+ return;
+ }
+ }
+
+ // AT APPLY & REMOVE
+
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ // Unstable Power
+ if( GetId()==24658 )
+ {
+ uint32 spellId = 24659;
+ if (apply)
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(spellId);
+ if (!spell)
+ return;
+ for (int i=0; i < spell->StackAmount; ++i)
+ caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID());
+ return;
+ }
+ m_target->RemoveAurasDueToSpell(spellId);
+ return;
+ }
+ // Restless Strength
+ if( GetId()==24661 )
+ {
+ uint32 spellId = 24662;
+ if (apply)
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(spellId);
+ if (!spell)
+ return;
+ for (int i=0; i < spell->StackAmount; ++i)
+ caster->CastSpell(m_target, spell->Id, true, NULL, NULL, GetCasterGUID());
+ return;
+ }
+ m_target->RemoveAurasDueToSpell(spellId);
+ return;
+ }
+ // Victorious
+ if(GetId()==32216 && m_target->getClass()==CLASS_WARRIOR)
+ {
+ m_target->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, apply);
+ return;
+ }
+ //Summon Fire Elemental
+ if (GetId() == 40133 && caster)
+ {
+ Unit *owner = caster->GetOwner();
+ if (owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(apply)
+ owner->CastSpell(owner,8985,true);
+ else
+ ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+ }
+ return;
+ }
+
+ //Summon Earth Elemental
+ if (GetId() == 40132 && caster)
+ {
+ Unit *owner = caster->GetOwner();
+ if (owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(apply)
+ owner->CastSpell(owner,19704,true);
+ else
+ ((Player*)owner)->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Hypothermia
+ if( GetId()==41425 )
+ {
+ m_target->ModifyAuraState(AURA_STATE_HYPOTHERMIA,apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Lifebloom
+ if ( GetSpellProto()->SpellFamilyFlags & 0x1000000000LL )
+ {
+ if ( apply )
+ {
+ if ( caster )
+ // prevent double apply bonuses
+ if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
+ m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target);
+ }
+ else
+ {
+ // Final heal only on dispelled or duration end
+ if ( !(GetAuraDuration() <= 0 || m_removeMode==AURA_REMOVE_BY_DISPEL) )
+ return;
+
+ // 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
+ m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID());
+ }
+ return;
+ }
+
+ // Predatory Strikes
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetSpellProto()->SpellIconID == 1563)
+ {
+ ((Player*)m_target)->UpdateAttackPowerAndDamage();
+ return;
+ }
+ // Idol of the Emerald Queen
+ if ( GetId() == 34246 && m_target->GetTypeId()==TYPEID_PLAYER )
+ {
+ if(apply)
+ {
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_DOT;
+ mod->value = m_modifier.m_amount/7;
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ mod->mask = 0x001000000000LL;
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Improved Aspect of the Viper
+ if( GetId()==38390 && m_target->GetTypeId()==TYPEID_PLAYER )
+ {
+ if(apply)
+ {
+ // + effect value for Aspect of the Viper
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_EFFECT1;
+ mod->value = m_modifier.m_amount;
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ mod->mask = 0x4000000000000LL;
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ // 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_modifier.m_amount;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = GetId();
+ mod->effectId = m_effIndex;
+ mod->lastAffected = NULL;
+ switch (m_effIndex)
+ {
+ case 0:
+ mod->mask = 0x00200000000LL; // Windfury Totem
+ break;
+ case 1:
+ mod->mask = 0x00400000000LL; // Flametongue Totem
+ break;
+ }
+ mod->charges = 0;
+
+ m_spellmod = mod;
+ }
+
+ ((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+ return;
+ }
+ break;
+ }
+ }
+
+ // pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(GetId()))
+ {
+ if(apply)
+ m_target->AddPetAura(petSpell);
+ else
+ m_target->RemovePetAura(petSpell);
+ return;
+ }
+}
+
+void Aura::HandleAuraPeriodicDummy(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ SpellEntry const*spell = GetSpellProto();
+ switch( spell->SpellFamilyName)
+ {
+ case SPELLFAMILY_ROGUE:
+ {
+ // Master of Subtlety
+ if (spell->Id==31666 && !apply && Real)
+ {
+ m_target->RemoveAurasDueToSpell(31665);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Aspect of the Viper
+ if (spell->SpellFamilyFlags&0x0004000000000000LL)
+ {
+ // Update regen on remove
+ if (!apply && m_target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_target)->UpdateManaRegen();
+ break;
+ }
+ break;
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleAuraMounted(bool apply, bool Real)
+{
+ if(apply)
+ {
+ CreatureInfo const* ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue);
+ if(!ci)
+ {
+ sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)", m_modifier.m_miscvalue);
+ return;
+ }
+
+ uint32 team = 0;
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_target)->GetTeam();
+
+ uint32 display_id = objmgr.ChooseDisplayId(team,ci);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (minfo)
+ display_id = minfo->modelid;
+
+ m_target->Mount(display_id);
+ }
+ else
+ {
+ m_target->Unmount();
+ }
+}
+
+void Aura::HandleAuraWaterWalk(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_WATER_WALK, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_LAND_WALK, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleAuraFeatherFall(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_FEATHER_FALL, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_NORMAL_FALL, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)0;
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleAuraHover(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_HOVER, 8+4);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_HOVER, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+}
+
+void Aura::HandleWaterBreathing(bool apply, bool Real)
+{
+ if(apply)
+ m_target->waterbreath = true;
+ else if(m_target->GetAurasByType(SPELL_AURA_WATER_BREATHING).empty())
+ {
+ m_target->waterbreath = false;
+
+ // update for enable timer in case not moving target
+ if(m_target->GetTypeId()==TYPEID_PLAYER && m_target->IsInWorld())
+ {
+ ((Player*)m_target)->UpdateUnderwaterState(m_target->GetMap(),m_target->GetPositionX(),m_target->GetPositionY(),m_target->GetPositionZ());
+ ((Player*)m_target)->HandleDrowning();
+ }
+ }
+}
+
+void Aura::HandleAuraModShapeshift(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ uint32 modelid = 0;
+ Powers PowerType = POWER_MANA;
+ ShapeshiftForm form = ShapeshiftForm(m_modifier.m_miscvalue);
+ switch(form)
+ {
+ case FORM_CAT:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 892;
+ else
+ modelid = 8571;
+ PowerType = POWER_ENERGY;
+ break;
+ case FORM_TRAVEL:
+ modelid = 632;
+ break;
+ case FORM_AQUA:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2428;
+ else
+ modelid = 2428;
+ break;
+ case FORM_BEAR:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2281;
+ else
+ modelid = 2289;
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_GHOUL:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 10045;
+ break;
+ case FORM_DIREBEAR:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 2281;
+ else
+ modelid = 2289;
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_CREATUREBEAR:
+ modelid = 902;
+ break;
+ case FORM_GHOSTWOLF:
+ modelid = 4613;
+ break;
+ case FORM_FLIGHT:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 20857;
+ else
+ modelid = 20872;
+ break;
+ case FORM_MOONKIN:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 15374;
+ else
+ modelid = 15375;
+ break;
+ case FORM_FLIGHT_EPIC:
+ if(Player::TeamForRace(m_target->getRace())==ALLIANCE)
+ modelid = 21243;
+ else
+ modelid = 21244;
+ break;
+ case FORM_AMBIENT:
+ case FORM_SHADOW:
+ case FORM_STEALTH:
+ break;
+ case FORM_TREE:
+ modelid = 864;
+ break;
+ case FORM_BATTLESTANCE:
+ case FORM_BERSERKERSTANCE:
+ case FORM_DEFENSIVESTANCE:
+ PowerType = POWER_RAGE;
+ break;
+ case FORM_SPIRITOFREDEMPTION:
+ modelid = 16031;
+ break;
+ default:
+ sLog.outError("Auras: Unknown Shapeshift Type: %u", m_modifier.m_miscvalue);
+ }
+
+ // remove polymorph before changing display id to keep new display id
+ switch ( form )
+ {
+ case FORM_CAT:
+ case FORM_TREE:
+ case FORM_TRAVEL:
+ case FORM_AQUA:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_FLIGHT_EPIC:
+ case FORM_FLIGHT:
+ case FORM_MOONKIN:
+ // remove movement affects
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
+
+ // and polymorphic affects
+ if(m_target->IsPolymorphed())
+ m_target->RemoveAurasDueToSpell(m_target->getTransForm());
+ break;
+ default:
+ break;
+ }
+
+ if(apply)
+ {
+ // remove other shapeshift before applying a new one
+ if(m_target->m_ShapeShiftFormSpellId)
+ {
+ m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId,this);
+ }
+
+ m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
+
+ if(modelid > 0)
+ {
+ m_target->SetDisplayId(modelid);
+ }
+
+ if(PowerType != POWER_MANA)
+ {
+ // reset power to default values only at power change
+ if(m_target->getPowerType()!=PowerType)
+ m_target->setPowerType(PowerType);
+
+ switch(form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ {
+ // get furor proc chance
+ uint32 FurorChance = 0;
+ Unit::AuraList const& mDummy = m_target->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 238)
+ {
+ FurorChance = (*i)->GetModifier()->m_amount;
+ break;
+ }
+ }
+
+ if (m_modifier.m_miscvalue == FORM_CAT)
+ {
+ m_target->SetPower(POWER_ENERGY,0);
+ if(urand(1,100) <= FurorChance)
+ {
+ m_target->CastSpell(m_target,17099,true,NULL,this);
+ }
+ }
+ else
+ {
+ m_target->SetPower(POWER_RAGE,0);
+ if(urand(1,100) <= FurorChance)
+ {
+ m_target->CastSpell(m_target,17057,true,NULL,this);
+ }
+ }
+ 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;
+ }
+ }
+
+ m_target->m_ShapeShiftFormSpellId = GetId();
+ m_target->m_form = form;
+ }
+ else
+ {
+ m_target->SetDisplayId(m_target->GetNativeDisplayId());
+ m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, FORM_NONE);
+ if(m_target->getClass() == CLASS_DRUID)
+ m_target->setPowerType(POWER_MANA);
+ m_target->m_ShapeShiftFormSpellId = 0;
+ m_target->m_form = FORM_NONE;
+
+ switch(form)
+ {
+ // Nordrassil Harness - bonus
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_CAT:
+ {
+ if(Aura* dummy = m_target->GetDummyAura(37315) )
+ m_target->CastSpell(m_target,37316,true,NULL,dummy);
+ break;
+ }
+ // Nordrassil Regalia - bonus
+ case FORM_MOONKIN:
+ {
+ if(Aura* dummy = m_target->GetDummyAura(37324) )
+ m_target->CastSpell(m_target,37325,true,NULL,dummy);
+ break;
+ }
+ }
+ }
+
+ // adding/removing linked auras
+ // add/remove the shapeshift aura's boosts
+ HandleShapeshiftBoosts(apply);
+
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_target)->InitDataForForm();
+}
+
+void Aura::HandleAuraTransform(bool apply, bool Real)
+{
+ if (apply)
+ {
+ // special case (spell specific functionality)
+ if(m_modifier.m_miscvalue==0)
+ {
+ // player applied only
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ switch(GetId())
+ {
+ // Orb of Deception
+ case 16739:
+ {
+ uint32 orb_model = m_target->GetNativeDisplayId();
+ switch(orb_model)
+ {
+ // Troll Female
+ case 1479: m_target->SetDisplayId(10134); break;
+ // Troll Male
+ case 1478: m_target->SetDisplayId(10135); break;
+ // Tauren Male
+ case 59: m_target->SetDisplayId(10136); break;
+ // Human Male
+ case 49: m_target->SetDisplayId(10137); break;
+ // Human Female
+ case 50: m_target->SetDisplayId(10138); break;
+ // Orc Male
+ case 51: m_target->SetDisplayId(10139); break;
+ // Orc Female
+ case 52: m_target->SetDisplayId(10140); break;
+ // Dwarf Male
+ case 53: m_target->SetDisplayId(10141); break;
+ // Dwarf Female
+ case 54: m_target->SetDisplayId(10142); break;
+ // NightElf Male
+ case 55: m_target->SetDisplayId(10143); break;
+ // NightElf Female
+ case 56: m_target->SetDisplayId(10144); break;
+ // Undead Female
+ case 58: m_target->SetDisplayId(10145); break;
+ // Undead Male
+ case 57: m_target->SetDisplayId(10146); break;
+ // Tauren Female
+ case 60: m_target->SetDisplayId(10147); break;
+ // Gnome Male
+ case 1563: m_target->SetDisplayId(10148); break;
+ // Gnome Female
+ case 1564: m_target->SetDisplayId(10149); break;
+ // BloodElf Female
+ case 15475: m_target->SetDisplayId(17830); break;
+ // BloodElf Male
+ case 15476: m_target->SetDisplayId(17829); break;
+ // Dranei Female
+ case 16126: m_target->SetDisplayId(17828); break;
+ // Dranei Male
+ case 16125: m_target->SetDisplayId(17827); break;
+ default: break;
+ }
+ break;
+ }
+ // Murloc costume
+ case 42365: m_target->SetDisplayId(21723); break;
+ default: break;
+ }
+ }
+ else
+ {
+ CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue);
+ if(!ci)
+ {
+ //pig pink ^_^
+ m_target->SetDisplayId(16358);
+ sLog.outError("Auras: unknown creature id = %d (only need its modelid) Form Spell Aura Transform in Spell ID = %d", m_modifier.m_miscvalue, GetId());
+ }
+ else
+ {
+ // Will use the default model here
+ m_target->SetDisplayId(ci->DisplayID_A);
+
+ // Dragonmaw Illusion (set mount model also)
+ if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED).empty())
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
+ }
+ m_target->setTransForm(GetId());
+ }
+
+ // polymorph case
+ if( Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed())
+ {
+ // for players, start regeneration after 1s (in polymorph fast regeneration case)
+ // only if caster is Player (after patch 2.4.2)
+ if(IS_PLAYER_GUID(GetCasterGUID()) )
+ ((Player*)m_target)->setRegenTimer(1000);
+
+ //dismount polymorphed target (after patch 2.4.2)
+ if (m_target->IsMounted())
+ m_target->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ }
+ }
+ else
+ {
+ Unit::AuraList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM);
+ if(otherTransforms.empty())
+ {
+ m_target->SetDisplayId(m_target->GetNativeDisplayId());
+ m_target->setTransForm(0);
+ }
+ else
+ {
+ // look for other transform auras
+ Aura* handledAura = *otherTransforms.begin();
+ for(Unit::AuraList::const_iterator i = otherTransforms.begin();i != otherTransforms.end(); ++i)
+ {
+ // negative auras are prefered
+ if(!IsPositiveSpell((*i)->GetSpellProto()->Id))
+ {
+ handledAura = *i;
+ break;
+ }
+ }
+ handledAura->ApplyModifier(true);
+ }
+
+ // Dragonmaw Illusion (restore mount model)
+ if(GetId()==42016 && m_target->GetMountID()==16314)
+ {
+ if(!m_target->GetAurasByType(SPELL_AURA_MOUNTED).empty())
+ {
+ uint32 cr_id = m_target->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetModifier()->m_miscvalue;
+ if(CreatureInfo const* ci = objmgr.GetCreatureTemplate(cr_id))
+ {
+ uint32 team = 0;
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_target)->GetTeam();
+
+ uint32 display_id = objmgr.ChooseDisplayId(team,ci);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (minfo)
+ display_id = minfo->modelid;
+
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,display_id);
+ }
+ }
+ }
+ }
+}
+
+void Aura::HandleForceReaction(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!Real)
+ return;
+
+ Player* player = (Player*)m_target;
+
+ uint32 faction_id = m_modifier.m_miscvalue;
+ uint32 faction_rank = m_modifier.m_amount;
+
+ if(apply)
+ player->m_forcedReactions[faction_id] = ReputationRank(faction_rank);
+ else
+ player->m_forcedReactions.erase(faction_id);
+
+ WorldPacket data;
+ data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+player->m_forcedReactions.size()*(4+4));
+ data << uint32(player->m_forcedReactions.size());
+ for(ForcedReactions::const_iterator itr = player->m_forcedReactions.begin(); itr != player->m_forcedReactions.end(); ++itr)
+ {
+ data << uint32(itr->first); // faction_id (Faction.dbc)
+ data << uint32(itr->second); // reputation rank
+ }
+ player->SendDirectMessage(&data);
+}
+
+void Aura::HandleAuraModSkill(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 prot=GetSpellProto()->EffectMiscValue[m_effIndex];
+ int32 points = GetModifier()->m_amount;
+
+ ((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_modifier.m_auraname==SPELL_AURA_MOD_SKILL_TALENT);
+ if(prot == SKILL_DEFENSE)
+ ((Player*)m_target)->UpdateDefenseBonusesMod();
+}
+
+void Aura::HandleChannelDeathItem(bool apply, bool Real)
+{
+ if(Real && !apply)
+ {
+ Unit* caster = GetCaster();
+ Unit* victim = GetTarget();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER || !victim || m_removeMode!=AURA_REMOVE_BY_DEATH)
+ return;
+
+ SpellEntry const *spellInfo = GetSpellProto();
+ if(spellInfo->EffectItemType[m_effIndex] == 0)
+ return;
+
+ // Soul Shard only from non-grey units
+ if( spellInfo->EffectItemType[m_effIndex] == 6265 &&
+ (victim->getLevel() <= MaNGOS::XP::GetGrayLevel(caster->getLevel()) ||
+ victim->GetTypeId()==TYPEID_UNIT && !((Player*)caster)->isAllowedToLoot((Creature*)victim)) )
+ return;
+ ItemPosCountVec dest;
+ uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], 1 );
+ if( msg != EQUIP_ERR_OK )
+ {
+ ((Player*)caster)->SendEquipError( msg, NULL, NULL );
+ return;
+ }
+
+ Item* newitem = ((Player*)caster)->StoreNewItem(dest, spellInfo->EffectItemType[m_effIndex], true);
+ ((Player*)caster)->SendNewItem(newitem, 1, true, false);
+ }
+}
+
+void Aura::HandleBindSight(bool apply, bool Real)
+{
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0);
+}
+
+void Aura::HandleFarSight(bool apply, bool Real)
+{
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_modifier.m_miscvalue : 0);
+}
+
+void Aura::HandleAuraTrackCreatures(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+ m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1) : 0 );
+}
+
+void Aura::HandleAuraTrackResources(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+ m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1): 0 );
+}
+
+void Aura::HandleAuraTrackStealthed(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->RemoveNoStackAurasDueToAura(this);
+
+ m_target->ApplyModFlag(PLAYER_FIELD_BYTES,PLAYER_FIELD_BYTE_TRACK_STEALTHED,apply);
+}
+
+void Aura::HandleAuraModScale(bool apply, bool Real)
+{
+ m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleModPossess(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(m_target->getLevel() > m_modifier.m_amount)
+ return;
+
+ // not possess yourself
+ if(GetCasterGUID() == m_target->GetGUID())
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster)
+ return;
+
+ if( apply )
+ {
+ m_target->SetCharmerGUID(GetCasterGUID());
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction());
+ caster->SetCharm(m_target);
+
+ m_target->CombatStop();
+ m_target->DeleteThreatList();
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ m_target->StopMoving();
+ m_target->GetMotionMaster()->Clear();
+ m_target->GetMotionMaster()->MoveIdle();
+ CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target);
+ charmInfo->InitPossessCreateSpells();
+ }
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)caster)->PossessSpellInitialize();
+ }
+ }
+ else
+ {
+ m_target->SetCharmerGUID(0);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->setFactionForRace(m_target->getRace());
+ }
+ else if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+ }
+
+ caster->SetCharm(0);
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+
+ if (((Creature*)m_target)->AI())
+ ((Creature*)m_target)->AI()->AttackStart(caster);
+ }
+ }
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0);
+}
+
+void Aura::HandleModPossessPet(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(caster->GetPet() != m_target)
+ return;
+
+ if(apply)
+ {
+ caster->SetUInt64Value(PLAYER_FARSIGHT, m_target->GetGUID());
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5);
+ }
+ else
+ {
+ caster->SetUInt64Value(PLAYER_FARSIGHT, 0);
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5);
+ }
+}
+
+void Aura::HandleModCharm(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // not charm yourself
+ if(GetCasterGUID() == m_target->GetGUID())
+ return;
+
+ Unit* caster = GetCaster();
+ if(!caster)
+ return;
+
+ if(int32(m_target->getLevel()) <= m_modifier.m_amount)
+ {
+ if( apply )
+ {
+ m_target->SetCharmerGUID(GetCasterGUID());
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction());
+ m_target->CastStop(m_target==caster ? GetId() : 0);
+ caster->SetCharm(m_target);
+
+ m_target->CombatStop();
+ m_target->DeleteThreatList();
+
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+ CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target);
+ charmInfo->InitCharmCreateSpells();
+ charmInfo->SetReactState( REACT_DEFENSIVE );
+
+ if(caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK)
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+ if(cinfo && cinfo->type == CREATURE_TYPE_DEMON)
+ {
+ //to prevent client crash
+ m_target->SetFlag(UNIT_FIELD_BYTES_0, 2048);
+ //just to enable stat window
+ charmInfo->SetPetNumber(objmgr.GeneratePetNumber(), true);
+ //if charmed two demons the same session, the 2nd gets the 1st one's name
+ m_target->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+ }
+ }
+ }
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)caster)->CharmSpellInitialize();
+ }
+ }
+ else
+ {
+ m_target->SetCharmerGUID(0);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->setFactionForRace(m_target->getRace());
+ }
+ else
+ {
+ CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo();
+
+ // restore faction
+ if(((Creature*)m_target)->isPet())
+ {
+ if(Unit* owner = m_target->GetOwner())
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
+ else if(cinfo)
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+ }
+ else if(cinfo) // normal creature
+ m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A);
+
+ // restore UNIT_FIELD_BYTES_0
+ if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON)
+ {
+ CreatureDataAddon const *cainfo = ((Creature*)m_target)->GetCreatureAddon();
+ if(cainfo && cainfo->bytes0 != 0)
+ m_target->SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_BYTES_0, 2048);
+
+ if(m_target->GetCharmInfo())
+ m_target->GetCharmInfo()->SetPetNumber(0, true);
+ else
+ sLog.outError("Aura::HandleModCharm: target="I64FMTD" with typeid=%d has a charm aura but no charm info!", m_target->GetGUID(), m_target->GetTypeId());
+ }
+ }
+
+ caster->SetCharm(0);
+
+ if(caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ if(m_target->GetTypeId() == TYPEID_UNIT)
+ {
+ ((Creature*)m_target)->AIM_Initialize();
+ if (((Creature*)m_target)->AI())
+ ((Creature*)m_target)->AI()->AttackStart(caster);
+ }
+ }
+ }
+}
+
+void Aura::HandleModConfuse(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ m_target->SetConfused(apply, GetCasterGUID(), GetId());
+}
+
+void Aura::HandleModFear(bool apply, bool Real)
+{
+ if (!Real)
+ return;
+
+ m_target->SetFeared(apply, GetCasterGUID(), GetId());
+}
+
+void Aura::HandleFeignDeath(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if( apply )
+ {
+ /*
+ WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9);
+ data<<m_target->GetGUID();
+ data<<uint8(0);
+ m_target->SendMessageToSet(&data,true);
+ */
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6);
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
+ // blizz like 2.0.x
+ m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
+
+ m_target->addUnitState(UNIT_STAT_DIED);
+ m_target->CombatStop();
+
+ // prevent interrupt message
+ if(m_caster_guid==m_target->GetGUID() && m_target->m_currentSpells[CURRENT_GENERIC_SPELL])
+ m_target->m_currentSpells[CURRENT_GENERIC_SPELL]->finish();
+ m_target->InterruptNonMeleeSpells(true);
+ m_target->getHostilRefManager().deleteReferences();
+ }
+ else
+ {
+ /*
+ WorldPacket data(SMSG_FEIGN_DEATH_RESISTED, 9);
+ data<<m_target->GetGUID();
+ data<<uint8(1);
+ m_target->SendMessageToSet(&data,true);
+ */
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6);
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
+ // blizz like 2.0.x
+ m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
+
+ m_target->clearUnitState(UNIT_STAT_DIED);
+ }
+}
+
+void Aura::HandleAuraModDisarm(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(!apply && m_target->HasAuraType(SPELL_AURA_MOD_DISARM))
+ return;
+
+ // not sure for it's correctness
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED);
+
+ // only at real add/remove aura
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // main-hand attack speed already set to special value for feral form already and don't must chnage and reset at remove.
+ if (((Player *)m_target)->IsInFeralForm())
+ return;
+
+ if (apply)
+ m_target->SetAttackTime(BASE_ATTACK,BASE_ATTACK_TIME);
+ else
+ ((Player *)m_target)->SetRegularAttackTime();
+
+ m_target->UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Aura::HandleAuraModStun(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if (apply)
+ {
+ m_target->addUnitState(UNIT_STAT_STUNDED);
+ m_target->SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ m_target->CastStop(m_target->GetGUID() == GetCasterGUID() ? GetId() : 0);
+
+ // Creature specific
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ ((Creature*)m_target)->StopMoving();
+ else
+ m_target->SetUnitMovementFlags(0); //Clear movement flags
+
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8);
+
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_STUN))
+ return;
+
+ m_target->clearUnitState(UNIT_STAT_STUNDED);
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+
+ if(!m_target->hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect
+ {
+ if(m_target->getVictim() && m_target->isAlive())
+ m_target->SetUInt64Value(UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() );
+
+ WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0);
+ m_target->SendMessageToSet(&data,true);
+ }
+
+ // Wyvern Sting
+ if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1721)
+ {
+ Unit* caster = GetCaster();
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ uint32 spell_id = 0;
+
+ switch(GetId())
+ {
+ case 19386: spell_id = 24131; break;
+ case 24132: spell_id = 24134; break;
+ case 24133: spell_id = 24135; break;
+ case 27068: spell_id = 27069; break;
+ default:
+ sLog.outError("Spell selection called for unexpected original spell %u, new spell for this spell family?",GetId());
+ return;
+ }
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
+
+ if(!spellInfo)
+ return;
+
+ caster->CastSpell(m_target,spellInfo,true,NULL,this);
+ return;
+ }
+ }
+}
+
+void Aura::HandleModStealth(bool apply, bool Real)
+{
+ if(apply)
+ {
+ // drop flag at stealth in bg
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER && ((Player*)m_target)->InBattleGround())
+ if(BattleGround *bg = ((Player*)m_target)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_target);
+
+ // only at real aura add
+ if(Real)
+ {
+ m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x02);
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000);
+
+ // apply only if not in GM invisibility (and overwrite invisibility state)
+ if(m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_STEALTH);
+ }
+
+ // for RACE_NIGHTELF stealth
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580)
+ m_target->CastSpell(m_target, 21009, true, NULL, this);
+ }
+ }
+ else
+ {
+ // only at real aura remove
+ if(Real)
+ {
+ // for RACE_NIGHTELF stealth
+ if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580)
+ m_target->RemoveAurasDueToSpell(21009);
+
+ // if last SPELL_AURA_MOD_STEALTH and no GM invisibility
+ if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH) && m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00);
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000);
+
+ // restore invisibility if any
+ if(m_target->HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
+ {
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ }
+ else
+ m_target->SetVisibility(VISIBILITY_ON);
+ }
+ }
+ }
+
+ // Master of Subtlety
+ Unit::AuraList const& mDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 2114)
+ {
+ if (apply)
+ {
+ int32 bp = (*i)->GetModifier()->m_amount;
+ m_target->CastCustomSpell(m_target,31665,&bp,NULL,NULL,true);
+ }
+ else
+ m_target->CastSpell(m_target,31666,true);
+ break;
+ }
+ }
+}
+
+void Aura::HandleInvisibility(bool apply, bool Real)
+{
+ if(apply)
+ {
+ m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue);
+
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // apply glow vision
+ m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
+ // drop flag at invisible in bg
+ if(((Player*)m_target)->InBattleGround())
+ if(BattleGround *bg = ((Player*)m_target)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_target);
+ }
+
+ // apply only if not in GM invisibility and not stealth
+ if(m_target->GetVisibility()==VISIBILITY_ON)
+ {
+ // Aura not added yet but visibility code expect temporary add aura
+ m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT);
+ m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ }
+ }
+ else
+ {
+ // recalculate value at modifier remove (current aura already removed)
+ m_target->m_invisibilityMask = 0;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue);
+
+ // only at real aura remove and if not have different invisibility auras.
+ if(Real && m_target->m_invisibilityMask==0)
+ {
+ // remove glow vision
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
+ // apply only if not in GM invisibility & not stealthed while invisible
+ if(m_target->GetVisibility()!=VISIBILITY_OFF)
+ {
+ // if have stealth aura then already have stealth visibility
+ if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH))
+ m_target->SetVisibility(VISIBILITY_ON);
+ }
+ }
+ }
+}
+
+void Aura::HandleInvisibilityDetect(bool apply, bool Real)
+{
+ if(apply)
+ {
+ m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue);
+ }
+ else
+ {
+ // recalculate value at modifier remove (current aura already removed)
+ m_target->m_detectInvisibilityMask = 0;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue);
+ }
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ ObjectAccessor::UpdateVisibilityForPlayer((Player*)m_target);
+}
+
+void Aura::HandleAuraModRoot(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ uint32 apply_stat = UNIT_STAT_ROOT;
+ if (apply)
+ {
+ m_target->addUnitState(UNIT_STAT_ROOT);
+ m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0);
+ // probably wrong
+ m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16));
+
+ //Save last orientation
+ if( m_target->getVictim() )
+ m_target->SetOrientation(m_target->GetAngle(m_target->getVictim()));
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)2;
+ m_target->SendMessageToSet(&data,true);
+
+ //Clear unit movement flags
+ m_target->SetUnitMovementFlags(0);
+ }
+ else
+ ((Creature *)m_target)->StopMoving();
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_ROOT))
+ return;
+
+ m_target->clearUnitState(UNIT_STAT_ROOT);
+ // probably wrong
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16));
+
+ if(!m_target->hasUnitState(UNIT_STAT_STUNDED)) // prevent allow move if have also stun effect
+ {
+ if(m_target->getVictim() && m_target->isAlive())
+ m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() );
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10);
+ data.append(m_target->GetPackGUID());
+ data << (uint32)2;
+ m_target->SendMessageToSet(&data,true);
+ }
+ }
+ }
+}
+
+void Aura::HandleAuraModSilence(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(apply)
+ {
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
+ // Stop cast only spells vs PreventionType == SPELL_PREVENTION_TYPE_SILENCE
+ for (uint32 i = CURRENT_MELEE_SPELL; i < CURRENT_MAX_SPELL;i++)
+ {
+ Spell* currentSpell = m_target->m_currentSpells[i];
+ if (currentSpell && currentSpell->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE)
+ {
+ uint32 state = currentSpell->getState();
+ // Stop spells on prepere or casting state
+ if ( state == SPELL_STATE_PREPARING || state == SPELL_STATE_CASTING )
+ {
+ currentSpell->cancel();
+ currentSpell->SetDeletable(true);
+ m_target->m_currentSpells[i] = NULL;
+ }
+ }
+ }
+
+ switch (GetId())
+ {
+ // Arcane Torrent (Energy)
+ case 25046:
+ {
+ Unit * caster = GetCaster();
+ if (!caster)
+ return;
+
+ // Search Mana Tap auras on caster
+ int32 energy = 0;
+ Unit::AuraList const& m_dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ if ((*i)->GetId() == 28734)
+ ++energy;
+ if (energy)
+ {
+ energy *= 10;
+ caster->CastCustomSpell(caster, 25048, &energy, NULL, NULL, true);
+ caster->RemoveAurasDueToSpell(28734);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Real remove called after current aura remove from lists, check if other similar auras active
+ if(m_target->HasAuraType(SPELL_AURA_MOD_SILENCE))
+ return;
+
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
+ }
+}
+
+void Aura::HandleModThreat(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive())
+ return;
+
+ int level_diff = 0;
+ int multiplier = 0;
+ switch (GetId())
+ {
+ // Arcane Shroud
+ case 26400:
+ level_diff = m_target->getLevel() - 60;
+ multiplier = 2;
+ break;
+ // The Eye of Diminution
+ case 28862:
+ level_diff = m_target->getLevel() - 60;
+ multiplier = 1;
+ break;
+ }
+ if (level_diff > 0)
+ m_modifier.m_amount += multiplier * level_diff;
+
+ for(int8 x=0;x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_positive ? m_modifier.m_amount : -m_modifier.m_amount, apply);
+ }
+ }
+}
+
+void Aura::HandleAuraModTotalThreat(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive() || m_target->GetTypeId()!= TYPEID_PLAYER)
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive())
+ return;
+
+ float threatMod = 0.0f;
+ if(apply)
+ threatMod = float(m_modifier.m_amount);
+ else
+ threatMod = float(-m_modifier.m_amount);
+
+ m_target->getHostilRefManager().threatAssist(caster, threatMod);
+}
+
+void Aura::HandleModTaunt(bool apply, bool Real)
+{
+ // only at real add/remove aura
+ if(!Real)
+ return;
+
+ if(!m_target->isAlive() || !m_target->CanHaveThreatList())
+ return;
+
+ Unit* caster = GetCaster();
+
+ if(!caster || !caster->isAlive() || caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ {
+ m_target->TauntApply(caster);
+ }
+ else
+ {
+ // When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat
+ m_target->TauntFadeOut(caster);
+ }
+}
+
+/*********************************************************/
+/*** MODIFY SPEED ***/
+/*********************************************************/
+void Aura::HandleAuraModIncreaseSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+}
+
+void Aura::HandleAuraModIncreaseMountedSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+}
+
+void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ // Enable Fly mode for flying mounts
+ if (m_modifier.m_auraname == SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED)
+ {
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0); // unknown
+ m_target->SendMessageToSet(&data, true);
+
+ //Players on flying mounts must be immune to polymorph
+ if (m_target->GetTypeId()==TYPEID_PLAYER)
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,MECHANIC_POLYMORPH,apply);
+
+ // Dragonmaw Illusion (overwrite mount model, mounted aura already applied)
+ if( apply && m_target->HasAura(42016,0) && m_target->GetMountID())
+ m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
+ }
+
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+void Aura::HandleAuraModIncreaseSwimSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+}
+
+void Aura::HandleAuraModDecreaseSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+void Aura::HandleAuraModUseNormalSpeed(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->UpdateSpeed(MOVE_RUN, true);
+ m_target->UpdateSpeed(MOVE_SWIM, true);
+ m_target->UpdateSpeed(MOVE_FLY, true);
+}
+
+/*********************************************************/
+/*** IMMUNITY ***/
+/*********************************************************/
+
+void Aura::HandleModMechanicImmunity(bool apply, bool Real)
+{
+ uint32 mechanic = 1 << m_modifier.m_miscvalue;
+
+ //immune movement impairment and loss of control
+ if(GetId()==42292)
+ mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
+
+ if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ Unit::AuraMap& Auras = m_target->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = iter->second->GetSpellProto();
+ if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) // spells unaffected by invulnerability
+ && !iter->second->IsPositive() // only remove negative spells
+ && spell->Id != GetId())
+ {
+ //check for mechanic mask
+ if(GetSpellMechanicMask(spell, iter->second->GetEffIndex()) & mechanic)
+ {
+ m_target->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,m_modifier.m_miscvalue,apply);
+
+ // special cases
+ switch(m_modifier.m_miscvalue)
+ {
+ case MECHANIC_INVULNERABILITY:
+ m_target->ModifyAuraState(AURA_STATE_FORBEARANCE,apply);
+ break;
+ case MECHANIC_SHIELD:
+ m_target->ModifyAuraState(AURA_STATE_WEAKENED_SOUL,apply);
+ break;
+ }
+
+ // Bestial Wrath
+ if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1680)
+ {
+ // The Beast Within cast on owner if talent present
+ if ( Unit* owner = m_target->GetOwner() )
+ {
+ // Search talent
+ Unit::AuraList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ {
+ if ( (*i)->GetSpellProto()->SpellIconID == 2229 )
+ {
+ if (apply)
+ owner->CastSpell(owner, 34471, true, 0, this);
+ else
+ owner->RemoveAurasDueToSpell(34471);
+ break;
+ }
+ }
+ }
+ }
+
+ // The Beast Within and Bestial Wrath - immunity
+ if(GetId() == 19574 || GetId() == 34471)
+ {
+ if(apply)
+ {
+ m_target->CastSpell(m_target,24395,true);
+ m_target->CastSpell(m_target,24396,true);
+ m_target->CastSpell(m_target,24397,true);
+ m_target->CastSpell(m_target,26592,true);
+ }
+ else
+ {
+ m_target->RemoveAurasDueToSpell(24395);
+ m_target->RemoveAurasDueToSpell(24396);
+ m_target->RemoveAurasDueToSpell(24397);
+ m_target->RemoveAurasDueToSpell(26592);
+ }
+ }
+}
+
+void Aura::HandleAuraModEffectImmunity(bool apply, bool Real)
+{
+ if(!apply)
+ {
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)m_target)->InBattleGround())
+ {
+ BattleGround *bg = ((Player*)m_target)->GetBattleGround();
+ if(bg)
+ {
+ switch(bg->GetTypeID())
+ {
+ case BATTLEGROUND_AV:
+ {
+ break;
+ }
+ case BATTLEGROUND_WS:
+ {
+ // Warsong Flag, horde // Silverwing Flag, alliance
+ if(GetId() == 23333 || GetId() == 23335)
+ bg->EventPlayerDroppedFlag(((Player*)m_target));
+ break;
+ }
+ case BATTLEGROUND_AB:
+ {
+ break;
+ }
+ case BATTLEGROUND_EY:
+ {
+ if(GetId() == 34976)
+ bg->EventPlayerDroppedFlag(((Player*)m_target));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModStateImmunity(bool apply, bool Real)
+{
+ if(apply && Real && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ Unit::AuraList const& auraList = m_target->GetAurasByType(AuraType(m_modifier.m_miscvalue));
+ for(Unit::AuraList::const_iterator itr = auraList.begin(); itr != auraList.end();)
+ {
+ if (auraList.front() != this) // skip itself aura (it already added)
+ {
+ m_target->RemoveAurasDueToSpell(auraList.front()->GetId());
+ itr = auraList.begin();
+ }
+ else
+ ++itr;
+ }
+ }
+
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModSchoolImmunity(bool apply, bool Real)
+{
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,m_modifier.m_miscvalue,apply);
+
+ if(Real && apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ {
+ if(IsPositiveSpell(GetId())) //Only positive immunity removes auras
+ {
+ uint32 school_mask = m_modifier.m_miscvalue;
+ Unit::AuraMap& Auras = m_target->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = iter->second->GetSpellProto();
+ if((GetSpellSchoolMask(spell) & school_mask)//Check for school mask
+ && !( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) //Spells unaffected by invulnerability
+ && !iter->second->IsPositive() //Don't remove positive spells
+ && spell->Id != GetId() ) //Don't remove self
+ {
+ m_target->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ }
+ if( Real && GetSpellProto()->Mechanic == MECHANIC_BANISH )
+ {
+ if( apply )
+ m_target->addUnitState(UNIT_STAT_ISOLATED);
+ else
+ m_target->clearUnitState(UNIT_STAT_ISOLATED);
+ }
+}
+
+void Aura::HandleAuraModDmgImmunity(bool apply, bool Real)
+{
+ m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,m_modifier.m_miscvalue,apply);
+}
+
+void Aura::HandleAuraModDispelImmunity(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(m_modifier.m_miscvalue), apply);
+}
+
+void Aura::HandleAuraProcTriggerSpell(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(apply)
+ {
+ // some spell have charges by functionality not have its in spell data
+ switch (GetId())
+ {
+ case 28200: // Ascendance (Talisman of Ascendance trinket)
+ m_procCharges = 6;
+ UpdateAuraCharges();
+ break;
+ default: break;
+ }
+ }
+}
+
+void Aura::HandleAuraModStalked(bool apply, bool Real)
+{
+ // used by spells: Hunter's Mark, Mind Vision, Syndicate Tracker (MURP) DND
+ if(apply)
+ m_target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
+ else
+ m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
+}
+
+/*********************************************************/
+/*** PERIODIC ***/
+/*********************************************************/
+
+void Aura::HandlePeriodicTriggerSpell(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+ m_isTrigger = apply;
+
+ // Curse of the Plaguebringer
+ if (!apply && m_spellProto->Id == 29213 && m_removeMode!=AURA_REMOVE_BY_DISPEL)
+ {
+ // Cast Wrath of the Plaguebringer if not dispelled
+ m_target->CastSpell(m_target, 29214, true, 0, this);
+ }
+}
+
+void Aura::HandlePeriodicEnergize(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicHeal(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+
+ // only at real apply
+ if (Real && apply && GetSpellProto()->Mechanic == MECHANIC_BANDAGE)
+ {
+ // provided m_target as original caster to prevent apply aura caster selection for this negative buff
+ m_target->CastSpell(m_target,11196,true,NULL,this,m_target->GetGUID());
+ }
+
+ // For prevent double apply bonuses
+ bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading());
+
+ if(!loading && apply)
+ {
+ switch (m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_DRUID:
+ {
+ // Rejuvenation
+ if(m_spellProto->SpellFamilyFlags & 0x0000000000000010LL)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k)
+ {
+ int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex];
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 4953: // Increased Rejuvenation Healing - Harold's Rejuvenating Broach Aura
+ case 4415: // Increased Rejuvenation Healing - Idol of Rejuvenation Aura
+ {
+ m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Aura::HandlePeriodicDamage(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+
+ // For prevent double apply bonuses
+ bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading());
+
+ Unit *caster = GetCaster();
+
+ switch (m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ // Pounce Bleed
+ if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual == 0 )
+ {
+ // $AP*0.18/6 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Rend
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL)
+ {
+ // 0.00743*(($MWB+$mwb)/2+$AP/14*$MWS) bonus per tick
+ if (apply && !loading && caster)
+ {
+ float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
+ int32 mws = caster->GetAttackTime(BASE_ATTACK);
+ float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE);
+ float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE);
+ // WARNING! in 3.0 multipler 0.00743f change to 0.6
+ m_modifier.m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.00743f);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Rake
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000001000LL)
+ {
+ // $AP*0.06/3 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 / 100);
+ return;
+ }
+ // Lacerate
+ if (m_spellProto->SpellFamilyFlags & 0x000000010000000000LL)
+ {
+ // $AP*0.05/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ return;
+ }
+ // Rip
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000800000LL)
+ {
+ // $AP * min(0.06*$cp, 0.24)/6 [Yes, there is no difference, wheather 4 or 5 CPs are being used]
+ if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ uint8 cp = ((Player*)caster)->GetComboPoints();
+
+ // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
+ Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
+ {
+ if((*itr)->GetId()==34241)
+ {
+ m_modifier.m_amount += cp * (*itr)->GetModifier()->m_amount;
+ break;
+ }
+ }
+
+ if (cp > 4) cp = 4;
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100);
+ }
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Deadly poison aura state
+ if((m_spellProto->SpellFamilyFlags & 0x10000) && m_spellProto->SpellVisual==5100)
+ {
+ if(apply)
+ m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,true);
+ else
+ {
+ // current aura already removed, search present of another
+ bool found = false;
+ Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ SpellEntry const* itr_spell = (*itr)->GetSpellProto();
+ if(itr_spell && itr_spell->SpellFamilyName==SPELLFAMILY_ROGUE && (itr_spell->SpellFamilyFlags & 0x10000) && itr_spell->SpellVisual==5100)
+ {
+ found = true;
+ break;
+ }
+ }
+ // this has been last deadly poison aura
+ if(!found)
+ m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,false);
+ }
+ return;
+ }
+ // Rupture
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000100000LL)
+ {
+ // Dmg/tick = $AP*min(0.01*$cp, 0.03) [Like Rip: only the first three CP inrease the contribution from AP]
+ if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ uint8 cp = ((Player*)caster)->GetComboPoints();
+ if (cp > 3) cp = 3;
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100);
+ }
+ return;
+ }
+ // Garrote
+ if (m_spellProto->SpellFamilyFlags & 0x000000000000000100LL)
+ {
+ // $AP*0.18/6 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Serpent Sting
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000004000LL)
+ {
+ // $RAP*0.1/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500);
+ return;
+ }
+ // Immolation Trap
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000004LL && m_spellProto->SpellIconID == 678)
+ {
+ // $RAP*0.1/5 bonus per tick
+ if (apply && !loading && caster)
+ m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500);
+ return;
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Consecration
+ if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL)
+ {
+ if (apply && !loading)
+ {
+ if(Unit* caster = GetCaster())
+ {
+ Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k)
+ {
+ int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex];
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 5147: // Improved Consecration - Libram of the Eternal Rest
+ {
+ m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Aura::HandlePeriodicDamagePCT(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicLeech(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandlePeriodicManaLeech(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+/*********************************************************/
+/*** MODIFY STATS ***/
+/*********************************************************/
+
+/********************************/
+/*** RESISTANCE ***/
+/********************************/
+
+void Aura::HandleAuraModResistanceExclusive(bool apply, bool Real)
+{
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply);
+ }
+ }
+}
+
+void Aura::HandleAuraModResistance(bool apply, bool Real)
+{
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,m_modifier.m_amount, apply);
+ }
+ }
+
+ // Faerie Fire (druid versions)
+ if( m_spellProto->SpellIconID == 109 &&
+ m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID &&
+ m_spellProto->SpellFamilyFlags & 0x0000000000000400LL )
+ {
+ m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE,apply);
+ }
+}
+
+void Aura::HandleAuraModBaseResistancePCT(bool apply, bool Real)
+{
+ // only players have base stats
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ //pets only have base armor
+ if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ {
+ m_target->HandleStatModifier(UNIT_MOD_ARMOR, BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+ else
+ {
+ for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<x))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+ }
+}
+
+void Aura::HandleModResistancePercent(bool apply, bool Real)
+{
+ for(int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if(m_modifier.m_miscvalue & int32(1<<i))
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ {
+ m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),true,m_modifier.m_amount, apply);
+ m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),false,m_modifier.m_amount, apply);
+ }
+ }
+ }
+}
+
+void Aura::HandleModBaseResistance(bool apply, bool Real)
+{
+ // only players have base stats
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ //only pets have base stats
+ if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ m_target->HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+}
+
+/********************************/
+/*** STAT ***/
+/********************************/
+
+void Aura::HandleAuraModStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -2 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),m_modifier.m_miscvalue);
+ return;
+ }
+
+ for(int32 i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ // -1 or -2 is all stats ( misc < -2 checked in function beginning )
+ if (m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue == i)
+ {
+ //m_target->ApplyStatMod(Stats(i), m_modifier.m_amount,apply);
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyStatBuffMod(Stats(i),m_modifier.m_amount,apply);
+ }
+ }
+}
+
+void Aura::HandleModPercentStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
+ return;
+ }
+
+ // only players have base stats
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ {
+ if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1)
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_modifier.m_amount), apply);
+ }
+ }
+}
+
+void Aura::HandleModSpellDamagePercentFromStat(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModSpellHealingPercentFromStat(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleAuraModDispelResist(bool apply, bool Real)
+{
+ if(!Real || !apply)
+ return;
+
+ if(GetId()==33206)
+ m_target->CastSpell(m_target,44416,true,NULL,this,GetCasterGUID());
+}
+
+void Aura::HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModHealingDone(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // implemented in Unit::SpellHealingBonus
+ // this information is for client side only
+ ((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
+}
+
+void Aura::HandleModTotalPercentStat(bool apply, bool Real)
+{
+ if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4)
+ {
+ sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
+ return;
+ }
+
+ //save current and max HP before applying aura
+ uint32 curHPValue = m_target->GetHealth();
+ uint32 maxHPValue = m_target->GetMaxHealth();
+
+ for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1)
+ {
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, float(m_modifier.m_amount), apply);
+ if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet())
+ m_target->ApplyStatPercentBuffMod(Stats(i), m_modifier.m_amount, apply );
+ }
+ }
+
+ //recalculate current HP/MP after applying aura modifications (only for spells with 0x10 flag)
+ if ((m_modifier.m_miscvalue == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10))
+ {
+ // newHP = (curHP / maxHP) * newMaxHP = (newMaxHP * curHP) / maxHP -> which is better because no int -> double -> int conversion is needed
+ uint32 newHPValue = (m_target->GetMaxHealth() * curHPValue) / maxHPValue;
+ m_target->SetHealth(newHPValue);
+ }
+}
+
+void Aura::HandleAuraModResistenceOfStatPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(m_modifier.m_miscvalue != SPELL_SCHOOL_MASK_NORMAL)
+ {
+ // support required adding replace UpdateArmor by loop by UpdateResistence at intelect update
+ // and include in UpdateResistence same code as in UpdateArmor for aura mod apply.
+ sLog.outError("Aura SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT(182) need adding support for non-armor resistences!");
+ return;
+ }
+
+ // Recalculate Armor
+ m_target->UpdateArmor();
+}
+
+/********************************/
+/*** HEAL & ENERGIZE ***/
+/********************************/
+void Aura::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real)
+{
+ /*
+ Need additional checking for auras who reduce or increase healing, magic effect like Dumpen Magic,
+ so this aura not fully working.
+ */
+ if(apply)
+ {
+ if(!m_target->isAlive())
+ return;
+
+ if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(m_periodicTimer <= 0)
+ {
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_target->GetHealth() < m_target->GetMaxHealth())
+ {
+ // PeriodicTick can cast triggered spells with stats changes
+ PeriodicTick();
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleAuraModTotalManaPercentRegen(bool apply, bool Real)
+{
+ if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+ if(apply)
+ {
+ if(m_modifier.periodictime == 0)
+ m_modifier.periodictime = 1000;
+ if(m_periodicTimer <= 0 && m_target->getPowerType() == POWER_MANA)
+ {
+ m_periodicTimer += m_modifier.periodictime;
+
+ if(m_target->GetPower(POWER_MANA) < m_target->GetMaxPower(POWER_MANA))
+ {
+ // PeriodicTick can cast triggered spells with stats changes
+ PeriodicTick();
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleModRegen(bool apply, bool Real) // eating
+{
+ if(apply)
+ {
+ if(!m_target->isAlive())
+ return;
+
+ if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(m_periodicTimer <= 0)
+ {
+ m_periodicTimer += 5000;
+ int32 gain = m_target->ModifyHealth(m_modifier.m_amount);
+ Unit *caster = GetCaster();
+ if (caster)
+ {
+ SpellEntry const *spellProto = GetSpellProto();
+ if (spellProto)
+ m_target->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, spellProto);
+ }
+ }
+ }
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleModPowerRegen(bool apply, bool Real) // drinking
+{
+ if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState())
+ m_target->SetStandState(PLAYER_STATE_SIT);
+
+ if(apply && m_periodicTimer <= 0)
+ {
+ m_periodicTimer += 2000;
+
+ Powers pt = m_target->getPowerType();
+ if(int32(pt) != m_modifier.m_miscvalue)
+ return;
+
+ if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED )
+ {
+ // eating anim
+ m_target->HandleEmoteCommand(EMOTE_ONESHOT_EAT);
+ }
+ else if( GetId() == 20577 )
+ {
+ // cannibalize anim
+ m_target->HandleEmoteCommand(398);
+ }
+
+ // Warrior talent, gain 1 rage every 3 seconds while in combat
+ if(pt == POWER_RAGE && m_target->isInCombat())
+ {
+ m_target->ModifyPower(pt, m_modifier.m_amount*10/17);
+ m_periodicTimer += 1000;
+ }
+ }
+ m_isPeriodic = apply;
+ if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_modifier.m_miscvalue == POWER_MANA)
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleModPowerRegenPCT(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Update manaregen value
+ if (m_modifier.m_miscvalue == POWER_MANA)
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleModManaRegen(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ //Note: an increase in regen does NOT cause threat.
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleComprehendLanguage(bool apply, bool Real)
+{
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG);
+}
+
+void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real)
+{
+ // Special case with temporary increase max/current health
+ switch(GetId())
+ {
+ case 12976: // Warrior Last Stand triggered spell
+ case 28726: // Nightmare Seed ( Nightmare Seed )
+ case 34511: // Valor (Bulwark of Kings, Bulwark of the Ancient Kings)
+ case 44055: // Tremendous Fortitude (Battlemaster's Alacrity)
+ {
+ if(Real)
+ {
+ if(apply)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->ModifyHealth(m_modifier.m_amount);
+ }
+ else
+ {
+ if (int32(m_target->GetHealth()) > m_modifier.m_amount)
+ m_target->ModifyHealth(-m_modifier.m_amount);
+ else
+ m_target->SetHealth(1);
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ }
+ return;
+ }
+ }
+
+ // generic case
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool Real)
+{
+ uint32 oldhealth = m_target->GetHealth();
+ double healthPercentage = (double)oldhealth / (double)m_target->GetMaxHealth();
+
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+
+ // refresh percentage
+ if(oldhealth > 0)
+ {
+ uint32 newhealth = uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage));
+ if(newhealth==0)
+ newhealth = 1;
+
+ m_target->SetHealth(newhealth);
+ }
+}
+
+void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real)
+{
+ Powers powerType = m_target->getPowerType();
+ if(int32(powerType) != m_modifier.m_miscvalue)
+ return;
+
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseEnergyPercent(bool apply, bool Real)
+{
+ Powers powerType = m_target->getPowerType();
+ if(int32(powerType) != m_modifier.m_miscvalue)
+ return;
+
+ m_target->HandleStatModifier(UnitMods(UNIT_MOD_POWER_START + powerType), TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModIncreaseHealthPercent(bool apply, bool Real)
+{
+ //m_target->ApplyMaxHealthPercentMod(m_modifier.m_amount,apply);
+ m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+/********************************/
+/*** FIGHT ***/
+/********************************/
+
+void Aura::HandleAuraModParryPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateParryPercentage();
+}
+
+void Aura::HandleAuraModDodgePercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateDodgePercentage();
+ //sLog.outError("BONUS DODGE CHANCE: + %f", float(m_modifier.m_amount));
+}
+
+void Aura::HandleAuraModBlockPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateBlockPercentage();
+ //sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_modifier.m_amount));
+}
+
+void Aura::HandleAuraModRegenInterrupt(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateManaRegen();
+}
+
+void Aura::HandleAuraModCritPercent(bool apply, bool Real)
+{
+ if(m_target->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ // apply item specific bonuses for already equipped weapon
+ if(Real)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraCritMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if (GetSpellProto()->EquippedItemClass == -1)
+ {
+ ((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ ((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ ((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+}
+
+void Aura::HandleModHitChance(bool apply, bool Real)
+{
+ m_target->m_modMeleeHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount);
+ m_target->m_modRangedHitChance += apply ? m_modifier.m_amount : (-m_modifier.m_amount);
+}
+
+void Aura::HandleModSpellHitChance(bool apply, bool Real)
+{
+ m_target->m_modSpellHitChance += apply ? m_modifier.m_amount: (-m_modifier.m_amount);
+}
+
+void Aura::HandleModSpellCritChance(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_target)->UpdateAllSpellCritChances();
+ }
+ else
+ {
+ m_target->m_baseSpellCritChance += apply ? m_modifier.m_amount:(-m_modifier.m_amount);
+ }
+}
+
+void Aura::HandleModSpellCritChanceShool(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for(int school = SPELL_SCHOOL_NORMAL; school < MAX_SPELL_SCHOOL; ++school)
+ if (m_modifier.m_miscvalue & (1<<school))
+ ((Player*)m_target)->UpdateSpellCritChance(school);
+}
+
+/********************************/
+/*** ATTACK SPEED ***/
+/********************************/
+
+void Aura::HandleModCastingSpeed(bool apply, bool Real)
+{
+ m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply);
+}
+
+void Aura::HandleModMeleeRangedSpeedPct(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleModCombatSpeedPct(bool apply, bool Real)
+{
+ m_target->ApplyCastTimePercentMod(m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleModAttackSpeed(bool apply, bool Real)
+{
+ if(!m_target->isAlive() )
+ return;
+
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleHaste(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(BASE_ATTACK, m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(OFF_ATTACK, m_modifier.m_amount,apply);
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount,apply);
+}
+
+void Aura::HandleAuraModRangedHaste(bool apply, bool Real)
+{
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_modifier.m_amount, apply);
+}
+
+void Aura::HandleRangedAmmoHaste(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_modifier.m_amount, apply);
+}
+
+/********************************/
+/*** ATTACK POWER ***/
+/********************************/
+
+void Aura::HandleAuraModAttackPower(bool apply, bool Real)
+{
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPower(bool apply, bool Real)
+{
+ if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraAttackPowerAttacker(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+ Unit *caster = GetCaster();
+
+ if (!caster)
+ return;
+
+ // Hunter's Mark
+ if (m_spellProto->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellProto->SpellFamilyFlags & 0x0000000000000400LL)
+ {
+ // Check Improved Hunter's Mark bonus on caster
+ Unit::AuraList const& mOverrideClassScript = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ // mproved Hunter's Mark script from 5236 to 5240
+ if (mod->m_miscvalue >= 5236 && mod->m_miscvalue <= 5240)
+ {
+ // Get amount of ranged bonus for this spell..
+ int32 ranged_bonus = caster->CalculateSpellDamage(m_spellProto, 1, m_spellProto->EffectBasePoints[1], m_target);
+ // Set melee attack power bonus % from ranged depends from Improved mask aura
+ m_modifier.m_amount = mod->m_amount * ranged_bonus / 100;
+ m_currentBasePoints = m_modifier.m_amount;
+ break;
+ }
+ }
+ return;
+ }
+}
+
+void Aura::HandleAuraModAttackPowerPercent(bool apply, bool Real)
+{
+ //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real)
+{
+ if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1
+ m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER && (m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
+ return;
+
+ if(m_modifier.m_miscvalue != STAT_INTELLECT)
+ {
+ // support required adding UpdateAttackPowerAndDamage calls at stat update
+ sLog.outError("Aura SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT (212) need support non-intelect stats!");
+ return;
+ }
+
+ // Recalculate bonus
+ ((Player*)m_target)->UpdateAttackPowerAndDamage(true);
+}
+
+/********************************/
+/*** DAMAGE BONUS ***/
+/********************************/
+void Aura::HandleModDamageDone(bool apply, bool Real)
+{
+ // apply item specific bonuses for already equipped weapon
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // m_modifier.m_miscvalue is bitmask of spell schools
+ // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL)
+ // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands
+ // 127 - full bitmask any damages
+ //
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0)
+ {
+ // apply generic physical damage bonuses including wand case
+ if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(m_positive)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,m_modifier.m_amount,apply);
+ else
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,m_modifier.m_amount,apply);
+ }
+ }
+
+ // Skip non magic case for speedup
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0)
+ return;
+
+ if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 )
+ {
+ // wand magic case (skip generic to all item spell bonuses)
+ // done in Player::_ApplyWeaponDependentAuraMods
+
+ // Skip item specific requirements for not wand magic damage
+ return;
+ }
+
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(m_positive)
+ {
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if((m_modifier.m_miscvalue & (1<<i)) != 0)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i,m_modifier.m_amount,apply);
+ }
+ }
+ else
+ {
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ {
+ if((m_modifier.m_miscvalue & (1<<i)) != 0)
+ m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i,m_modifier.m_amount,apply);
+ }
+ }
+ Pet* pet = m_target->GetPet();
+ if(pet)
+ pet->UpdateAttackPowerAndDamage();
+ }
+}
+
+void Aura::HandleModDamagePercentDone(bool apply, bool Real)
+{
+ sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", m_modifier.m_miscvalue, m_positive ? 0 : 1);
+
+ // apply item specific bonuses for already equipped weapon
+ if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
+ ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
+ }
+
+ // m_modifier.m_miscvalue is bitmask of spell schools
+ // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL)
+ // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wand
+ // 127 - full bitmask any damages
+ //
+ // mods must be applied base at equipped weapon class and subclass comparison
+ // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
+ // m_modifier.m_miscvalue comparison with item generated damage types
+
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0)
+ {
+ // apply generic physical damage bonuses including wand case
+ if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER)
+ {
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(m_modifier.m_amount), apply);
+ }
+ else
+ {
+ // done in Player::_ApplyWeaponDependentAuraMods
+ }
+ // For show in client
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_modifier.m_amount/100.0f,apply);
+ }
+
+ // Skip non magic case for speedup
+ if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0)
+ return;
+
+ if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 )
+ {
+ // wand magic case (skip generic to all item spell bonuses)
+ // done in Player::_ApplyWeaponDependentAuraMods
+
+ // Skip item specific requirements for not wand magic damage
+ return;
+ }
+
+ // Magic damage percent modifiers implemented in Unit::SpellDamageBonus
+ // Send info to client
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i,m_modifier.m_amount/100.0f,apply);
+}
+
+void Aura::HandleModOffhandDamagePercent(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ sLog.outDebug("AURA MOD OFFHAND DAMAGE");
+
+ m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+/********************************/
+/*** POWER COST ***/
+/********************************/
+
+void Aura::HandleModPowerCostPCT(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ float amount = m_modifier.m_amount/100.0f;
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->ApplyModSignedFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,amount,apply);
+}
+
+void Aura::HandleModPowerCost(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if(m_modifier.m_miscvalue & (1<<i))
+ m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,m_modifier.m_amount,apply);
+}
+
+/*********************************************************/
+/*** OTHERS ***/
+/*********************************************************/
+
+void Aura::HandleShapeshiftBoosts(bool apply)
+{
+ uint32 spellId = 0;
+ uint32 spellId2 = 0;
+ uint32 HotWSpellId = 0;
+
+ switch(GetModifier()->m_miscvalue)
+ {
+ case FORM_CAT:
+ spellId = 3025;
+ HotWSpellId = 24900;
+ break;
+ case FORM_TREE:
+ spellId = 5420;
+ break;
+ case FORM_TRAVEL:
+ spellId = 5419;
+ break;
+ case FORM_AQUA:
+ spellId = 5421;
+ break;
+ case FORM_BEAR:
+ spellId = 1178;
+ spellId2 = 21178;
+ HotWSpellId = 24899;
+ break;
+ case FORM_DIREBEAR:
+ spellId = 9635;
+ spellId2 = 21178;
+ HotWSpellId = 24899;
+ break;
+ case FORM_BATTLESTANCE:
+ spellId = 21156;
+ break;
+ case FORM_DEFENSIVESTANCE:
+ spellId = 7376;
+ break;
+ case FORM_BERSERKERSTANCE:
+ spellId = 7381;
+ break;
+ case FORM_MOONKIN:
+ spellId = 24905;
+ // aura from effect trigger spell
+ spellId2 = 24907;
+ break;
+ case FORM_FLIGHT:
+ spellId = 33948;
+ break;
+ case FORM_FLIGHT_EPIC:
+ spellId = 40122;
+ spellId2 = 40121;
+ break;
+ case FORM_SPIRITOFREDEMPTION:
+ spellId = 27792;
+ spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation.
+ break;
+ case FORM_GHOSTWOLF:
+ case FORM_AMBIENT:
+ case FORM_GHOUL:
+ case FORM_SHADOW:
+ case FORM_STEALTH:
+ case FORM_CREATURECAT:
+ case FORM_CREATUREBEAR:
+ spellId = 0;
+ break;
+ }
+
+ uint32 form = GetModifier()->m_miscvalue-1;
+
+ if(apply)
+ {
+ if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this );
+ if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this);
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ const PlayerSpellMap& 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;
+ if(itr->first==spellId || itr->first==spellId2) continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (!spellInfo || !(spellInfo->Attributes & ((1<<6) | (1<<7)))) continue;
+ if (spellInfo->Stances & (1<<form))
+ m_target->CastSpell(m_target, itr->first, true, NULL, this);
+ }
+ //LotP
+ if (((Player*)m_target)->HasSpell(17007))
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932);
+ if (spellInfo && spellInfo->Stances & (1<<form))
+ m_target->CastSpell(m_target, 24932, true, NULL, this);
+ }
+ // HotW
+ if (HotWSpellId)
+ {
+ Unit::AuraList const& mModTotalStatPct = m_target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE);
+ for(Unit::AuraList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 240 && (*i)->GetModifier()->m_miscvalue == 3)
+ {
+ int32 HotWMod = (*i)->GetModifier()->m_amount;
+ if(GetModifier()->m_miscvalue == FORM_CAT)
+ HotWMod /= 2;
+
+ m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ m_target->RemoveAurasDueToSpell(spellId);
+ m_target->RemoveAurasDueToSpell(spellId2);
+
+ Unit::AuraMap& tAuras = m_target->GetAuras();
+ for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
+ {
+ if (itr->second->IsRemovedOnShapeLost())
+ {
+ m_target->RemoveAurasDueToSpell(itr->second->GetId());
+ itr = tAuras.begin();
+ }
+ else
+ {
+ ++itr;
+ }
+ }
+ }
+
+ /*double healthPercentage = (double)m_target->GetHealth() / (double)m_target->GetMaxHealth();
+ m_target->SetHealth(uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)));*/
+}
+
+void Aura::HandleAuraEmpathy(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_target->GetEntry());
+ if(ci && ci->type == CREATURE_TYPE_BEAST)
+ {
+ m_target->ApplyModUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO, apply);
+ }
+}
+
+void Aura::HandleAuraUntrackable(bool apply, bool Real)
+{
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE);
+}
+
+void Aura::HandleAuraModPacify(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED);
+}
+
+void Aura::HandleAuraModPacifyAndSilence(bool apply, bool Real)
+{
+ HandleAuraModPacify(apply,Real);
+ HandleAuraModSilence(apply,Real);
+}
+
+void Aura::HandleAuraGhost(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(apply)
+ {
+ m_target->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST);
+ }
+ else
+ {
+ m_target->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST);
+ }
+}
+
+void Aura::HandleAuraAllowFlight(bool apply, bool Real)
+{
+ // all applied/removed only at real aura add/remove
+ if(!Real)
+ return;
+
+ // allow fly
+ WorldPacket data;
+ if(apply)
+ data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12);
+ else
+ data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12);
+ data.append(m_target->GetPackGUID());
+ data << uint32(0); // unk
+ m_target->SendMessageToSet(&data, true);
+}
+
+void Aura::HandleModRating(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
+ if (m_modifier.m_miscvalue & (1 << rating))
+ ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), m_modifier.m_amount, apply);
+}
+
+void Aura::HandleForceMoveForward(bool apply, bool Real)
+{
+ if(!Real || m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
+}
+
+void Aura::HandleAuraModExpertise(bool apply, bool Real)
+{
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)m_target)->UpdateExpertise(BASE_ATTACK);
+ ((Player*)m_target)->UpdateExpertise(OFF_ATTACK);
+}
+
+void Aura::HandleModTargetResistance(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+ // applied to damage as HandleNoImmediateEffect in Unit::CalcAbsorbResist and Unit::CalcArmorReducedDamage
+
+ // show armor penetration
+ if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL))
+ m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,m_modifier.m_amount, apply);
+
+ // show as spell penetration only full spell penetration bonuses (all resistances except armor and holy
+ if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL)
+ m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,m_modifier.m_amount, apply);
+}
+
+//HandleNoImmediateEffect auras implementation to support new stat system
+void Aura::HandleAuraHealing(bool apply, bool Real)
+{
+ //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_VALUE, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraHealingPct(bool apply, bool Real)
+{
+ //m_target->HandleStatModifier(UNIT_MOD_HEALING, TOTAL_PCT, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleShieldBlockValue(bool apply, bool Real)
+{
+ BaseModType modType = FLAT_MOD;
+ if(m_modifier.m_auraname == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT)
+ modType = PCT_MOD;
+
+ if(m_target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_target)->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(m_modifier.m_amount), apply);
+}
+
+void Aura::HandleAuraRetainComboPoints(bool apply, bool Real)
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ if(m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *target = (Player*)m_target;
+
+ // combo points was added in SPELL_EFFECT_ADD_COMBO_POINTS handler
+ // remove only if aura expire by time (in case combo points amount change aura removed without combo points lost)
+ if( !apply && m_duration==0 && target->GetComboTarget())
+ if(Unit* unit = ObjectAccessor::GetUnit(*m_target,target->GetComboTarget()))
+ target->AddComboPoints(unit, -m_modifier.m_amount);
+}
+
+void Aura::HandleModUnattackable( bool Apply, bool Real )
+{
+ if(Real && Apply)
+ m_target->CombatStop();
+
+ m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE,Apply);
+}
+
+void Aura::HandleSpiritOfRedemption( bool apply, bool Real )
+{
+ // spells required only Real aura add/remove
+ if(!Real)
+ return;
+
+ // prepare spirit state
+ if(apply)
+ {
+ if(m_target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // disable breath/etc timers
+ ((Player*)m_target)->StopMirrorTimers();
+
+ // set stand state (expected in this form)
+ if(!m_target->IsStandState())
+ m_target->SetStandState(PLAYER_STATE_NONE);
+ }
+
+ m_target->SetHealth(1);
+ }
+ // die at aura end
+ else
+ m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, GetSpellProto(), false);
+}
+
+void Aura::CleanupTriggeredSpells()
+{
+ uint32 tSpellId = m_spellProto->EffectTriggerSpell[GetEffIndex()];
+ if(!tSpellId)
+ return;
+
+ SpellEntry const* tProto = sSpellStore.LookupEntry(tSpellId);
+ if(!tProto)
+ return;
+
+ if(GetSpellDuration(tProto) != -1)
+ return;
+
+ // needed for spell 43680, maybe others
+ // TODO: is there a spell flag, which can solve this in a more sophisticated way?
+ if(m_spellProto->EffectApplyAuraName[GetEffIndex()] == SPELL_AURA_PERIODIC_TRIGGER_SPELL &&
+ GetSpellDuration(m_spellProto) == m_spellProto->EffectAmplitude[GetEffIndex()])
+ return;
+ m_target->RemoveAurasDueToSpell(tSpellId);
+}
+
+void Aura::HandleAuraPowerBurn(bool apply, bool Real)
+{
+ if (m_periodicTimer <= 0)
+ m_periodicTimer += m_modifier.periodictime;
+
+ m_isPeriodic = apply;
+}
+
+void Aura::HandleSchoolAbsorb(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // prevent double apply bonuses
+ if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()))
+ {
+ if(Unit* caster = GetCaster())
+ {
+ float DoneActualBenefit = 0.0f;
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_PRIEST:
+ if(m_spellProto->SpellFamilyFlags == 0x1) //PW:S
+ {
+ //+30% from +healing bonus
+ DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.3f;
+ break;
+ }
+ break;
+ case SPELLFAMILY_MAGE:
+ if(m_spellProto->SpellFamilyFlags == 0x80100 || m_spellProto->SpellFamilyFlags == 0x8 || m_spellProto->SpellFamilyFlags == 0x100000000LL)
+ {
+ //frost ward, fire ward, ice barrier
+ //+10% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f;
+ break;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ if(m_spellProto->SpellFamilyFlags == 0x00)
+ {
+ //shadow ward
+ //+10% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
+
+ m_modifier.m_amount += (int32)DoneActualBenefit;
+ }
+ }
+}
+
+void Aura::PeriodicTick()
+{
+ if(!m_target->isAlive())
+ return;
+
+ switch(m_modifier.m_auraname)
+ {
+ case SPELL_AURA_PERIODIC_DAMAGE:
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ // some auras remove at specific health level or more
+ if(m_modifier.m_auraname==SPELL_AURA_PERIODIC_DAMAGE)
+ {
+ switch(GetId())
+ {
+ case 43093: case 31956: case 38801:
+ case 35321: case 38363: case 39215:
+ if(m_target->GetHealth() == m_target->GetMaxHealth() )
+ {
+ m_target->RemoveAurasDueToSpell(GetId());
+ return;
+ }
+ break;
+ case 38772:
+ {
+ uint32 percent =
+ GetEffIndex() < 2 && GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_DUMMY ?
+ pCaster->CalculateSpellDamage(GetSpellProto(),GetEffIndex()+1,GetSpellProto()->EffectBasePoints[GetEffIndex()+1],m_target) :
+ 100;
+ if(m_target->GetHealth()*100 >= m_target->GetMaxHealth()*percent )
+ {
+ m_target->RemoveAurasDueToSpell(GetId());
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ uint32 absorb=0;
+ uint32 resist=0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage;
+
+ if(m_modifier.m_auraname == SPELL_AURA_PERIODIC_DAMAGE)
+ {
+ pdamage = amount;
+
+ // Calculate armor mitigation if it is a physical spell
+ // But not for bleed mechanic spells
+ if ( GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL &&
+ GetEffectMechanic(GetSpellProto(), m_effIndex) != MECHANIC_BLEED)
+ {
+ uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage);
+ cleanDamage.damage += pdamage - pdamageReductedArmor;
+ pdamage = pdamageReductedArmor;
+ }
+
+ pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT);
+
+ // Curse of Agony damage-per-tick calculation
+ if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x0000000000000400LL) && GetSpellProto()->SpellIconID==544)
+ {
+ // 1..4 ticks, 1/2 from normal tick damage
+ if (m_duration>=((m_maxduration-m_modifier.periodictime)*2/3))
+ pdamage = pdamage/2;
+ // 9..12 ticks, 3/2 from normal tick damage
+ else if(m_duration<((m_maxduration-m_modifier.periodictime)/3))
+ pdamage += (pdamage+1)/2; // +1 prevent 0.5 damage possible lost at 1..4 ticks
+ // 5..8 ticks have normal tick damage
+ }
+ }
+ else
+ pdamage = uint32(m_target->GetMaxHealth()*amount/100);
+
+ //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)
+ pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage);
+
+ pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist);
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
+ GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb);
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)pdamage;
+ data << (uint32)GetSpellSchoolMask(GetSpellProto()); // will be mask in 2.4.x
+ data << (uint32)absorb;
+ data << (uint32)resist;
+ m_target->SendMessageToSet(&data,true);
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+
+ pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
+
+ // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
+
+ pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), GetSpellSchoolMask(spellProto), spellProto);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_LEECH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if(!pCaster->isAlive())
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ uint32 absorb=0;
+ uint32 resist=0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ //Calculate armor mitigation if it is a physical spell
+ if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage);
+ cleanDamage.damage += pdamage - pdamageReductedArmor;
+ pdamage = pdamageReductedArmor;
+ }
+
+ pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT);
+
+ // talent Soul Siphon add bonus to Drain Life spells
+ if( GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x8) )
+ {
+ // find talent max bonus percentage
+ Unit::AuraList const& mClassScriptAuras = pCaster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = mClassScriptAuras.begin(); i != mClassScriptAuras.end(); ++i)
+ {
+ if ((*i)->GetModifier()->m_miscvalue == 4992 || (*i)->GetModifier()->m_miscvalue == 4993)
+ {
+ if((*i)->GetEffIndex()!=1)
+ {
+ sLog.outError("Expected spell %u structure change, need code update",(*i)->GetId());
+ break;
+ }
+
+ // effect 1 m_amount
+ int32 maxPercent = (*i)->GetModifier()->m_amount;
+ // effect 0 m_amount
+ int32 stepPercent = pCaster->CalculateSpellDamage((*i)->GetSpellProto(),0,(*i)->GetSpellProto()->EffectBasePoints[0],pCaster);
+
+ // count affliction effects and calc additional damage in percentage
+ int32 modPercent = 0;
+ Unit::AuraMap const& victimAuras = m_target->GetAuras();
+ for (Unit::AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr)
+ {
+ Aura* aura = itr->second;
+ if (aura->IsPositive())continue;
+ SpellEntry const* m_spell = aura->GetSpellProto();
+ if (m_spell->SpellFamilyName != SPELLFAMILY_WARLOCK)
+ continue;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(m_spell->Id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(m_spell->Id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(_spell_idx->second->skillId == SKILL_AFFLICTION)
+ {
+ modPercent += stepPercent;
+ if (modPercent >= maxPercent)
+ {
+ modPercent = maxPercent;
+ break;
+ }
+ }
+ }
+ }
+ pdamage += (pdamage*modPercent/100);
+ 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)
+ pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage);
+
+ pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist);
+
+ if(m_target->GetHealth() < pdamage)
+ pdamage = uint32(m_target->GetHealth());
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) health leech of %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
+ GetCasterGUID(), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb);
+
+ pCaster->SendSpellNonMeleeDamageLog(m_target, GetId(), pdamage, GetSpellSchoolMask(GetSpellProto()), absorb, resist, false, 0);
+
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+ float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1;
+
+ uint32 new_damage = pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), false);
+
+ // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
+
+ pCaster->ProcDamageAndSpell(target, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, new_damage, GetSpellSchoolMask(spellProto), spellProto);
+ if (!target->isAlive() && pCaster->IsNonMeleeSpellCasted(false))
+ {
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (pCaster->m_currentSpells[i] && pCaster->m_currentSpells[i]->m_spellInfo->Id == spellProto->Id)
+ pCaster->m_currentSpells[i]->cancel();
+ }
+ }
+
+
+ if(Player *modOwner = pCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ uint32 heal = pCaster->SpellHealingBonus(spellProto, uint32(new_damage * multiplier), DOT, pCaster);
+
+ int32 gain = pCaster->ModifyHealth(heal);
+ pCaster->getHostilRefManager().threatAssist(pCaster, gain * 0.5f, spellProto);
+
+ pCaster->SendHealSpellLog(pCaster, spellProto->Id, heal);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ // heal for caster damage (must be alive)
+ if(m_target != pCaster && GetSpellProto()->SpellVisual==163 && !pCaster->isAlive())
+ return;
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage;
+
+ if(m_modifier.m_auraname==SPELL_AURA_OBS_MOD_HEALTH)
+ pdamage = uint32(m_target->GetMaxHealth() * amount/100);
+ else
+ pdamage = amount;
+
+ pdamage = pCaster->SpellHealingBonus(GetSpellProto(), pdamage, DOT, m_target);
+
+ 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());
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyHealth(pdamage);
+
+ // add HoTs to amount healed in bgs
+ if( pCaster->GetTypeId() == TYPEID_PLAYER )
+ if( BattleGround *bg = ((Player*)pCaster)->GetBattleGround() )
+ bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain);
+
+ //Do check before because m_modifier.auraName can be invalidate by DealDamage.
+ bool procSpell = (m_modifier.m_auraname == SPELL_AURA_PERIODIC_HEAL && m_target != pCaster);
+
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+
+ Unit* target = m_target; // aura can be deleted in DealDamage
+ SpellEntry const* spellProto = GetSpellProto();
+ bool haveCastItem = GetCastItemGUID()!=0;
+
+ // heal for caster damage
+ if(m_target!=pCaster && spellProto->SpellVisual==163)
+ {
+ uint32 dmg = spellProto->manaPerSecond;
+ if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER)
+ {
+ pCaster->RemoveAurasDueToSpell(GetId());
+
+ // finish current generic/channeling spells, don't affect autorepeat
+ if(pCaster->m_currentSpells[CURRENT_GENERIC_SPELL])
+ {
+ pCaster->m_currentSpells[CURRENT_GENERIC_SPELL]->finish();
+ }
+ if(pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ pCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+ }
+ }
+ else
+ {
+ pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), gain, GetSpellSchoolMask(GetSpellProto()), 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+ pCaster->DealDamage(pCaster, gain, &cleanDamage, NODAMAGE, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
+ }
+ }
+
+ // ignore item heals
+ if(procSpell && !haveCastItem)
+ pCaster->ProcDamageAndSpell(target,PROC_FLAG_HEAL, PROC_FLAG_HEALED, pdamage, SPELL_SCHOOL_MASK_NONE, spellProto);
+ break;
+ }
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ if(!pCaster->isAlive())
+ return;
+
+ if( GetSpellProto()->Effect[GetEffIndex()]==SPELL_EFFECT_PERSISTENT_AREA_AURA &&
+ pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) power leech of %u (TypeId: %u) for %u dmg inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4)
+ break;
+
+ Powers power = Powers(m_modifier.m_miscvalue);
+
+ // power type might have changed between aura applying and tick (druid's shapeshift)
+ if(m_target->getPowerType() != power)
+ break;
+
+ int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power);
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ if (power == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER)
+ drain_amount -= ((Player*)m_target)->GetSpellCritDamageReduction(drain_amount);
+
+ m_target->ModifyPower(power, -drain_amount);
+
+ float gain_multiplier = 0;
+
+ if(pCaster->GetMaxPower(power) > 0)
+ {
+ gain_multiplier = GetSpellProto()->EffectMultipleValue[GetEffIndex()];
+
+ if(Player *modOwner = pCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_MULTIPLE_VALUE, gain_multiplier);
+ }
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)power; // power type
+ data << (uint32)drain_amount;
+ data << (float)gain_multiplier;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain_amount = int32(drain_amount*gain_multiplier);
+
+ if(gain_amount)
+ {
+ int32 gain = pCaster->ModifyPower(power,gain_amount);
+ m_target->AddThreat(pCaster, float(gain) * 0.5f, GetSpellSchoolMask(GetSpellProto()), GetSpellProto());
+ }
+ break;
+ }
+ case SPELL_AURA_PERIODIC_ENERGIZE:
+ {
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ 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(), pdamage, GetId());
+
+ if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4)
+ break;
+
+ Powers power = Powers(m_modifier.m_miscvalue);
+
+ if(m_target->GetMaxPower(power) == 0)
+ break;
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)power; // power type
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyPower(power,pdamage);
+
+ if(Unit* pCaster = GetCaster())
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+ break;
+ }
+ case SPELL_AURA_OBS_MOD_MANA:
+ {
+ // ignore non positive values (can be result apply spellmods to aura damage
+ uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ uint32 pdamage = uint32(m_target->GetMaxPower(POWER_MANA) * amount/100);
+
+ sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u mana inflicted by %u",
+ GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId());
+
+ if(m_target->GetMaxPower(POWER_MANA) == 0)
+ break;
+
+ WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size
+ data.append(m_target->GetPackGUID());
+ data.appendPackGUID(GetCasterGUID());
+ data << uint32(GetId());
+ data << uint32(1);
+ data << uint32(m_modifier.m_auraname);
+ data << (uint32)0; // ?
+ data << (uint32)pdamage;
+ m_target->SendMessageToSet(&data,true);
+
+ int32 gain = m_target->ModifyPower(POWER_MANA, pdamage);
+
+ if(Unit* pCaster = GetCaster())
+ m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
+ break;
+ }
+ case SPELL_AURA_POWER_BURN_MANA:
+ {
+ Unit *pCaster = GetCaster();
+ if(!pCaster)
+ return;
+
+ // Check for immune (not use charges)
+ if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto())))
+ return;
+
+ int32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0;
+
+ Powers powerType = Powers(m_modifier.m_miscvalue);
+
+ if(!m_target->isAlive() || m_target->getPowerType() != powerType)
+ return;
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ if (powerType == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER)
+ pdamage -= ((Player*)m_target)->GetSpellCritDamageReduction(pdamage);
+
+ uint32 gain = uint32(-m_target->ModifyPower(powerType, -pdamage));
+
+ gain = uint32(gain * GetSpellProto()->EffectMultipleValue[GetEffIndex()]);
+
+ //maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG
+ pCaster->SpellNonMeleeDamageLog(m_target, GetId(), gain);
+ break;
+ }
+ // Here tick dummy auras
+ case SPELL_AURA_PERIODIC_DUMMY:
+ {
+ PeriodicDummyTick();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Aura::PeriodicDummyTick()
+{
+ SpellEntry const* spell = GetSpellProto();
+ switch (spell->Id)
+ {
+ // Drink
+ case 430:
+ case 431:
+ case 432:
+ case 1133:
+ case 1135:
+ case 1137:
+ case 10250:
+ case 22734:
+ case 27089:
+ case 34291:
+ case 43706:
+ case 46755:
+ {
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // Search SPELL_AURA_MOD_POWER_REGEN aura for this spell and add bonus
+ Unit::AuraList const& aura = m_target->GetAurasByType(SPELL_AURA_MOD_POWER_REGEN);
+ for(Unit::AuraList::const_iterator i = aura.begin(); i != aura.end(); ++i)
+ {
+ if ((*i)->GetId() == GetId())
+ {
+ // Get tick number
+ int32 tick = (m_maxduration - m_duration) / m_modifier.periodictime;
+ // Default case (not on arenas)
+ if (tick == 0)
+ {
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount;
+ ((Player*)m_target)->UpdateManaRegen();
+ // Disable continue
+ m_isPeriodic = false;
+ }
+ return;
+ //**********************************************
+ // Code commended since arena patch not added
+ // This feature uses only in arenas
+ //**********************************************
+ // Here need increase mana regen per tick (6 second rule)
+ // on 0 tick - 0 (handled in 2 second)
+ // on 1 tick - 166% (handled in 4 second)
+ // on 2 tick - 133% (handled in 6 second)
+ // Not need update after 3 tick
+ /*
+ if (tick > 3)
+ return;
+ // Apply bonus for 0 - 3 tick
+ switch (tick)
+ {
+ case 0: // 0%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount = 0;
+ break;
+ case 1: // 166%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount * 5 / 3;
+ break;
+ case 2: // 133%
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount * 4 / 3;
+ break;
+ default: // 100% - normal regen
+ (*i)->GetModifier()->m_amount = m_modifier.m_amount;
+ break;
+ }
+ ((Player*)m_target)->UpdateManaRegen();
+ return;*/
+ }
+ }
+ return;
+ }
+// // Panda
+// case 19230: break;
+// // Master of Subtlety
+// case 31666: break;
+// // Gossip NPC Periodic - Talk
+// case 33208: break;
+// // Gossip NPC Periodic - Despawn
+// case 33209: break;
+// // Force of Nature
+// case 33831: break;
+ // Aspect of the Viper
+ case 34074:
+ {
+ if (m_target->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // Should be manauser
+ if (m_target->getPowerType()!=POWER_MANA)
+ return;
+ Unit *caster = GetCaster();
+ if (!caster)
+ return;
+ // Regen amount is max (100% from spell) on 21% or less mana and min on 92.5% or greater mana (20% from spell)
+ int mana = m_target->GetPower(POWER_MANA);
+ int max_mana = m_target->GetMaxPower(POWER_MANA);
+ int32 base_regen = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target);
+ float regen_pct = 1.20f - 1.1f * mana / max_mana;
+ if (regen_pct > 1.0f) regen_pct = 1.0f;
+ else if (regen_pct < 0.2f) regen_pct = 0.2f;
+ m_modifier.m_amount = int32 (base_regen * regen_pct);
+ ((Player*)m_target)->UpdateManaRegen();
+ return;
+ }
+// // Steal Weapon
+// case 36207: break;
+// // Simon Game START timer, (DND)
+// case 39993: break;
+// // Harpooner's Mark
+// case 40084: 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;
+// // 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;
+// // 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;
+// // Mole Machine Portal Schedule
+// case 49466: break;
+// // Drink Coffee
+// case 49472: break;
+// // Listening to Music
+// case 50493: break;
+// // Love Rocket Barrage
+// case 50530: break;
+ default:
+ break;
+ }
+}
+
+void Aura::HandlePreventFleeing(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ Unit::AuraList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR);
+ if( !fearAuras.empty() )
+ {
+ if (apply)
+ m_target->SetFeared(false, fearAuras.front()->GetCasterGUID());
+ else
+ m_target->SetFeared(true);
+ }
+}
+
+void Aura::HandleManaShield(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ // prevent double apply bonuses
+ if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()))
+ {
+ if(Unit* caster = GetCaster())
+ {
+ float DoneActualBenefit = 0.0f;
+ switch(m_spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ if(m_spellProto->SpellFamilyFlags & 0x8000)
+ {
+ // Mana Shield
+ // +50% from +spd bonus
+ DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.5f;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
+
+ m_modifier.m_amount += (int32)DoneActualBenefit;
+ }
+ }
+}
+
+void Aura::HandleArenaPreparation(bool apply, bool Real)
+{
+ if(!Real)
+ return;
+
+ if(apply)
+ m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
+ else
+ m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
+}
diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h
new file mode 100644
index 00000000000..24b94202978
--- /dev/null
+++ b/src/game/SpellAuras.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef MANGOS_SPELLAURAS_H
+#define MANGOS_SPELLAURAS_H
+
+#include "SpellAuraDefines.h"
+
+struct DamageManaShield
+{
+ uint32 m_spellId;
+ uint32 m_modType;
+ int32 m_schoolType;
+ uint32 m_totalAbsorb;
+ uint32 m_currAbsorb;
+};
+
+struct Modifier
+{
+ AuraType m_auraname;
+ int32 m_amount;
+ int32 m_miscvalue;
+ uint32 periodictime;
+};
+
+class Unit;
+struct SpellEntry;
+struct SpellModifier;
+struct ProcTriggerSpell;
+
+// forward decl
+class Aura;
+
+typedef void(Aura::*pAuraHandler)(bool Apply, bool Real);
+// Real == true at aura add/remove
+// Real == false at aura mod unapply/reapply; when adding/removing dependent aura/item/stat mods
+//
+// Code in aura handler can be guarded by if(Real) check if it should execution only at real add/remove of aura
+//
+// MAIN RULE: Code MUST NOT be guarded by if(Real) check if it modifies any stats
+// (percent auras, stats mods, etc)
+// Second rule: Code must be guarded by if(Real) check if it modifies object state (start/stop attack, send packets to client, etc)
+//
+// Other case choice: each code line moved under if(Real) check is mangos speedup,
+// each setting object update field code line moved under if(Real) check is significant mangos speedup, and less server->client data sends
+// each packet sending code moved under if(Real) check is _large_ mangos speedup, and lot less server->client data sends
+
+class MANGOS_DLL_SPEC Aura
+{
+ friend Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem);
+
+ public:
+ //aura handlers
+ void HandleNULL(bool, bool)
+ {
+ // NOT IMPLEMENTED
+ }
+ void HandleUnused(bool, bool)
+ {
+ // NOT USED BY ANY SPELL OR USELESS
+ }
+ void HandleNoImmediateEffect(bool, bool)
+ {
+ // aura not have immediate effect at add/remove and handled by ID in other code place
+ }
+ void HandleBindSight(bool Apply, bool Real);
+ void HandleModPossess(bool Apply, bool Real);
+ void HandlePeriodicDamage(bool Apply, bool Real);
+ void HandleAuraDummy(bool Apply, bool Real);
+ void HandleAuraPeriodicDummy(bool apply, bool Real);
+ void HandleModConfuse(bool Apply, bool Real);
+ void HandleModCharm(bool Apply, bool Real);
+ void HandleModFear(bool Apply, bool Real);
+ void HandlePeriodicHeal(bool Apply, bool Real);
+ void HandleModAttackSpeed(bool Apply, bool Real);
+ void HandleModMeleeRangedSpeedPct(bool apply, bool Real);
+ void HandleModCombatSpeedPct(bool apply, bool Real);
+ void HandleModThreat(bool Apply, bool Real);
+ void HandleModTaunt(bool Apply, bool Real);
+ void HandleFeignDeath(bool Apply, bool Real);
+ void HandleAuraModDisarm(bool Apply, bool Real);
+ void HandleAuraModStalked(bool Apply, bool Real);
+ void HandleAuraWaterWalk(bool Apply, bool Real);
+ void HandleAuraFeatherFall(bool Apply, bool Real);
+ void HandleAuraHover(bool Apply, bool Real);
+ void HandleAddModifier(bool Apply, bool Real);
+ void HandleAuraModStun(bool Apply, bool Real);
+ void HandleModDamageDone(bool Apply, bool Real);
+ void HandleAuraUntrackable(bool Apply, bool Real);
+ void HandleAuraEmpathy(bool Apply, bool Real);
+ void HandleModOffhandDamagePercent(bool apply, bool Real);
+ void HandleAuraModRangedAttackPower(bool Apply, bool Real);
+ void HandleAuraModIncreaseEnergyPercent(bool Apply, bool Real);
+ void HandleAuraModIncreaseHealthPercent(bool Apply, bool Real);
+ void HandleAuraModRegenInterrupt(bool Apply, bool Real);
+ void HandleHaste(bool Apply, bool Real);
+ void HandlePeriodicTriggerSpell(bool Apply, bool Real);
+ void HandlePeriodicEnergize(bool Apply, bool Real);
+ void HandleAuraModResistanceExclusive(bool Apply, bool Real);
+ void HandleModStealth(bool Apply, bool Real);
+ void HandleInvisibility(bool Apply, bool Real);
+ void HandleInvisibilityDetect(bool Apply, bool Real);
+ void HandleAuraModTotalHealthPercentRegen(bool Apply, bool Real);
+ void HandleAuraModTotalManaPercentRegen(bool Apply, bool Real);
+ void HandleAuraModResistance(bool Apply, bool Real);
+ void HandleAuraModRoot(bool Apply, bool Real);
+ void HandleAuraModSilence(bool Apply, bool Real);
+ void HandleAuraModStat(bool Apply, bool Real);
+ void HandleAuraModIncreaseSpeed(bool Apply, bool Real);
+ void HandleAuraModIncreaseMountedSpeed(bool Apply, bool Real);
+ void HandleAuraModIncreaseFlightSpeed(bool Apply, bool Real);
+ void HandleAuraModDecreaseSpeed(bool Apply, bool Real);
+ void HandleAuraModUseNormalSpeed(bool Apply, bool Real);
+ void HandleAuraModIncreaseHealth(bool Apply, bool Real);
+ void HandleAuraModIncreaseEnergy(bool Apply, bool Real);
+ void HandleAuraModShapeshift(bool Apply, bool Real);
+ void HandleAuraModEffectImmunity(bool Apply, bool Real);
+ void HandleAuraModStateImmunity(bool Apply, bool Real);
+ void HandleAuraModSchoolImmunity(bool Apply, bool Real);
+ void HandleAuraModDmgImmunity(bool Apply, bool Real);
+ void HandleAuraModDispelImmunity(bool Apply, bool Real);
+ void HandleAuraProcTriggerSpell(bool Apply, bool Real);
+ void HandleAuraTrackCreatures(bool Apply, bool Real);
+ void HandleAuraTrackResources(bool Apply, bool Real);
+ void HandleAuraModParryPercent(bool Apply, bool Real);
+ void HandleAuraModDodgePercent(bool Apply, bool Real);
+ void HandleAuraModBlockPercent(bool Apply, bool Real);
+ void HandleAuraModCritPercent(bool Apply, bool Real);
+ void HandlePeriodicLeech(bool Apply, bool Real);
+ void HandleModHitChance(bool Apply, bool Real);
+ void HandleModSpellHitChance(bool Apply, bool Real);
+ void HandleAuraModScale(bool Apply, bool Real);
+ void HandlePeriodicManaLeech(bool Apply, bool Real);
+ void HandleModCastingSpeed(bool Apply, bool Real);
+ void HandleAuraMounted(bool Apply, bool Real);
+ void HandleWaterBreathing(bool Apply, bool Real);
+ void HandleModBaseResistance(bool Apply, bool Real);
+ void HandleModRegen(bool Apply, bool Real);
+ void HandleModPowerRegen(bool Apply, bool Real);
+ void HandleModPowerRegenPCT(bool Apply, bool Real);
+ void HandleChannelDeathItem(bool Apply, bool Real);
+ void HandlePeriodicDamagePCT(bool Apply, bool Real);
+ void HandleAuraModAttackPower(bool Apply, bool Real);
+ void HandleAuraTransform(bool Apply, bool Real);
+ void HandleModSpellCritChance(bool Apply, bool Real);
+ void HandleAuraModIncreaseSwimSpeed(bool Apply, bool Real);
+ void HandleModPowerCostPCT(bool Apply, bool Real);
+ void HandleModPowerCost(bool Apply, bool Real);
+ void HandleFarSight(bool Apply, bool Real);
+ void HandleModPossessPet(bool Apply, bool Real);
+ void HandleModMechanicImmunity(bool Apply, bool Real);
+ void HandleAuraModSkill(bool Apply, bool Real);
+ void HandleModDamagePercentDone(bool Apply, bool Real);
+ void HandleModPercentStat(bool Apply, bool Real);
+ void HandleModResistancePercent(bool Apply, bool Real);
+ void HandleAuraModBaseResistancePCT(bool Apply, bool Real);
+ void HandleModShieldBlockPCT(bool Apply, bool Real);
+ void HandleAuraTrackStealthed(bool Apply, bool Real);
+ void HandleModShieldBlock(bool Apply, bool Real);
+ void HandleForceReaction(bool Apply, bool Real);
+ void HandleAuraModRangedHaste(bool Apply, bool Real);
+ void HandleRangedAmmoHaste(bool Apply, bool Real);
+ void HandleModHealingDone(bool Apply, bool Real);
+ void HandleModTotalPercentStat(bool Apply, bool Real);
+ void HandleAuraModTotalThreat(bool Apply, bool Real);
+ void HandleModUnattackable(bool Apply, bool Real);
+ void HandleAuraModPacify(bool Apply, bool Real);
+ void HandleAuraGhost(bool Apply, bool Real);
+ void HandleAuraAllowFlight(bool Apply, bool Real);
+ void HandleModRating(bool apply, bool Real);
+ void HandleModTargetResistance(bool apply, bool Real);
+ void HandleAuraAttackPowerAttacker(bool apply, bool Real);
+ void HandleAuraModAttackPowerPercent(bool apply, bool Real);
+ void HandleAuraModRangedAttackPowerPercent(bool apply, bool Real);
+ void HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real);
+ void HandleSpiritOfRedemption(bool apply, bool Real);
+ void HandleAuraHealingPct(bool apply, bool Real);
+ void HandleModManaRegen(bool apply, bool Real);
+ void HandleComprehendLanguage(bool apply, bool Real);
+ void HandleAuraHealing(bool apply, bool Real);
+ void HandleShieldBlockValue(bool apply, bool Real);
+ void HandleModSpellCritChanceShool(bool apply, bool Real);
+ void HandleAuraRetainComboPoints(bool apply, bool Real);
+ void HandleModSpellDamagePercentFromStat(bool apply, bool Real);
+ void HandleModSpellHealingPercentFromStat(bool apply, bool Real);
+ void HandleAuraModDispelResist(bool apply, bool Real);
+ void HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real);
+ void HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real);
+ void HandleAuraModPacifyAndSilence(bool Apply, bool Real);
+ void HandleAuraModIncreaseMaxHealth(bool apply, bool Real);
+ void HandleAuraModExpertise(bool apply, bool Real);
+ void HandleForceMoveForward(bool apply, bool Real);
+ void HandleAuraModResistenceOfStatPercent(bool apply, bool Real);
+ void HandleAuraPowerBurn(bool apply, bool Real);
+ void HandleSchoolAbsorb(bool apply, bool Real);
+ void HandlePreventFleeing(bool apply, bool Real);
+ void HandleManaShield(bool apply, bool Real);
+ void HandleArenaPreparation(bool apply, bool Real);
+
+ virtual ~Aura();
+
+ void SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue);
+ Modifier* GetModifier() {return &m_modifier;}
+ int32 GetMiscValue() {return m_spellProto->EffectMiscValue[m_effIndex];}
+ int32 GetMiscBValue() {return m_spellProto->EffectMiscValueB[m_effIndex];}
+
+ SpellEntry const* GetSpellProto() const { return m_spellProto; }
+ uint32 GetId() const{ return m_spellProto->Id; }
+ uint64 GetCastItemGUID() const { return m_castItemGuid; }
+ uint32 GetEffIndex() const{ return m_effIndex; }
+ int32 GetBasePoints() const { return m_currentBasePoints; }
+
+ int32 GetAuraMaxDuration() const { return m_maxduration; }
+ void SetAuraMaxDuration(int32 duration) { m_maxduration = duration; }
+ int32 GetAuraDuration() const { return m_duration; }
+ void SetAuraDuration(int32 duration) { m_duration = duration; }
+ time_t GetAuraApplyTime() { return m_applyTime; }
+ void UpdateAuraDuration();
+ void SendAuraDurationForCaster(Player* caster);
+
+ uint64 const& GetCasterGUID() const { return m_caster_guid; }
+ Unit* GetCaster() const;
+ Unit* GetTarget() const { return m_target; }
+ void SetTarget(Unit* target) { m_target = target; }
+ void SetLoadedState(uint64 caster_guid,int32 damage,int32 maxduration,int32 duration,int32 charges)
+ {
+ m_caster_guid = caster_guid;
+ m_modifier.m_amount = damage;
+ m_maxduration = maxduration;
+ m_duration = duration;
+ m_procCharges = charges;
+ }
+
+ uint8 GetAuraSlot() const { return m_auraSlot; }
+ void SetAuraSlot(uint8 slot) { m_auraSlot = slot; }
+ void UpdateAuraCharges()
+ {
+ uint8 slot = GetAuraSlot();
+
+ // only aura inslot with charges and without stack limitation
+ if (slot < MAX_AURAS && m_procCharges >= 1 && GetSpellProto()->StackAmount==0)
+ SetAuraApplication(slot, m_procCharges - 1);
+ }
+
+ bool IsPositive() { return m_positive; }
+ void SetNegative() { m_positive = false; }
+ void SetPositive() { m_positive = true; }
+
+ bool IsPermanent() const { return m_permanent; }
+ bool IsAreaAura() const { return m_isAreaAura; }
+ bool IsPeriodic() const { return m_isPeriodic; }
+ bool IsTrigger() const { return m_isTrigger; }
+ bool IsPassive() const { return m_isPassive; }
+ bool IsPersistent() const { return m_isPersistent; }
+ bool IsDeathPersistent() const { return m_isDeathPersist; }
+ bool IsRemovedOnShapeLost() const { return m_isRemovedOnShapeLost; }
+ bool IsInUse() const { return m_in_use;}
+
+ virtual void Update(uint32 diff);
+ void ApplyModifier(bool apply, bool Real = false);
+
+ void _AddAura();
+ void _RemoveAura();
+
+ void TriggerSpell();
+
+ bool IsUpdated() { return m_updated; }
+ void SetUpdated(bool val) { m_updated = val; }
+ void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; }
+
+ int32 m_procCharges;
+
+ virtual Unit* GetTriggerTarget() const { return m_target; }
+
+ // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras
+ void HandleShapeshiftBoosts(bool apply);
+
+ // Allow Apply Aura Handler to modify and access m_AuraDRGroup
+ void setDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; }
+ DiminishingGroup getDiminishGroup() const { return m_AuraDRGroup; }
+
+ void PeriodicTick();
+ void PeriodicDummyTick();
+ protected:
+ Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL);
+
+ Modifier m_modifier;
+ SpellModifier *m_spellmod;
+ uint32 m_effIndex;
+ SpellEntry const *m_spellProto;
+ int32 m_currentBasePoints; // cache SpellEntry::EffectBasePoints and use for set custom base points
+ uint64 m_caster_guid;
+ Unit* m_target;
+ int32 m_maxduration;
+ int32 m_duration;
+ int32 m_timeCla;
+ uint64 m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted
+ time_t m_applyTime;
+
+ AuraRemoveMode m_removeMode;
+
+ uint8 m_auraSlot;
+
+ bool m_positive:1;
+ bool m_permanent:1;
+ bool m_isPeriodic:1;
+ bool m_isTrigger:1;
+ bool m_isAreaAura:1;
+ bool m_isPassive:1;
+ bool m_isPersistent:1;
+ bool m_isDeathPersist:1;
+ bool m_isRemovedOnShapeLost:1;
+ bool m_updated:1;
+ bool m_in_use:1; // true while in Aura::ApplyModifier call
+
+ int32 m_periodicTimer;
+ uint32 m_PeriodicEventId;
+ DiminishingGroup m_AuraDRGroup;
+ private:
+ void UpdateSlotCounterAndDuration(bool add);
+ void CleanupTriggeredSpells();
+ void SetAura(uint32 slot, bool remove) { m_target->SetUInt32Value(UNIT_FIELD_AURA + slot, remove ? 0 : GetId()); }
+ void SetAuraFlag(uint32 slot, bool add);
+ void SetAuraLevel(uint32 slot, uint32 level);
+ void SetAuraApplication(uint32 slot, int8 count);
+};
+
+class MANGOS_DLL_SPEC AreaAura : public Aura
+{
+ public:
+ AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL);
+ ~AreaAura();
+ void Update(uint32 diff);
+ private:
+ float m_radius;
+ AreaAuraType m_areaAuraType;
+};
+
+class MANGOS_DLL_SPEC PersistentAreaAura : public Aura
+{
+ public:
+ PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL);
+ ~PersistentAreaAura();
+ void Update(uint32 diff);
+};
+
+class MANGOS_DLL_SPEC SingleEnemyTargetAura : public Aura
+{
+ friend Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem);
+
+ public:
+ ~SingleEnemyTargetAura();
+ Unit* GetTriggerTarget() const;
+
+ protected:
+ SingleEnemyTargetAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL);
+ uint64 m_casters_target_guid;
+};
+
+Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL);
+#endif
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
new file mode 100644
index 00000000000..fa037fcdb24
--- /dev/null
+++ b/src/game/SpellEffects.cpp
@@ -0,0 +1,6090 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "SharedDefines.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "SkillExtraItems.h"
+#include "Unit.h"
+#include "CreatureAI.h"
+#include "Spell.h"
+#include "DynamicObject.h"
+#include "SpellAuras.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "SharedDefines.h"
+#include "Pet.h"
+#include "GameObject.h"
+#include "GossipDef.h"
+#include "Creature.h"
+#include "Totem.h"
+#include "CreatureAI.h"
+#include "BattleGround.h"
+#include "BattleGroundEY.h"
+#include "BattleGroundWS.h"
+#include "VMapFactory.h"
+#include "Language.h"
+#include "SocialMgr.h"
+#include "Util.h"
+
+pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
+{
+ &Spell::EffectNULL, // 0
+ &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL
+ &Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE
+ &Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY
+ &Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused
+ &Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS
+ &Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA
+ &Spell::EffectEnvirinmentalDMG, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE
+ &Spell::EffectPowerDrain, // 8 SPELL_EFFECT_POWER_DRAIN
+ &Spell::EffectHealthLeech, // 9 SPELL_EFFECT_HEALTH_LEECH
+ &Spell::EffectHeal, // 10 SPELL_EFFECT_HEAL
+ &Spell::EffectUnused, // 11 SPELL_EFFECT_BIND
+ &Spell::EffectNULL, // 12 SPELL_EFFECT_PORTAL
+ &Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused
+ &Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused
+ &Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused
+ &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE
+ &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL
+ &Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT
+ &Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS
+ &Spell::EffectUnused, // 20 SPELL_EFFECT_DODGE one spell: Dodge
+ &Spell::EffectUnused, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND)
+ &Spell::EffectParry, // 22 SPELL_EFFECT_PARRY
+ &Spell::EffectUnused, // 23 SPELL_EFFECT_BLOCK one spell: Block
+ &Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM
+ &Spell::EffectUnused, // 25 SPELL_EFFECT_WEAPON
+ &Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense
+ &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA
+ &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON
+ &Spell::EffectMomentMove, // 29 SPELL_EFFECT_LEAP
+ &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE
+ &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE
+ &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE
+ &Spell::EffectOpenLock, // 33 SPELL_EFFECT_OPEN_LOCK
+ &Spell::EffectSummonChangeItem, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM
+ &Spell::EffectApplyAreaAura, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY
+ &Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL
+ &Spell::EffectUnused, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND)
+ &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL
+ &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE
+ &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD
+ &Spell::EffectSummonWild, // 41 SPELL_EFFECT_SUMMON_WILD
+ &Spell::EffectSummonGuardian, // 42 SPELL_EFFECT_SUMMON_GUARDIAN
+ &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER
+ &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP
+ &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related
+ &Spell::EffectNULL, // 46 SPELL_EFFECT_SPAWN we must spawn pet there
+ &Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL
+ &Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth
+ &Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect
+ &Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR
+ &Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused
+ &Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT one spell: zzOLDCritical Shot
+ &Spell::EffectEnchantItemPerm, // 53 SPELL_EFFECT_ENCHANT_ITEM
+ &Spell::EffectEnchantItemTmp, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY
+ &Spell::EffectTameCreature, // 55 SPELL_EFFECT_TAMECREATURE
+ &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::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY
+ &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT
+ &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN
+ &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT
+ &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL
+ &Spell::EffectUnused, // 65 SPELL_EFFECT_HEALTH_FUNNEL unused
+ &Spell::EffectUnused, // 66 SPELL_EFFECT_POWER_FUNNEL unused
+ &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
+ &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST
+ &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT
+ &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move
+ &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET
+ &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT
+ &Spell::EffectSummonGuardian, // 73 SPELL_EFFECT_SUMMON_POSSESSED
+ &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM
+ &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit
+ &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD
+ &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT
+ &Spell::EffectUnused, // 78 SPELL_EFFECT_ATTACK
+ &Spell::EffectSanctuary, // 79 SPELL_EFFECT_SANCTUARY
+ &Spell::EffectAddComboPoints, // 80 SPELL_EFFECT_ADD_COMBO_POINTS
+ &Spell::EffectUnused, // 81 SPELL_EFFECT_CREATE_HOUSE one spell: Create House (TEST)
+ &Spell::EffectNULL, // 82 SPELL_EFFECT_BIND_SIGHT
+ &Spell::EffectDuel, // 83 SPELL_EFFECT_DUEL
+ &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK
+ &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER
+ &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT
+ &Spell::EffectSummonTotem, // 87 SPELL_EFFECT_SUMMON_TOTEM_SLOT1
+ &Spell::EffectSummonTotem, // 88 SPELL_EFFECT_SUMMON_TOTEM_SLOT2
+ &Spell::EffectSummonTotem, // 89 SPELL_EFFECT_SUMMON_TOTEM_SLOT3
+ &Spell::EffectSummonTotem, // 90 SPELL_EFFECT_SUMMON_TOTEM_SLOT4
+ &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash
+ &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM
+ &Spell::EffectUnused, // 93 SPELL_EFFECT_SUMMON_PHANTASM
+ &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT
+ &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING
+ &Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE
+ &Spell::EffectSummonCritter, // 97 SPELL_EFFECT_SUMMON_CRITTER
+ &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK
+ &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT
+ &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE
+ &Spell::EffectFeedPet, //101 SPELL_EFFECT_FEED_PET
+ &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET
+ &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION
+ &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1
+ &Spell::EffectSummonObject, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2
+ &Spell::EffectSummonObject, //106 SPELL_EFFECT_SUMMON_OBJECT_SLOT3
+ &Spell::EffectSummonObject, //107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4
+ &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC
+ &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET
+ &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS
+ &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE
+ &Spell::EffectSummonDemon, //112 SPELL_EFFECT_SUMMON_DEMON
+ &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW
+ &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME
+ &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT
+ &Spell::EffectSkinPlayerCorpse, //116 SPELL_EFFECT_SKIN_PLAYER_CORPSE one spell: Remove Insignia, bg usage, required special corpse flags...
+ &Spell::EffectSpiritHeal, //117 SPELL_EFFECT_SPIRIT_HEAL one spell: Spirit Heal
+ &Spell::EffectSkill, //118 SPELL_EFFECT_SKILL professions and more
+ &Spell::EffectApplyAreaAura, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET
+ &Spell::EffectUnused, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD one spell: Graveyard Teleport Test
+ &Spell::EffectWeaponDmg, //121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG
+ &Spell::EffectUnused, //122 SPELL_EFFECT_122 unused
+ &Spell::EffectSendTaxi, //123 SPELL_EFFECT_SEND_TAXI taxi/flight related (misc value is taxi path id)
+ &Spell::EffectPlayerPull, //124 SPELL_EFFECT_PLAYER_PULL opposite of knockback effect (pulls player twoard caster)
+ &Spell::EffectModifyThreatPercent, //125 SPELL_EFFECT_MODIFY_THREAT_PERCENT
+ &Spell::EffectStealBeneficialBuff, //126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF spell steal effect?
+ &Spell::EffectProspecting, //127 SPELL_EFFECT_PROSPECTING Prospecting spell
+ &Spell::EffectApplyAreaAura, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND
+ &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY
+ &Spell::EffectNULL, //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
+ &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
+ &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT
+ &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT
+ &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap
+ &Spell::EffectUnused, //139 SPELL_EFFECT_139 unused
+ &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST
+ &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed?
+ &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE
+ &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER
+ &Spell::EffectNULL, //144 SPELL_EFFECT_144 Spectral Blast
+ &Spell::EffectNULL, //145 SPELL_EFFECT_145 Black Hole Effect
+ &Spell::EffectUnused, //146 SPELL_EFFECT_146 unused
+ &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail
+ &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused
+ &Spell::EffectNULL, //149 SPELL_EFFECT_149 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
+ &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry
+};
+
+void Spell::EffectNULL(uint32 /*i*/)
+{
+ sLog.outDebug("WORLD: Spell Effect DUMMY");
+}
+
+void Spell::EffectUnused(uint32 /*i*/)
+{
+ // NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS
+}
+
+void Spell::EffectResurrectNew(uint32 i)
+{
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!unitTarget->IsInWorld())
+ return;
+
+ Player* pTarget = ((Player*)unitTarget);
+
+ if(pTarget->isRessurectRequested()) // already have one active request
+ return;
+
+ uint32 health = damage;
+ uint32 mana = m_spellInfo->EffectMiscValue[i];
+ pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ SendResurrectRequest(pTarget);
+}
+
+void Spell::EffectInstaKill(uint32 /*i*/)
+{
+ if( !unitTarget || !unitTarget->isAlive() )
+ return;
+
+ // Demonic Sacrifice
+ if(m_spellInfo->Id==18788 && unitTarget->GetTypeId()==TYPEID_UNIT)
+ {
+ uint32 entry = unitTarget->GetEntry();
+ uint32 spellID;
+ switch(entry)
+ {
+ case 416: spellID=18789; break; //imp
+ case 417: spellID=18792; break; //fellhunter
+ case 1860: spellID=18790; break; //void
+ case 1863: spellID=18791; break; //succubus
+ case 17252: spellID=35701; break; //fellguard
+ default:
+ sLog.outError("EffectInstaKill: Unhandled creature entry (%u) case.",entry);
+ return;
+ }
+
+ m_caster->CastSpell(m_caster,spellID,true);
+ }
+
+ if(m_caster==unitTarget) // prevent interrupt message
+ finish();
+
+ uint32 health = unitTarget->GetHealth();
+ m_caster->DealDamage(unitTarget, health, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+}
+
+void Spell::EffectEnvirinmentalDMG(uint32 i)
+{
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ // Note: this hack with damage replace required until GO casting not implemented
+ // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
+ // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc
+ damage = m_spellInfo->EffectBasePoints[i]+m_spellInfo->EffectBaseDice[i];
+
+ m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false);
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->EnvironmentalDamage(m_caster->GetGUID(),DAMAGE_FIRE,damage);
+}
+
+void Spell::EffectSchoolDMG(uint32 effect_idx)
+{
+ if( unitTarget && unitTarget->isAlive())
+ {
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ //Gore
+ if(m_spellInfo->SpellIconID == 2269 )
+ {
+ damage+= rand()%2 ? damage : 0;
+ }
+
+ switch(m_spellInfo->Id) // better way to check unknown
+ {
+ // Meteor like spells (divided damage to targets)
+ case 24340: case 26558: case 28884: // Meteor
+ case 36837: case 38903: case 41276: // Meteor
+ case 26789: // Shard of the Fallen Star
+ case 31436: // Malevolent Cleave
+ case 35181: // Dive Bomb
+ case 40810: case 43267: case 43268: // Saber Lash
+ case 42384: // Brutal Swipe
+ case 45150: // Meteor Slash
+ {
+ uint32 count = 0;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if(ihit->effectMask & (1<<effect_idx))
+ ++count;
+
+ damage /= count; // divide to all targets
+ break;
+ }
+ // percent from health with min
+ case 25599: // Thundercrash
+ {
+ damage = unitTarget->GetHealth() / 2;
+ if(damage < 200)
+ damage = 200;
+ break;
+ }
+ }
+ break;
+ }
+
+ case SPELLFAMILY_MAGE:
+ {
+ // Arcane Blast
+ if(m_spellInfo->SpellFamilyFlags & 0x20000000LL)
+ {
+ m_caster->CastSpell(m_caster,36032,true);
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Bloodthirst
+ if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
+ {
+ damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100);
+ }
+ // Shield Slam
+ else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ damage += int32(m_caster->GetShieldBlockValue());
+ // Victory Rush
+ else if(m_spellInfo->SpellFamilyFlags & 0x10000000000LL)
+ {
+ damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false);
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Incinerate Rank 1 & 2
+ if((m_spellInfo->SpellFamilyFlags & 0x00004000000000LL) && m_spellInfo->SpellIconID==2128)
+ {
+ // Incinerate does more dmg (dmg*0.25) if the target is Immolated.
+ if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE))
+ damage += int32(damage*0.25);
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Ferocious Bite
+ if((m_spellInfo->SpellFamilyFlags & 0x000800000) && m_spellInfo->SpellVisual==6587)
+ {
+ // converts each extra point of energy into ($f1+$AP/630) additional damage
+ float multiple = m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 630 + m_spellInfo->DmgMultiplier[effect_idx];
+ damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple);
+ m_caster->SetPower(POWER_ENERGY,0);
+ }
+ // Rake
+ else if(m_spellInfo->SpellFamilyFlags & 0x0000000000001000LL)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ }
+ // Swipe
+ else if(m_spellInfo->SpellFamilyFlags & 0x0010000000000000LL)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f);
+ }
+ // Starfire
+ else if ( m_spellInfo->SpellFamilyFlags & 0x0004LL )
+ {
+ Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
+ {
+ // Starfire Bonus (caster)
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case 5481: // Nordrassil Regalia - bonus
+ {
+ Unit::AuraList const& m_periodicDamageAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = m_periodicDamageAuras.begin(); itr != m_periodicDamageAuras.end(); ++itr)
+ {
+ // Moonfire or Insect Swarm (target debuff from any casters)
+ if ( (*itr)->GetSpellProto()->SpellFamilyFlags & 0x00200002LL )
+ {
+ int32 mod = (*i)->GetModifier()->m_amount;
+ damage += damage*mod/100;
+ break;
+ }
+ }
+ break;
+ }
+ case 5148: //Improved Starfire - Ivory Idol of the Moongoddes Aura
+ {
+ damage += (*i)->GetModifier()->m_amount;
+ break;
+ }
+ }
+ }
+ }
+ //Mangle Bonus for the initial damage of Lacerate and Rake
+ if((m_spellInfo->SpellFamilyFlags==0x0000000000001000LL && m_spellInfo->SpellIconID==494) ||
+ (m_spellInfo->SpellFamilyFlags==0x0000010000000000LL && m_spellInfo->SpellIconID==2246))
+ {
+ Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ if((*i)->GetSpellProto()->SpellFamilyFlags & 0x0000044000000000LL && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID)
+ {
+ damage = int32(damage*(100.0f+(*i)->GetModifier()->m_amount)/100.0f);
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Envenom
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x800000000LL))
+ {
+ // consume from stack dozes not more that have combo-points
+ if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
+ {
+ // count consumed deadly poison doses at target
+ uint32 doses = 0;
+
+ // remove consumed poison doses
+ Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end() && combo;)
+ {
+ // Deadly poison (only attacker applied)
+ if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && ((*itr)->GetSpellProto()->SpellFamilyFlags & 0x10000) &&
+ (*itr)->GetSpellProto()->SpellVisual==5100 && (*itr)->GetCasterGUID()==m_caster->GetGUID() )
+ {
+ --combo;
+ ++doses;
+
+ unitTarget->RemoveSingleAuraFromStack((*itr)->GetId(), (*itr)->GetEffIndex());
+
+ itr = auras.begin();
+ }
+ else
+ ++itr;
+ }
+
+ damage *= doses;
+ damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses);
+
+ // Eviscerate and Envenom Bonus Damage (item set effect)
+ if(m_caster->GetDummyAura(37169))
+ damage += ((Player*)m_caster)->GetComboPoints()*40;
+ }
+ }
+ // Eviscerate
+ else if((m_spellInfo->SpellFamilyFlags & 0x00020000LL) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f);
+
+ // Eviscerate and Envenom Bonus Damage (item set effect)
+ if(m_caster->GetDummyAura(37169))
+ damage += combo*40;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Mongoose Bite
+ if((m_spellInfo->SpellFamilyFlags & 0x000000002) && m_spellInfo->SpellVisual==342)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2);
+ }
+ // Arcane Shot
+ else if((m_spellInfo->SpellFamilyFlags & 0x00000800) && m_spellInfo->maxLevel > 0)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15);
+ }
+ // Steady Shot
+ else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ {
+ 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.2f);
+ }
+ //Explosive Trap Effect
+ else if(m_spellInfo->SpellFamilyFlags & 0x00000004)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1);
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ //Judgement of Vengeance
+ if((m_spellInfo->SpellFamilyFlags & 0x800000000LL) && m_spellInfo->SpellIconID==2292)
+ {
+ uint32 stacks = 0;
+ Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
+ if((*itr)->GetId() == 31803 && (*itr)->GetCasterGUID()==m_caster->GetGUID())
+ ++stacks;
+ if(!stacks)
+ //No damage if the target isn't affected by this
+ damage = -1;
+ else
+ damage *= stacks;
+ }
+ break;
+ }
+ }
+
+ if(damage >= 0)
+ {
+ uint32 finalDamage;
+ if(m_originalCaster) // m_caster only passive source of cast
+ finalDamage = m_originalCaster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+ else
+ finalDamage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+
+ // post effects
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Bloodthirst
+ if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
+ {
+ uint32 BTAura = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 23881: BTAura = 23885; break;
+ case 23892: BTAura = 23886; break;
+ case 23893: BTAura = 23887; break;
+ case 23894: BTAura = 23888; break;
+ case 25251: BTAura = 25252; break;
+ case 30335: BTAura = 30339; break;
+ default:
+ sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
+ break;
+ }
+
+ if (BTAura)
+ m_caster->CastSpell(m_caster,BTAura,true);
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Shadow Word: Death
+ if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000200000000LL) && unitTarget->isAlive())
+ // deals damage equal to damage done to caster if victim is not killed
+ m_caster->SpellNonMeleeDamageLog( m_caster, m_spellInfo->Id, finalDamage, m_IsTriggeredSpell, false);
+
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Judgement of Blood
+ if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL) && m_spellInfo->SpellIconID==153)
+ {
+ int32 damagePoint = finalDamage * 33 / 100;
+ m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Spell::EffectDummy(uint32 i)
+{
+ if(!unitTarget && !gameObjTarget && !itemTarget)
+ return;
+
+ // selection by spell family
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ // Gnomish Poultryizer trinket
+ switch(m_spellInfo->Id )
+ {
+ case 8063: // Deviate Fish
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,5))
+ {
+ case 1: spell_id = 8064; break; // Sleepy
+ case 2: spell_id = 8065; break; // Invigorate
+ case 3: spell_id = 8066; break; // Shrink
+ case 4: spell_id = 8067; break; // Party Time!
+ case 5: spell_id = 8068; break; // Healthy Spirit
+ }
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 8213: // Savory Deviate Delight
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,2))
+ {
+ // Flip Out - ninja
+ case 1: spell_id = (m_caster->getGender() == GENDER_MALE ? 8219 : 8220); break;
+ // Yaaarrrr - pirate
+ case 2: spell_id = (m_caster->getGender() == GENDER_MALE ? 8221 : 8222); break;
+ }
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 8593: // Symbol of life (restore creature to life)
+ case 31225: // Shimmering Vessel (restore creature to life)
+ {
+ if(!unitTarget || unitTarget->GetTypeId()!=TYPEID_UNIT)
+ return;
+ ((Creature*)unitTarget)->setDeathState(JUST_ALIVED);
+ return;
+ }
+ case 12162: // Deep wounds
+ case 12850: // (now good common check for this spells)
+ case 12868:
+ {
+ if(!unitTarget)
+ return;
+
+ float damage;
+ // DW should benefit of attack power, damage percent mods etc.
+ // TODO: check if using offhand damage is correct and if it should be divided by 2
+ if (m_caster->haveOffhandWeapon() && m_caster->getAttackTimer(BASE_ATTACK) > m_caster->getAttackTimer(OFF_ATTACK))
+ damage = (m_caster->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE))/2;
+ else
+ damage = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE))/2;
+
+ switch (m_spellInfo->Id)
+ {
+ case 12850: damage *= 0.2f; break;
+ case 12162: damage *= 0.4f; break;
+ case 12868: damage *= 0.6f; break;
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id);
+ return;
+ };
+
+ int32 deepWoundsDotBasePoints0 = int32(damage / 4);
+ m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL);
+ return;
+ }
+ case 12975: //Last Stand
+ {
+ int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3);
+ m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL);
+ return;
+ }
+ case 13120: // net-o-matic
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = 0;
+
+ uint32 roll = urand(0, 99);
+
+ if(roll < 2) // 2% for 30 sec self root (off-like chance unknown)
+ spell_id = 16566;
+ else if(roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown)
+ spell_id = 13119;
+ else // normal root
+ spell_id = 13099;
+
+ m_caster->CastSpell(unitTarget,spell_id,true,NULL);
+ return;
+ }
+ case 13567: // Dummy Trigger
+ {
+ // can be used for different aura triggreing, so select by aura
+ if(!m_triggeredByAuraSpell || !unitTarget)
+ return;
+
+ switch(m_triggeredByAuraSpell->Id)
+ {
+ case 26467: // Persistent Shield
+ m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true);
+ break;
+ default:
+ sLog.outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u",m_triggeredByAuraSpell->Id);
+ break;
+ }
+ return;
+ }
+ case 14185: // Preparation Rogue
+ {
+ 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)
+ {
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x26000000860LL))
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ case 15998: // Capture Worg Pup
+ case 29435: // Capture Female Kaliri Hatchling
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+ return;
+ }
+ case 16589: // Noggenfogger Elixir
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,3))
+ {
+ case 1: spell_id = 16595; break;
+ case 2: spell_id = 16593; break;
+ default:spell_id = 16591; break;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 17251: // Spirit Healer Res
+ {
+ if(!unitTarget || !m_originalCaster)
+ return;
+
+ if(m_originalCaster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
+ data << unitTarget->GetGUID();
+ ((Player*)m_originalCaster)->GetSession()->SendPacket( &data );
+ }
+ return;
+ }
+ case 17271: // Test Fetid Skull
+ {
+ if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = roll_chance_i(50) ? 17269 : 17270;
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 20577: // Cannibalize
+ if (unitTarget)
+ m_caster->CastSpell(m_caster,20578,false,NULL);
+ return;
+ case 23019: // Crystal Prison Dummy DND
+ {
+ if(!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet())
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+ if(creatureTarget->isPet())
+ return;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ GameObject* pGameObj = new GameObject;
+
+ Map *map = creatureTarget->GetMap();
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 179644, map,
+ creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(),
+ creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, 1) )
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetRespawnTime(creatureTarget->GetRespawnTime()-time(NULL));
+ pGameObj->SetOwnerGUID(m_caster->GetGUID() );
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy\n");
+ map->Add(pGameObj);
+
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << uint64(pGameObj->GetGUID());
+ m_caster->SendMessageToSet(&data,true);
+
+ return;
+ }
+ case 23074: // Arc. Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,19804,true,m_CastItem);
+ return;
+ case 23075: // Mithril Mechanical Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,12749,true,m_CastItem);
+ return;
+ case 23076: // Mechanical Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,4073,true,m_CastItem);
+ return;
+ case 23133: // Gnomish Battle Chicken
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,13166,true,m_CastItem);
+ return;
+ case 23448: // Ultrasafe Transporter: Gadgetzan - backfires
+ {
+ int32 r = irand(0, 119);
+ if ( r < 20 ) // 1/6 polymorph
+ m_caster->CastSpell(m_caster,23444,true);
+ else if ( r < 100 ) // 4/6 evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ else // 1/6 miss the target
+ m_caster->CastSpell(m_caster,36902,true);
+ return;
+ }
+ case 23453: // Ultrasafe Transporter: Gadgetzan
+ if ( roll_chance_i(50) ) // success
+ m_caster->CastSpell(m_caster,23441,true);
+ else // failure
+ m_caster->CastSpell(m_caster,23446,true);
+ return;
+ case 23645: // Hourglass Sand
+ m_caster->RemoveAurasDueToSpell(23170);
+ return;
+ case 23725: // Gift of Life (warrior bwl trinket)
+ m_caster->CastSpell(m_caster,23782,true);
+ m_caster->CastSpell(m_caster,23783,true);
+ return;
+ case 25860: // Reindeer Transformation
+ {
+ if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
+ return;
+
+ float flyspeed = m_caster->GetSpeedRate(MOVE_FLY);
+ float speed = m_caster->GetSpeedRate(MOVE_RUN);
+
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ //5 different spells used depending on mounted speed and if mount can fly or not
+ if (flyspeed >= 4.1f)
+ m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer
+ else if (flyspeed >= 3.8f)
+ m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer
+ else if (flyspeed >= 1.6f)
+ m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer
+ else if (speed >= 2.0f)
+ m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer
+ else
+ m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
+
+ return;
+ }
+ //case 26074: // Holiday Cheer
+ // return; -- implemented at client side
+ case 28006: // Arcane Cloaking
+ {
+ if( unitTarget->GetTypeId() == TYPEID_PLAYER )
+ m_caster->CastSpell(unitTarget,29294,true);
+ return;
+ }
+ case 28730: // Arcane Torrent (Mana)
+ {
+ int32 count = 0;
+ Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ if ((*i)->GetId() == 28734)
+ ++count;
+ if (count)
+ {
+ m_caster->RemoveAurasDueToSpell(28734);
+ int32 bp = damage * count;
+ m_caster->CastCustomSpell(m_caster, 28733, &bp, NULL, NULL, true);
+ }
+ return;
+ }
+ case 29200: // Purify Helboar Meat
+ {
+ if( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ uint32 spell_id = roll_chance_i(50) ? 29277 : 29278;
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 29858: // Soulshatter
+ if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsHostileTo(m_caster))
+ m_caster->CastSpell(unitTarget,32835,true);
+ return;
+ case 30458: // Nigh Invulnerability
+ if (!m_CastItem) return;
+ if(roll_chance_i(86)) // success
+ m_caster->CastSpell(m_caster, 30456, true, m_CastItem);
+ else // 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
+ m_caster->CastSpell(unitTarget, 30501, true, m_CastItem);
+ else // backfire 20%
+ m_caster->CastSpell(unitTarget, 30504, true, m_CastItem);
+ return;
+ case 33060: // Make a Wish
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+
+ 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;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 35745:
+ {
+ uint32 spell_id;
+ switch(m_caster->GetAreaId())
+ {
+ case 3900: spell_id = 35743; break;
+ case 3742: spell_id = 35744; break;
+ default: return;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true);
+ return;
+ }
+ case 37674: // Chaos Blast
+ if(unitTarget)
+ m_caster->CastSpell(unitTarget,37675,true);
+ return;
+ case 44875: // Complete Raptor Capture
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ //cast spell Raptor Capture Credit
+ m_caster->CastSpell(m_caster,42337,true,NULL);
+ return;
+ }
+ case 45030: // Impale Emissary
+ {
+ // Emissary of Hate Credit
+ m_caster->CastSpell(m_caster,45088,true);
+ return;
+ }
+ case 50243: // Teach Language
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // spell has a 1/3 chance to trigger one of the below
+ if(roll_chance_i(66))
+ return;
+ if(((Player*)m_caster)->GetTeam() == ALLIANCE)
+ {
+ // 1000001 - gnomish binary
+ m_caster->CastSpell(m_caster, 50242, true);
+ }
+ else
+ {
+ // 01001000 - goblin binary
+ m_caster->CastSpell(m_caster, 50246, true);
+ }
+
+ return;
+ }
+ case 51582: //Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite)
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(BattleGround* bg = ((Player*)m_caster)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+
+ m_caster->CastSpell(m_caster, 30452, true, NULL);
+ return;
+ }
+ }
+
+ //All IconID Check in there
+ switch(m_spellInfo->SpellIconID)
+ {
+ // Berserking (troll racial traits)
+ case 1661:
+ {
+ uint32 healthPerc = uint32((float(m_caster->GetHealth())/m_caster->GetMaxHealth())*100);
+ int32 melee_mod = 10;
+ if (healthPerc <= 40)
+ melee_mod = 30;
+ if (healthPerc < 100 && healthPerc > 40)
+ melee_mod = 10+(100-healthPerc)/3;
+
+ int32 hasteModBasePoints0 = melee_mod; // (EffectBasePoints[0]+1)-1+(5-melee_mod) = (melee_mod-1+1)-1+5-melee_mod = 5-1
+ int32 hasteModBasePoints1 = (5-melee_mod);
+ int32 hasteModBasePoints2 = 5;
+
+ // FIXME: custom spell required this aura state by some unknown reason, we not need remove it anyway
+ m_caster->ModifyAuraState(AURA_STATE_BERSERKING,true);
+ m_caster->CastCustomSpell(m_caster,26635,&hasteModBasePoints0,&hasteModBasePoints1,&hasteModBasePoints2,true,NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_MAGE:
+ switch(m_spellInfo->Id )
+ {
+ case 11958: // Cold Snap
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ 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)
+ {
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
+ (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) &&
+ spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 )
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_WARRIOR:
+ // Charge
+ if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual == 867)
+ {
+ int32 chargeBasePoints0 = damage;
+ m_caster->CastCustomSpell(m_caster,34846,&chargeBasePoints0,NULL,NULL,true);
+ return;
+ }
+ // Execute
+ if(m_spellInfo->SpellFamilyFlags & 0x20000000)
+ {
+ if(!unitTarget)
+ return;
+
+ int32 basePoints0 = damage+int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[i]);
+ m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0);
+ m_caster->SetPower(POWER_RAGE,0);
+ return;
+ }
+ if(m_spellInfo->Id==21977) //Warrior's Wrath
+ {
+ if(!unitTarget)
+ return;
+
+ m_caster->CastSpell(unitTarget,21887,true); // spell mod
+ return;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ //Life Tap (only it have this with dummy effect)
+ if (m_spellInfo->SpellFamilyFlags == 0x40000)
+ {
+ float cost = m_currentBasePoints[0]+1;
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost,this);
+
+ int32 dmg = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE);
+
+ if(int32(m_caster->GetHealth()) > dmg)
+ {
+ // Shouldn't Appear in Combat Log
+ m_caster->ModifyHealth(-dmg);
+
+ int32 mana = dmg;
+
+ Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr)
+ {
+ // only Imp. Life Tap have this in combination with dummy aura
+ if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208)
+ mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100;
+ }
+
+ m_caster->CastCustomSpell(m_caster,31818,&mana,NULL,NULL,true,NULL);
+
+ // Mana Feed
+ int32 manaFeedVal = m_caster->CalculateSpellDamage(m_spellInfo,1, m_spellInfo->EffectBasePoints[1],m_caster);
+ manaFeedVal = manaFeedVal * mana / 100;
+ if(manaFeedVal > 0)
+ m_caster->CastCustomSpell(m_caster,32553,&manaFeedVal,NULL,NULL,true,NULL);
+ }
+ else
+ SendCastResult(SPELL_FAILED_FIZZLE);
+ return;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ switch(m_spellInfo->Id )
+ {
+ case 28598: // Touch of Weakness triggered spell
+ {
+ if(!unitTarget || !m_triggeredByAuraSpell)
+ return;
+
+ uint32 spellid = 0;
+ switch(m_triggeredByAuraSpell->Id)
+ {
+ case 2652: spellid = 2943; break; // Rank 1
+ case 19261: spellid = 19249; break; // Rank 2
+ case 19262: spellid = 19251; break; // Rank 3
+ case 19264: spellid = 19252; break; // Rank 4
+ case 19265: spellid = 19253; break; // Rank 5
+ case 19266: spellid = 19254; break; // Rank 6
+ case 25461: spellid = 25460; break; // Rank 7
+ default:
+ sLog.outError("Spell::EffectDummy: Spell 28598 triggered by unhandeled spell %u",m_triggeredByAuraSpell->Id);
+ return;
+ }
+ m_caster->CastSpell(unitTarget, spellid, true, NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ switch(m_spellInfo->Id )
+ {
+ case 5420: // Tree of Life passive
+ {
+ // Tree of Life area effect
+ int32 health_mod = int32(m_caster->GetStat(STAT_SPIRIT)/4);
+ m_caster->CastCustomSpell(m_caster,34123,&health_mod,NULL,NULL,true,NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_ROGUE:
+ switch(m_spellInfo->Id )
+ {
+ case 31231: // Cheat Death
+ {
+ m_caster->CastSpell(m_caster,45182,true);
+ return;
+ }
+ case 5938: // Shiv
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *pCaster = ((Player*)m_caster);
+
+ Item *item = pCaster->GetWeaponForAttack(OFF_ATTACK);
+ if(!item)
+ return;
+
+ // all poison enchantments is temporary
+ uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT);
+ if(!enchant_id)
+ return;
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ for (int s=0;s<3;s++)
+ {
+ if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
+ continue;
+
+ SpellEntry const* combatEntry = sSpellStore.LookupEntry(pEnchant->spellid[s]);
+ if(!combatEntry || combatEntry->Dispel != DISPEL_POISON)
+ continue;
+
+ m_caster->CastSpell(unitTarget, combatEntry, true, item);
+ }
+
+ m_caster->CastSpell(unitTarget, 5940, true);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_HUNTER:
+ // Steady Shot
+ if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ {
+ if( !unitTarget || !unitTarget->isAlive())
+ return;
+
+ bool found = false;
+
+ // check dazed affect
+ Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
+ for(Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter)
+ {
+ if((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->Dispel==0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(found)
+ m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+ return;
+ }
+ // Kill command
+ if(m_spellInfo->SpellFamilyFlags & 0x00080000000000LL)
+ {
+ if(m_caster->getClass()!=CLASS_HUNTER)
+ return;
+
+ // clear hunter crit aura state
+ m_caster->ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE,false);
+
+ // additional damage from pet to pet target
+ Pet* pet = m_caster->GetPet();
+ if(!pet || !pet->getVictim())
+ return;
+
+ uint32 spell_id = 0;
+ switch (m_spellInfo->Id)
+ {
+ case 34026: spell_id = 34027; break; // rank 1
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in KC",m_spellInfo->Id);
+ return;
+ }
+
+ pet->CastSpell(pet->getVictim(), spell_id, true);
+ return;
+ }
+
+ switch(m_spellInfo->Id)
+ {
+ 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)
+ {
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 )
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ case 37506: // Scatter Shot
+ {
+ if (m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ // break Auto Shot and autohit
+ m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_caster->AttackStop();
+ ((Player*)m_caster)->SendAttackSwingCancelAttack();
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_PALADIN:
+ switch(m_spellInfo->SpellIconID)
+ {
+ case 156: // Holy Shock
+ {
+ if(!unitTarget)
+ return;
+
+ int hurt = 0;
+ int heal = 0;
+
+ switch(m_spellInfo->Id)
+ {
+ case 20473: hurt = 25912; heal = 25914; break;
+ case 20929: hurt = 25911; heal = 25913; break;
+ case 20930: hurt = 25902; heal = 25903; break;
+ case 27174: hurt = 27176; heal = 27175; break;
+ case 33072: hurt = 33073; heal = 33074; break;
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in HS",m_spellInfo->Id);
+ return;
+ }
+
+ if(m_caster->IsFriendlyTo(unitTarget))
+ m_caster->CastSpell(unitTarget, heal, true, 0);
+ else
+ m_caster->CastSpell(unitTarget, hurt, true, 0);
+
+ return;
+ }
+ case 561: // Judgement of command
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = m_currentBasePoints[i]+1;
+ SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id);
+ if(!spell_proto)
+ return;
+
+ if( !unitTarget->hasUnitState(UNIT_STAT_STUNDED) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ // decreased damage (/2) for non-stunned target.
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_DAMAGE;
+ mod->value = -50;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = m_spellInfo->Id;
+ mod->effectId = i;
+ mod->lastAffected = NULL;
+ mod->mask = 0x0000020000000000LL;
+ mod->charges = 0;
+
+ ((Player*)m_caster)->AddSpellMod(mod, true);
+ m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
+ // mod deleted
+ ((Player*)m_caster)->AddSpellMod(mod, false);
+ }
+ else
+ m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
+
+ return;
+ }
+ }
+
+ switch(m_spellInfo->Id)
+ {
+ case 31789: // Righteous Defense (step 1)
+ {
+ // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target)
+
+ // non-standard cast requirement check
+ if (!unitTarget || unitTarget->getAttackers().empty())
+ {
+ // clear cooldown at fail
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(m_spellInfo->Id);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+
+ SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT);
+ return;
+ }
+
+ // Righteous Defense (step 2) (in old version 31980 dummy effect)
+ // Clear targets for eff 1
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ ihit->effectMask &= ~(1<<1);
+
+ // not empty (checked)
+ Unit::AttackerSet const& attackers = unitTarget->getAttackers();
+
+ // chance to be selected from list
+ float chance = 100.0f/attackers.size();
+ uint32 count=0;
+ for(Unit::AttackerSet::const_iterator aItr = attackers.begin(); aItr != attackers.end() && count < 3; ++aItr)
+ {
+ if(!roll_chance_f(chance))
+ continue;
+ ++count;
+ AddUnitTarget((*aItr), 1);
+ }
+
+ // now let next effect cast spell at each target.
+ return;
+ }
+ case 37877: // Blessing of Faith
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = 0;
+ switch(unitTarget->getClass())
+ {
+ case CLASS_DRUID: spell_id = 37878; break;
+ case CLASS_PALADIN: spell_id = 37879; break;
+ case CLASS_PRIEST: spell_id = 37880; break;
+ case CLASS_SHAMAN: spell_id = 37881; break;
+ default: return; // ignore for not healing classes
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_SHAMAN:
+ //Shaman Rockbiter Weapon
+ if (m_spellInfo->SpellFamilyFlags == 0x400000)
+ {
+ uint32 spell_id = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 8017: spell_id = 36494; break; // Rank 1
+ case 8018: spell_id = 36750; break; // Rank 2
+ case 8019: spell_id = 36755; break; // Rank 3
+ case 10399: spell_id = 36759; break; // Rank 4
+ case 16314: spell_id = 36763; break; // Rank 5
+ case 16315: spell_id = 36766; break; // Rank 6
+ case 16316: spell_id = 36771; break; // Rank 7
+ case 25479: spell_id = 36775; break; // Rank 8
+ case 25485: spell_id = 36499; break; // Rank 9
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in RW",m_spellInfo->Id);
+ return;
+ }
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", spell_id);
+ return;
+ }
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for(int i = BASE_ATTACK; i <= OFF_ATTACK; ++i)
+ {
+ if(Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(i)))
+ {
+ if(item->IsFitToSpellRequirements(m_spellInfo))
+ {
+ Spell *spell = new Spell(m_caster, spellInfo, true);
+
+ // enchanting spell selected by calculated damage-per-sec in enchanting effect
+ // at calculation applied affect from Elemental Weapons talent
+ // real enchantment damage-1
+ spell->m_currentBasePoints[1] = damage-1;
+
+ SpellCastTargets targets;
+ targets.setItemTarget( item );
+ spell->prepare(&targets);
+ }
+ }
+ }
+ return;
+ }
+
+ if(m_spellInfo->Id == 39610) // Mana-Tide Totem effect
+ {
+ if(!unitTarget || unitTarget->getPowerType() != POWER_MANA)
+ return;
+
+ // Regenerate 6% of Total Mana Every 3 secs
+ int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100;
+ m_caster->CastCustomSpell(unitTarget,39609,&EffectBasePoints0,NULL,NULL,true,NULL,NULL,m_originalCasterGUID);
+ return;
+ }
+
+ break;
+ }
+
+ // pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id))
+ {
+ m_caster->AddPetAura(petSpell);
+ return;
+ }
+}
+
+void Spell::EffectTriggerSpellWithValue(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ int32 bp = damage;
+ m_caster->CastCustomSpell(unitTarget,triggered_spell_id,&bp,&bp,&bp,true,NULL,NULL,m_originalCasterGUID);
+}
+
+void Spell::EffectTriggerRitualOfSummoning(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ finish();
+ Spell *spell = new Spell(m_caster, spellInfo, true);
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( unitTarget);
+ spell->prepare(&targets);
+
+ m_caster->SetCurrentCastedSpell(spell);
+ spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]);
+
+}
+
+void Spell::EffectForceCast(uint32 i)
+{
+ if( !unitTarget )
+ return;
+
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ unitTarget->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID);
+}
+
+void Spell::EffectTriggerSpell(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // special cases
+ switch(triggered_spell_id)
+ {
+ // Vanish
+ case 18461:
+ {
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED);
+
+ // if this spell is given to NPC it must handle rest by it's own AI
+ if ( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ // get highest rank of the Stealth spell
+ uint32 spellId = 0;
+ const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ // only highest rank is shown in spell book, so simply check if shown in spell book
+ if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (!spellInfo)
+ continue;
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_STEALTH)
+ {
+ spellId = spellInfo->Id;
+ break;
+ }
+ }
+
+ // no Stealth spell found
+ if (!spellId)
+ return;
+
+ // reset cooldown on it if needed
+ if(((Player*)m_caster)->HasSpellCooldown(spellId))
+ ((Player*)m_caster)->RemoveSpellCooldown(spellId);
+
+ m_caster->CastSpell(m_caster, spellId, true);
+ return;
+ }
+ // just skip
+ case 23770: // Sayge's Dark Fortune of *
+ // not exist, common cooldown can be implemented in scripts if need.
+ return;
+ // Brittle Armor - (need add max stack of 24575 Brittle Armor)
+ case 29284:
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(24575);
+ if (!spell)
+ return;
+
+ for (int i=0; i < spell->StackAmount; ++i)
+ m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
+ return;
+ }
+ // Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
+ case 29286:
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(26464);
+ if (!spell)
+ return;
+
+ for (int i=0; i < spell->StackAmount; ++i)
+ m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
+ return;
+ }
+ // Righteous Defense
+ case 31980:
+ {
+ m_caster->CastSpell(unitTarget, 31790, true,m_CastItem,NULL,m_originalCasterGUID);
+ return;
+ }
+ // Cloak of Shadows
+ case 35729 :
+ {
+ Unit::AuraMap& Auras = m_caster->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter)
+ {
+ // remove all harmful spells on you...
+ if( // ignore positive and passive auras
+ !iter->second->IsPositive() && !iter->second->IsPassive() &&
+ // ignore physical auras
+ (GetSpellSchoolMask(iter->second->GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)==0 &&
+ // ignore immunity persistent spells
+ !( iter->second->GetSpellProto()->AttributesEx & 0x10000 ) )
+ {
+ m_caster->RemoveAurasDueToSpell(iter->second->GetSpellProto()->Id);
+ iter = Auras.begin();
+ }
+ }
+ return;
+ }
+ // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet
+ case 41967:
+ {
+ if (Unit *pet = m_caster->GetPet())
+ pet->CastSpell(pet, 28305, true);
+ return;
+ }
+ }
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ // some triggered spells require specific equipment
+ if(spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ // main hand weapon required
+ if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND)
+ {
+ Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK);
+
+ // skip spell if no weapon in slot or broken
+ if(!item || item->IsBroken() )
+ return;
+
+ // skip spell if weapon not fit to triggered spell
+ if(!item->IsFitToSpellRequirements(spellInfo))
+ return;
+ }
+
+ // offhand hand weapon required
+ if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
+ {
+ Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK);
+
+ // skip spell if no weapon in slot or broken
+ if(!item || item->IsBroken() )
+ return;
+
+ // skip spell if weapon not fit to triggered spell
+ if(!item->IsFitToSpellRequirements(spellInfo))
+ return;
+ }
+ }
+
+ // some triggered spells must be casted instantly (for example, if next effect case instant kill caster)
+ bool instant = false;
+ for(uint32 j = i+1; j < 3; ++j)
+ {
+ if(m_spellInfo->Effect[j]==SPELL_EFFECT_INSTAKILL && m_spellInfo->EffectImplicitTargetA[j]==TARGET_SELF)
+ {
+ instant = true;
+ break;
+ }
+ }
+
+ if(instant)
+ {
+ if (unitTarget)
+ m_caster->CastSpell(unitTarget,spellInfo,true,m_CastItem,NULL,m_originalCasterGUID);
+ }
+ else
+ m_TriggerSpells.push_back(spellInfo);
+}
+
+void Spell::EffectTriggerMissileSpell(uint32 effect_idx)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effect_idx];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerMissileSpell of spell %u: triggering unknown spell id %effect_idx", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ if (m_CastItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ Spell *spell = new Spell(m_caster, spellInfo, true, m_originalCasterGUID );
+
+ SpellCastTargets targets;
+ targets.setDestination(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ);
+ spell->m_CastItem = m_CastItem;
+ spell->prepare(&targets, NULL);
+}
+
+void Spell::EffectTeleportUnits(uint32 i)
+{
+ if(!unitTarget || unitTarget->isInFlight())
+ return;
+
+ switch (m_spellInfo->EffectImplicitTargetB[i])
+ {
+ case TARGET_INNKEEPER_COORDINATES:
+ {
+ // Only players can teleport to innkeeper
+ if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->TeleportTo(((Player*)unitTarget)->m_homebindMapId,((Player*)unitTarget)->m_homebindX,((Player*)unitTarget)->m_homebindY,((Player*)unitTarget)->m_homebindZ,unitTarget->GetOrientation(),unitTarget==m_caster ? TELE_TO_SPELL : 0);
+ return;
+ }
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ {
+ // TODO: Only players can teleport?
+ if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
+ if(!st)
+ {
+ sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u\n", m_spellInfo->Id );
+ return;
+ }
+ ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0);
+ break;
+ }
+ case TARGET_BEHIND_VICTIM:
+ {
+ // Get selected target for player (or victim for units)
+ Unit *pTarget = NULL;
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+ else
+ pTarget = m_caster->getVictim();
+ // No target present - return
+ if (!pTarget)
+ return;
+ // Init dest coordinates
+ uint32 mapid = m_caster->GetMapId();
+ float x = m_targets.m_destX;
+ float y = m_targets.m_destY;
+ float z = m_targets.m_destZ;
+ float orientation = pTarget->GetOrientation();
+ // Teleport
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ {
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
+ WorldPacket data;
+ unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
+ unitTarget->SendMessageToSet(&data, false);
+ }
+ return;
+ }
+ default:
+ {
+ // If not exist data for dest location - return
+ if(!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION))
+ {
+ sLog.outError( "Spell::EffectTeleportUnits - unknown EffectImplicitTargetB[%u] = %u for spell ID %u\n", i, m_spellInfo->EffectImplicitTargetB[i], m_spellInfo->Id );
+ return;
+ }
+ // Init dest coordinates
+ uint32 mapid = m_caster->GetMapId();
+ float x = m_targets.m_destX;
+ float y = m_targets.m_destY;
+ float z = m_targets.m_destZ;
+ float orientation = unitTarget->GetOrientation();
+ // Teleport
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ {
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
+ WorldPacket data;
+ unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
+ unitTarget->SendMessageToSet(&data, false);
+ }
+ return;
+ }
+ }
+
+ // post effects for TARGET_TABLE_X_Y_Z_COORDINATES
+ switch ( m_spellInfo->Id )
+ {
+ // Dimensional Ripper - Everlook
+ case 23442:
+ {
+ int32 r = irand(0, 119);
+ if ( r >= 70 ) // 7/12 success
+ {
+ if ( r < 100 ) // 4/12 evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ else // 1/12 fire
+ m_caster->CastSpell(m_caster,23449,true);
+ }
+ return;
+ }
+ // Ultrasafe Transporter: Toshley's Station
+ case 36941:
+ {
+ if ( roll_chance_i(50) ) // 50% success
+ {
+ int32 rand_eff = urand(1,7);
+ switch ( rand_eff )
+ {
+ case 1:
+ // soul split - evil
+ m_caster->CastSpell(m_caster,36900,true);
+ break;
+ case 2:
+ // soul split - good
+ m_caster->CastSpell(m_caster,36901,true);
+ break;
+ case 3:
+ // Increase the size
+ m_caster->CastSpell(m_caster,36895,true);
+ break;
+ case 4:
+ // Decrease the size
+ m_caster->CastSpell(m_caster,36893,true);
+ break;
+ case 5:
+ // Transform
+ {
+ if (((Player*)m_caster)->GetTeam() == ALLIANCE )
+ m_caster->CastSpell(m_caster,36897,true);
+ else
+ m_caster->CastSpell(m_caster,36899,true);
+ break;
+ }
+ case 6:
+ // chicken
+ m_caster->CastSpell(m_caster,36940,true);
+ break;
+ case 7:
+ // evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ break;
+ }
+ }
+ return;
+ }
+ // Dimensional Ripper - Area 52
+ case 36890:
+ {
+ if ( roll_chance_i(50) ) // 50% success
+ {
+ int32 rand_eff = urand(1,4);
+ switch ( rand_eff )
+ {
+ case 1:
+ // soul split - evil
+ m_caster->CastSpell(m_caster,36900,true);
+ break;
+ case 2:
+ // soul split - good
+ m_caster->CastSpell(m_caster,36901,true);
+ break;
+ case 3:
+ // Increase the size
+ m_caster->CastSpell(m_caster,36895,true);
+ break;
+ case 4:
+ // Transform
+ {
+ if (((Player*)m_caster)->GetTeam() == ALLIANCE )
+ m_caster->CastSpell(m_caster,36897,true);
+ else
+ m_caster->CastSpell(m_caster,36899,true);
+ break;
+ }
+ }
+ }
+ return;
+ }
+ }
+}
+
+void Spell::EffectApplyAura(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ SpellImmuneList const& list = unitTarget->m_spellImmune[IMMUNITY_STATE];
+ for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
+ if(itr->type == m_spellInfo->EffectApplyAuraName[i])
+ return;
+
+ // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
+ if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 &&
+ (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) )
+ return;
+
+ Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+ if(!caster)
+ return;
+
+ sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]);
+
+ Aura* Aur = CreateAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, caster, m_CastItem);
+
+ // Now Reduce spell duration using data received at spell hit
+ int32 duration = Aur->GetAuraMaxDuration();
+ unitTarget->ApplyDiminishingToDuration(m_diminishGroup,duration,m_caster,m_diminishLevel);
+ Aur->setDiminishGroup(m_diminishGroup);
+
+ // if Aura removed and deleted, do not continue.
+ if(duration== 0 && !(Aur->IsPermanent()))
+ {
+ delete Aur;
+ return;
+ }
+
+ if(duration != Aur->GetAuraMaxDuration())
+ {
+ Aur->SetAuraMaxDuration(duration);
+ Aur->SetAuraDuration(duration);
+ }
+
+ bool added = unitTarget->AddAura(Aur);
+
+ // Aura not added and deleted in AddAura call;
+ if (!added)
+ return;
+
+ // found crash at character loading, broken pointer to Aur...
+ // Aur was deleted in AddAura()...
+ if(!Aur)
+ return;
+
+ // TODO Make a way so it works for every related spell!
+ if(unitTarget->GetTypeId()==TYPEID_PLAYER) // Negative buff should only be applied on players
+ {
+ uint32 spellId = 0;
+ if(m_spellInfo->CasterAuraStateNot==AURA_STATE_WEAKENED_SOUL || m_spellInfo->TargetAuraStateNot==AURA_STATE_WEAKENED_SOUL)
+ spellId = 6788; // Weakened Soul
+ else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_FORBEARANCE || m_spellInfo->TargetAuraStateNot==AURA_STATE_FORBEARANCE)
+ spellId = 25771; // Forbearance
+ else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_HYPOTHERMIA)
+ spellId = 41425; // Hypothermia
+ else if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
+ spellId = 11196; // Recently Bandaged
+ else if( (m_spellInfo->AttributesEx & 0x20) && (m_spellInfo->AttributesEx2 & 0x20000) )
+ spellId = 23230; // Blood Fury - Healing Reduction
+
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId);
+ if (AdditionalSpellInfo)
+ {
+ // applied at target by target
+ Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, &m_currentBasePoints[0], unitTarget,unitTarget, 0);
+ unitTarget->AddAura(AdditionalAura);
+ sLog.outDebug("Spell: Additional Aura is: %u", AdditionalSpellInfo->EffectApplyAuraName[0]);
+ }
+ }
+
+ // Prayer of Mending (jump animation), we need formal caster instead original for correct animation
+ if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & 0x00002000000000LL))
+ m_caster->CastSpell(unitTarget,41637,true,NULL,Aur);
+}
+
+void Spell::EffectUnlearnSpecialization( uint32 i )
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)unitTarget;
+ uint32 spellToUnlearn = m_spellInfo->EffectTriggerSpell[i];
+
+ _player->removeSpell(spellToUnlearn);
+
+ sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() );
+}
+
+void Spell::EffectPowerDrain(uint32 i)
+{
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers drain_power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+ if(unitTarget->getPowerType() != drain_power)
+ return;
+ if(damage < 0)
+ return;
+
+ uint32 curPower = unitTarget->GetPower(drain_power);
+
+ //add spell damage bonus
+ damage=m_caster->SpellDamageBonus(unitTarget,m_spellInfo,uint32(damage),SPELL_DIRECT_DAMAGE);
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ uint32 power = damage;
+ if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
+ power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
+
+ int32 new_damage;
+ if(curPower < power)
+ new_damage = curPower;
+ else
+ new_damage = power;
+
+ unitTarget->ModifyPower(drain_power,-new_damage);
+
+ if(drain_power == POWER_MANA)
+ {
+ float manaMultiplier = m_spellInfo->EffectMultipleValue[i];
+ if(manaMultiplier==0)
+ manaMultiplier = 1;
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
+
+ int32 gain = int32(new_damage*manaMultiplier);
+
+ m_caster->ModifyPower(POWER_MANA,gain);
+ //send log
+ m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id,gain,POWER_MANA,false);
+ }
+}
+
+void Spell::EffectSendEvent(uint32 EffectIndex)
+{
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->InBattleGround())
+ {
+ BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+ if(bg && bg->GetStatus() == STATUS_IN_PROGRESS)
+ {
+ switch(m_spellInfo->Id)
+ {
+ case 23333: // Pickup Horde Flag
+ /*do not uncomment .
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Send Event Horde Flag Picked Up");
+ break;
+ /* not used :
+ case 23334: // Drop Horde Flag
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+ sLog.outDebug("Drop Horde Flag");
+ break;
+ */
+ case 23335: // Pickup Alliance Flag
+ /*do not uncomment ... (it will cause crash, because of null targetobject!) anyway this is a bad way to call that event, because it would cause recursion
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Send Event Alliance Flag Picked Up");
+ break;
+ /* not used :
+ case 23336: // Drop Alliance Flag
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+ sLog.outDebug("Drop Alliance Flag");
+ break;
+ case 23385: // Alliance Flag Returns
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Alliance Flag Returned");
+ break;
+ case 23386: // Horde Flag Returns
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Horde Flag Returned");
+ break;*/
+ case 34976:
+ /*
+ if(bg->GetTypeID()==BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ */
+ break;
+ default:
+ sLog.outDebug("Unknown spellid %u in BG event", m_spellInfo->Id);
+ break;
+ }
+ }
+ }
+ 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);
+}
+
+void Spell::EffectPowerBurn(uint32 i)
+{
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers powertype = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+ if(unitTarget->getPowerType()!=powertype)
+ return;
+ if(damage < 0)
+ return;
+
+ int32 curPower = int32(unitTarget->GetPower(powertype));
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ uint32 power = damage;
+ if ( powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
+ power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
+
+ int32 new_damage = (curPower < power) ? curPower : power;
+
+ unitTarget->ModifyPower(powertype,-new_damage);
+ float multiplier = m_spellInfo->EffectMultipleValue[i];
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ new_damage = int32(new_damage*multiplier);
+ m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
+}
+
+void Spell::EffectHeal( uint32 /*i*/ )
+{
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ int32 addhealth = damage;
+
+ // Vessel of the Naaru (Vial of the Sunwell trinket)
+ if (m_spellInfo->Id == 45064)
+ {
+ // Amount of heal - depends from stacked Holy Energy
+ int damageAmount = 0;
+ Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ if((*i)->GetId() == 45062)
+ damageAmount+=(*i)->GetModifier()->m_amount;
+ if (damageAmount)
+ m_caster->RemoveAurasDueToSpell(45062);
+
+ addhealth += damageAmount;
+ }
+ // Swiftmend - consumes Regrowth or Rejuvenation
+ else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND))
+ {
+ Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL);
+ // find most short by duration
+ Aura *targetAura = NULL;
+ for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i)
+ {
+ if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID
+ && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) )
+ {
+ if(!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration())
+ targetAura = *i;
+ }
+ }
+
+ if(!targetAura)
+ {
+ sLog.outError("Target(GUID:" I64FMTD ") has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID());
+ return;
+ }
+ int idx = 0;
+ while(idx < 3)
+ {
+ if(targetAura->GetSpellProto()->EffectApplyAuraName[idx] == SPELL_AURA_PERIODIC_HEAL)
+ break;
+ idx++;
+ }
+
+ int32 tickheal = caster->SpellHealingBonus(targetAura->GetSpellProto(), targetAura->GetModifier()->m_amount, DOT, unitTarget);
+ int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / targetAura->GetSpellProto()->EffectAmplitude[idx];
+ unitTarget->RemoveAurasDueToSpell(targetAura->GetId());
+
+ addhealth += tickheal * tickcount;
+ }
+ else
+ addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget);
+
+ bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, m_attackType);
+ if (crit)
+ addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, unitTarget);
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit);
+
+ int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
+ bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
+
+ // ignore item heals
+ if(m_CastItem)
+ return;
+
+ uint32 procHealer = PROC_FLAG_HEAL;
+ if (crit)
+ procHealer |= PROC_FLAG_CRIT_HEAL;
+
+ m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,SPELL_SCHOOL_MASK_NONE,m_spellInfo,m_IsTriggeredSpell);
+ }
+}
+
+void Spell::EffectHealPct( uint32 /*i*/ )
+{
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100;
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
+
+ int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
+ bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
+ }
+}
+
+void Spell::EffectHealMechanical( uint32 /*i*/ )
+{
+ // Mechanic creature type should be correctly checked by targetCreatureType field
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ uint32 addhealth = caster->SpellHealingBonus(m_spellInfo, uint32(damage), HEAL, unitTarget);
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
+ unitTarget->ModifyHealth( int32(damage) );
+ }
+}
+
+void Spell::EffectHealthLeech(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(damage < 0)
+ return;
+
+ sLog.outDebug("HealthLeech :%i", damage);
+
+ float multiplier = m_spellInfo->EffectMultipleValue[i];
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ int32 new_damage = int32(damage*multiplier);
+ uint32 curHealth = unitTarget->GetHealth();
+ new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
+ if(curHealth < new_damage)
+ new_damage = curHealth;
+
+ if(m_caster->isAlive())
+ {
+ new_damage = m_caster->SpellHealingBonus(m_spellInfo, new_damage, HEAL, m_caster);
+
+ m_caster->ModifyHealth(new_damage);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(new_damage));
+ }
+}
+
+void Spell::DoCreateItem(uint32 i, uint32 itemtype)
+{
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)unitTarget;
+
+ uint32 newitemid = itemtype;
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( newitemid );
+ if(!pProto)
+ {
+ player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ uint32 num_to_add;
+
+ // TODO: maybe all this can be replaced by using correct calculated `damage` value
+ if(pProto->Class != ITEM_CLASS_CONSUMABLE || m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE)
+ {
+ int32 basePoints = m_currentBasePoints[i];
+ int32 randomPoints = m_spellInfo->EffectDieSides[i];
+ if (randomPoints)
+ num_to_add = basePoints + irand(1, randomPoints);
+ else
+ num_to_add = basePoints + 1;
+ }
+ else if (pProto->MaxCount == 1)
+ num_to_add = 1;
+ else if(player->getLevel() >= m_spellInfo->spellLevel)
+ {
+ int32 basePoints = m_currentBasePoints[i];
+ float pointPerLevel = m_spellInfo->EffectRealPointsPerLevel[i];
+ num_to_add = basePoints + 1 + uint32((player->getLevel() - m_spellInfo->spellLevel)*pointPerLevel);
+ }
+ else
+ num_to_add = 2;
+
+ if (num_to_add < 1)
+ num_to_add = 1;
+ if (num_to_add > pProto->Stackable)
+ num_to_add = pProto->Stackable;
+
+ // init items_count to 1, since 1 item will be created regardless of specialization
+ int items_count=1;
+ // the chance to create additional items
+ float additionalCreateChance=0.0f;
+ // the maximum number of created additional items
+ uint8 additionalMaxNum=0;
+ // get the chance and maximum number for creating extra items
+ if ( canCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum) )
+ {
+ // roll with this chance till we roll not to create or we create the max num
+ while ( roll_chance_f(additionalCreateChance) && items_count<=additionalMaxNum )
+ ++items_count;
+ }
+
+ // really will be created more items
+ num_to_add *= items_count;
+
+ // can the player store the new item?
+ ItemPosCountVec dest;
+ uint32 no_space = 0;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space );
+ if( msg != EQUIP_ERR_OK )
+ {
+ // convert to possible store amount
+ if( msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ num_to_add -= no_space;
+ else
+ {
+ // if not created by another reason from full inventory or unique items amount limitation
+ player->SendEquipError( msg, NULL, NULL );
+ return;
+ }
+ }
+
+ if(num_to_add)
+ {
+ // create the new item and store it
+ Item* pItem = player->StoreNewItem( dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid));
+
+ // was it successful? return error if not
+ if(!pItem)
+ {
+ player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ // set the "Crafted by ..." property of the item
+ if( pItem->GetProto()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetProto()->Class != ITEM_CLASS_QUEST)
+ pItem->SetUInt32Value(ITEM_FIELD_CREATOR,player->GetGUIDLow());
+
+ // send info to the client
+ if(pItem)
+ player->SendNewItem(pItem, num_to_add, true, true);
+
+ // we succeeded in creating at least one item, so a levelup is possible
+ player->UpdateCraftSkill(m_spellInfo->Id);
+ }
+
+ // for battleground marks send by mail if not add all expected
+ if(no_space > 0 )
+ {
+ BattleGroundTypeId bgType;
+ switch(m_spellInfo->Id)
+ {
+ case SPELL_AV_MARK_WINNER:
+ case SPELL_AV_MARK_LOSER:
+ bgType = BATTLEGROUND_AV;
+ break;
+ case SPELL_WS_MARK_WINNER:
+ case SPELL_WS_MARK_LOSER:
+ bgType = BATTLEGROUND_WS;
+ break;
+ case SPELL_AB_MARK_WINNER:
+ case SPELL_AB_MARK_LOSER:
+ bgType = BATTLEGROUND_AB;
+ break;
+ default:
+ return;
+ }
+
+ if(BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgType))
+ bg->SendRewardMarkByMail(player,newitemid,no_space);
+ }
+}
+
+void Spell::EffectCreateItem(uint32 i)
+{
+ DoCreateItem(i,m_spellInfo->EffectItemType[i]);
+}
+
+void Spell::EffectPersistentAA(uint32 i)
+{
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius);
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+ DynamicObject* dynObj = new DynamicObject;
+ if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
+ {
+ delete dynObj;
+ return;
+ }
+ dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
+ dynObj->SetUInt32Value(GAMEOBJECT_DISPLAYID, 368003);
+ dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x01eeeeee);
+ m_caster->AddDynObject(dynObj);
+ MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
+}
+
+void Spell::EffectEnergize(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ // Some level depends spells
+ int multipler = 0;
+ int level_diff = 0;
+ switch (m_spellInfo->Id)
+ {
+ // Restore Energy
+ case 9512:
+ level_diff = m_caster->getLevel() - 40;
+ multipler = 2;
+ break;
+ // Blood Fury
+ case 24571:
+ level_diff = m_caster->getLevel() - 60;
+ multipler = 10;
+ break;
+ // Burst of Energy
+ case 24532:
+ level_diff = m_caster->getLevel() - 60;
+ multipler = 4;
+ break;
+ default:
+ break;
+ }
+
+ if (level_diff > 0)
+ damage -= multipler * level_diff;
+
+ if(damage < 0)
+ return;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(unitTarget->GetMaxPower(power) == 0)
+ return;
+
+ unitTarget->ModifyPower(power,damage);
+ m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
+
+ // Mad Alchemist's Potion
+ if (m_spellInfo->Id == 45051)
+ {
+ // find elixirs on target
+ uint32 elixir_mask = 0;
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
+ {
+ uint32 spell_id = itr->second->GetId();
+ if(uint32 mask = spellmgr.GetSpellElixirMask(spell_id))
+ elixir_mask |= mask;
+ }
+
+ // get available elixir mask any not active type from battle/guardian (and flask if no any)
+ elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK;
+
+ // get all available elixirs by mask and spell level
+ std::vector<uint32> elixirs;
+ SpellElixirMap const& m_spellElixirs = spellmgr.GetSpellElixirMap();
+ for(SpellElixirMap::const_iterator itr = m_spellElixirs.begin(); itr != m_spellElixirs.end(); ++itr)
+ {
+ if (itr->second & elixir_mask)
+ {
+ if (itr->second & (ELIXIR_UNSTABLE_MASK | ELIXIR_SHATTRATH_MASK))
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (spellInfo && (spellInfo->spellLevel < m_spellInfo->spellLevel || spellInfo->spellLevel > unitTarget->getLevel()))
+ continue;
+
+ elixirs.push_back(itr->first);
+ }
+ }
+
+ if (!elixirs.empty())
+ {
+ // cast random elixir on target
+ uint32 rand_spell = urand(0,elixirs.size()-1);
+ m_caster->CastSpell(unitTarget,elixirs[rand_spell],true,m_CastItem);
+ }
+ }
+}
+
+void Spell::EffectEnergisePct(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ uint32 maxPower = unitTarget->GetMaxPower(power);
+ if(maxPower == 0)
+ return;
+
+ uint32 gain = damage * maxPower / 100;
+ unitTarget->ModifyPower(power, gain);
+ m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
+}
+
+void Spell::SendLoot(uint64 guid, LootType loottype)
+{
+ Player* player = (Player*)m_caster;
+ if (!player)
+ return;
+
+ if (gameObjTarget)
+ {
+ switch (gameObjTarget->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ gameObjTarget->UseDoorOrButton();
+ sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget);
+ return;
+
+ case GAMEOBJECT_TYPE_QUESTGIVER:
+ // start or end quest
+ player->PrepareQuestMenu(guid);
+ player->SendPreparedQuest(guid);
+ return;
+
+ case GAMEOBJECT_TYPE_SPELL_FOCUS:
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+ return;
+
+ case GAMEOBJECT_TYPE_GOOBER:
+ // goober_scripts can be triggered if the player don't have the quest
+ 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);
+ }
+
+ // cast goober spell
+ if (gameObjTarget->GetGOInfo()->goober.questId)
+ ///Quest require to be active for GO using
+ if(player->GetQuestStatus(gameObjTarget->GetGOInfo()->goober.questId) != QUEST_STATUS_INCOMPLETE)
+ return;
+
+ gameObjTarget->AddUniqueUse(player);
+ gameObjTarget->SetLootState(GO_JUST_DEACTIVATED);
+
+ //TODO? Objective counting called without spell check but with quest objective check
+ // if send spell id then this line will duplicate to spell casting call (double counting)
+ // So we or have this line and not required in quest_template have reqSpellIdN
+ // or must remove this line and required in DB have data in quest_template have reqSpellIdN for all quest using cases.
+ player->CastedCreatureOrGO(gameObjTarget->GetEntry(), gameObjTarget->GetGUID(), 0);
+
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->goober.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+
+ return;
+
+ case GAMEOBJECT_TYPE_CHEST:
+ // TODO: possible must be moved to loot release (in different from linked triggering)
+ 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);
+ }
+
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+
+ // Don't return, let loots been taken
+ }
+ }
+
+ // Send loot
+ player->SendLoot(guid, loottype);
+}
+
+void Spell::EffectOpenLock(uint32 /*i*/)
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ {
+ sLog.outDebug( "WORLD: Open Lock - No Player Caster!");
+ return;
+ }
+
+ Player* player = (Player*)m_caster;
+
+ LootType loottype = LOOT_CORPSE;
+ uint32 lockId = 0;
+ uint64 guid = 0;
+
+ // Get lockId
+ if(gameObjTarget)
+ {
+ GameObjectInfo const* goInfo = gameObjTarget->GetGOInfo();
+ // Arathi Basin banner opening !
+ if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune ||
+ goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK )
+ {
+ if(BattleGround *bg = player->GetBattleGround())// in battleground
+ {
+ if( !player->IsMounted() && // not mounted
+ !player->HasStealthAura() && // not stealthed
+ !player->HasInvisibilityAura() && // not invisible
+ player->isAlive() ) // live player
+ {
+ // check if it's correct bg
+ if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+
+ return;
+ }
+ }
+ }
+ else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
+ {
+ if(BattleGround *bg = player->GetBattleGround())
+ if(bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+ return;
+ }
+ lockId = gameObjTarget->GetLockId();
+ guid = gameObjTarget->GetGUID();
+ }
+ else if(itemTarget)
+ {
+ lockId = itemTarget->GetProto()->LockID;
+ guid = itemTarget->GetGUID();
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: Open Lock - No GameObject/Item Target!");
+ return;
+ }
+
+ if(!lockId) // possible case for GO and maybe for items.
+ {
+ SendLoot(guid, loottype);
+ return;
+ }
+
+ // Get LockInfo
+ LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+
+ if (!lockInfo)
+ {
+ sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!",
+ (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId);
+ SendCastResult(SPELL_FAILED_BAD_TARGETS);
+ return;
+ }
+
+ // check key
+ for(int i = 0; i < 5; ++i)
+ {
+ // type==1 This means lockInfo->key[i] is an item
+ if(lockInfo->keytype[i]==LOCK_KEY_ITEM && lockInfo->key[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[i])
+ {
+ SendLoot(guid, loottype);
+ return;
+ }
+ }
+
+ uint32 SkillId = 0;
+ // Check and skill-up skill
+ if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL )
+ SkillId = m_spellInfo->EffectMiscValue[1];
+ // pickpocketing spells
+ else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK )
+ SkillId = SKILL_LOCKPICKING;
+
+ // skill bonus provided by casting spell (mostly item spells)
+ uint32 spellSkillBonus = uint32(m_currentBasePoints[0]+1);
+
+ uint32 reqSkillValue = lockInfo->requiredminingskill;
+
+ if(lockInfo->requiredlockskill) // required pick lock skill applying
+ {
+ if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?)
+ {
+ SendCastResult(SPELL_FAILED_FIZZLE);
+ return;
+ }
+
+ reqSkillValue = lockInfo->requiredlockskill;
+ }
+ else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target
+ {
+ SendCastResult(SPELL_FAILED_BAD_TARGETS);
+ return;
+ }
+
+ if ( SkillId )
+ {
+ loottype = LOOT_SKINNING;
+ if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue )
+ {
+ SendCastResult(SPELL_FAILED_LOW_CASTLEVEL);
+ return;
+ }
+
+ // update skill if really known
+ uint32 SkillValue = player->GetPureSkillValue(SkillId);
+ if(SkillValue) // non only item base skill
+ {
+ if(gameObjTarget)
+ {
+ // Allow one skill-up until respawned
+ if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) &&
+ player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) )
+ gameObjTarget->AddToSkillupList( player->GetGUIDLow() );
+ }
+ else if(itemTarget)
+ {
+ // Do one skill-up
+ uint32 SkillValue = player->GetPureSkillValue(SkillId);
+ player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue);
+ }
+ }
+ }
+
+ SendLoot(guid, loottype);
+}
+
+void Spell::EffectSummonChangeItem(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *player = (Player*)m_caster;
+
+ // applied only to using item
+ if(!m_CastItem)
+ return;
+
+ // ... only to item in own inventory/bank/equip_slot
+ if(m_CastItem->GetOwnerGUID()!=player->GetGUID())
+ return;
+
+ uint32 newitemid = m_spellInfo->EffectItemType[i];
+ if(!newitemid)
+ return;
+
+ uint16 pos = m_CastItem->GetPos();
+
+ Item *pNewItem = Item::CreateItem( newitemid, 1, player);
+ if( !pNewItem )
+ return;
+
+ for(uint8 i= PERM_ENCHANTMENT_SLOT; i<=TEMP_ENCHANTMENT_SLOT; ++i)
+ {
+ if(m_CastItem->GetEnchantmentId(EnchantmentSlot(i)))
+ pNewItem->SetEnchantment(EnchantmentSlot(i), m_CastItem->GetEnchantmentId(EnchantmentSlot(i)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(i)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(i)));
+ }
+
+ if(m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) < m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY))
+ {
+ double loosePercent = 1 - m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) / double(m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY));
+ player->DurabilityLoss(pNewItem, loosePercent);
+ }
+
+ if( player->IsInventoryPos( pos ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->StoreItem( dest, pNewItem, true);
+ return;
+ }
+ }
+ else if( player->IsBankPos ( pos ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = player->CanBankItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->BankItem( dest, pNewItem, true);
+ return;
+ }
+ }
+ else if( player->IsEquipmentPos ( pos ) )
+ {
+ uint16 dest;
+ uint8 msg = player->CanEquipItem( m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->EquipItem( dest, pNewItem, true);
+ player->AutoUnequipOffhandIfNeed();
+ return;
+ }
+ }
+
+ // fail
+ delete pNewItem;
+}
+
+void Spell::EffectOpenSecretSafe(uint32 i)
+{
+ EffectOpenLock(i); //no difference for now
+}
+
+void Spell::EffectProficiency(uint32 /*i*/)
+{
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player *p_target = (Player*)unitTarget;
+
+ uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask;
+ if(m_spellInfo->EquippedItemClass == 2 && !(p_target->GetWeaponProficiency() & subClassMask))
+ {
+ p_target->AddWeaponProficiency(subClassMask);
+ p_target->SendProficiency(uint8(0x02),p_target->GetWeaponProficiency());
+ }
+ if(m_spellInfo->EquippedItemClass == 4 && !(p_target->GetArmorProficiency() & subClassMask))
+ {
+ p_target->AddArmorProficiency(subClassMask);
+ p_target->SendProficiency(uint8(0x04),p_target->GetArmorProficiency());
+ }
+}
+
+void Spell::EffectApplyAreaAura(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ AreaAura* Aur = new AreaAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_caster, m_CastItem);
+ unitTarget->AddAura(Aur);
+}
+
+void Spell::EffectSummonType(uint32 i)
+{
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_GUARDIAN:
+ case SUMMON_TYPE_POSESSED:
+ case SUMMON_TYPE_POSESSED2:
+ EffectSummonGuardian(i);
+ break;
+ case SUMMON_TYPE_WILD:
+ EffectSummonWild(i);
+ break;
+ case SUMMON_TYPE_DEMON:
+ EffectSummonDemon(i);
+ break;
+ case SUMMON_TYPE_SUMMON:
+ EffectSummon(i);
+ break;
+ case SUMMON_TYPE_CRITTER:
+ case SUMMON_TYPE_CRITTER2:
+ EffectSummonCritter(i);
+ break;
+ case SUMMON_TYPE_TOTEM_SLOT1:
+ case SUMMON_TYPE_TOTEM_SLOT2:
+ case SUMMON_TYPE_TOTEM_SLOT3:
+ case SUMMON_TYPE_TOTEM_SLOT4:
+ case SUMMON_TYPE_TOTEM:
+ EffectSummonTotem(i);
+ break;
+ case SUMMON_TYPE_UNKNOWN1:
+ case SUMMON_TYPE_UNKNOWN2:
+ case SUMMON_TYPE_UNKNOWN3:
+ case SUMMON_TYPE_UNKNOWN4:
+ case SUMMON_TYPE_UNKNOWN5:
+ case SUMMON_TYPE_UNKNOWN6:
+ break;
+ default:
+ sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]);
+ break;
+ }
+}
+
+void Spell::EffectSummon(uint32 i)
+{
+ if(m_caster->GetPetGUID())
+ return;
+
+ if(!unitTarget)
+ return;
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+ uint32 level = m_caster->getLevel();
+ Pet* spawnCreature = new Pet(SUMMON_PET);
+
+ if(spawnCreature->LoadPetFromDB(m_caster,pet_entry))
+ {
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ return;
+ }
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),map,m_spellInfo->EffectMiscValue[i], pet_number))
+ {
+ sLog.outErrorDb("Spell::EffectSummon: no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
+ delete spawnCreature;
+ return;
+ }
+
+ // Summon in dest location
+ float x,y,z;
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ else
+ m_caster->GetClosePoint(x,y,z,spawnCreature->GetObjectSize());
+
+ spawnCreature->Relocate(x,y,z,-m_caster->GetOrientation());
+
+ if(!spawnCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
+ delete spawnCreature;
+ return;
+ }
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ spawnCreature->setPowerType(POWER_MANA);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+ spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ spawnCreature->InitStatsForLevel(level);
+
+ spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
+
+ spawnCreature->AIM_Initialize();
+ spawnCreature->InitPetCreateSpells();
+ spawnCreature->SetHealth(spawnCreature->GetMaxHealth());
+ spawnCreature->SetPower(POWER_MANA, spawnCreature->GetMaxPower(POWER_MANA));
+
+ std::string name = m_caster->GetName();
+ name.append(petTypeSuffix[spawnCreature->getPetType()]);
+ spawnCreature->SetName( name );
+
+ map->Add((Creature*)spawnCreature);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ m_caster->SetPet(spawnCreature);
+ spawnCreature->GetCharmInfo()->SetReactState( REACT_DEFENSIVE );
+ spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+}
+
+void Spell::EffectLearnSpell(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ EffectLearnPetSpell(i);
+
+ return;
+ }
+
+ Player *player = (Player*)unitTarget;
+
+ uint32 spellToLearn = (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) ? damage : m_spellInfo->EffectTriggerSpell[i];
+ player->learnSpell(spellToLearn);
+
+ sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() );
+}
+
+void Spell::EffectDispel(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ // Fill possible dispell list
+ std::vector <Aura *> dispel_list;
+
+ // Create dispel mask by dispel type
+ uint32 dispel_type = m_spellInfo->EffectMiscValue[i];
+ uint32 dispelMask = GetDispellMask( DispelType(dispel_type) );
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ Aura *aur = (*itr).second;
+ if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
+ {
+ if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC)
+ {
+ bool positive = true;
+ if (!aur->IsPositive())
+ positive = false;
+ else
+ positive = (aur->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)==0;
+
+ // do not remove positive auras if friendly target
+ // negative auras if non-friendly target
+ if(positive == unitTarget->IsFriendlyTo(m_caster))
+ continue;
+ }
+ // Add aura to dispel list
+ dispel_list.push_back(aur);
+ }
+ }
+ // Ok if exist some buffs for dispel try dispel it
+ if (!dispel_list.empty())
+ {
+ std::list < std::pair<uint32,uint64> > success_list;// (spell_id,casterGuid)
+ std::list < uint32 > fail_list; // spell_id
+ int32 list_size = dispel_list.size();
+ // Dispell N = damage buffs (or while exist buffs for dispel)
+ for (int32 count=0; count < damage && list_size > 0; ++count)
+ {
+ // Random select buff for dispel
+ Aura *aur = dispel_list[urand(0, list_size-1)];
+
+ SpellEntry const* spellInfo = aur->GetSpellProto();
+ // Base dispel chance
+ // TODO: possible chance depend from spell level??
+ int32 miss_chance = 0;
+ // Apply dispel mod from aura caster
+ if (Unit *caster = aur->GetCaster())
+ {
+ if ( Player* modOwner = caster->GetSpellModOwner() )
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this);
+ }
+ // Try dispel
+ if (roll_chance_i(miss_chance))
+ fail_list.push_back(aur->GetId());
+ else
+ success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
+ // Remove buff from list for prevent doubles
+ for (std::vector<Aura *>::iterator j = dispel_list.begin(); j != dispel_list.end(); )
+ {
+ Aura *dispeled = *j;
+ if (dispeled->GetId() == aur->GetId() && dispeled->GetCasterGUID() == aur->GetCasterGUID())
+ {
+ j = dispel_list.erase(j);
+ --list_size;
+ }
+ else
+ ++j;
+ }
+ }
+ // Send success log and really remove auras
+ if (!success_list.empty())
+ {
+ int32 count = success_list.size();
+ WorldPacket data(SMSG_SPELLDISPELLOG, 8+8+4+1+4+count*5);
+ data.append(unitTarget->GetPackGUID()); // Victim GUID
+ data.append(m_caster->GetPackGUID()); // Caster GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ data << uint8(0); // not used
+ data << uint32(count); // count
+ for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
+ data << uint32(spellInfo->Id); // Spell Id
+ data << uint8(0); // 0 - dispeled !=0 cleansed
+ unitTarget->RemoveAurasDueToSpellByDispel(spellInfo->Id, j->second, m_caster);
+ }
+ m_caster->SendMessageToSet(&data, true);
+
+ // On succes dispel
+ // Devour Magic
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12)
+ {
+ 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;
+ 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);
+ }
+ }
+ // Send fail log to client
+ if (!fail_list.empty())
+ {
+ // Failed to dispell
+ WorldPacket data(SMSG_DISPEL_FAILED, 8+8+4+4*fail_list.size());
+ data << uint64(m_caster->GetGUID()); // Caster GUID
+ data << uint64(unitTarget->GetGUID()); // Victim GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j)
+ data << uint32(*j); // Spell Id
+ m_caster->SendMessageToSet(&data, true);
+ }
+ }
+}
+
+void Spell::EffectDualWield(uint32 /*i*/)
+{
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->SetCanDualWield(true);
+}
+
+void Spell::EffectPull(uint32 /*i*/)
+{
+ // TODO: create a proper pull towards distract spell center for distract
+ sLog.outDebug("WORLD: Spell Effect DUMMY");
+}
+
+void Spell::EffectDistract(uint32 /*i*/)
+{
+ // Check for possible target
+ if (!unitTarget || unitTarget->isInCombat())
+ return;
+
+ // target must be OK to do this
+ if( unitTarget->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING ) )
+ return;
+
+ float angle = unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY);
+
+ if ( unitTarget->GetTypeId() == TYPEID_PLAYER )
+ {
+ // For players just turn them
+ WorldPacket data;
+ ((Player*)unitTarget)->BuildTeleportAckMsg(&data, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle);
+ ((Player*)unitTarget)->GetSession()->SendPacket( &data );
+ ((Player*)unitTarget)->SetPosition(unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle, false);
+ }
+ else
+ {
+ // Set creature Distracted, Stop it, And turn it
+ unitTarget->SetOrientation(angle);
+ unitTarget->StopMoving();
+ unitTarget->GetMotionMaster()->MoveDistract(damage*1000);
+ }
+}
+
+void Spell::EffectPickPocket(uint32 /*i*/)
+{
+ if( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ // victim must be creature and attackable
+ if( !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget) )
+ return;
+
+ // victim have to be alive and humanoid or undead
+ if( unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() &CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0)
+ {
+ int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel());
+
+ if (chance > irand(0, 19))
+ {
+ // Stealing successful
+ //sLog.outDebug("Sending loot from pickpocket");
+ ((Player*)m_caster)->SendLoot(unitTarget->GetGUID(),LOOT_PICKPOCKETING);
+ }
+ else
+ {
+ // Reveal action + get attack
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ if (((Creature*)unitTarget)->AI())
+ ((Creature*)unitTarget)->AI()->AttackStart(m_caster);
+ }
+ }
+}
+
+void Spell::EffectAddFarsight(uint32 i)
+{
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ int32 duration = GetSpellDuration(m_spellInfo);
+ DynamicObject* dynObj = new DynamicObject;
+ if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
+ {
+ delete dynObj;
+ return;
+ }
+ dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
+ dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002);
+ m_caster->AddDynObject(dynObj);
+ MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
+ m_caster->SetUInt64Value(PLAYER_FARSIGHT, dynObj->GetGUID());
+}
+
+void Spell::EffectSummonWild(uint32 i)
+{
+ uint32 creature_entry = m_spellInfo->EffectMiscValue[i];
+ if(!creature_entry)
+ return;
+
+ uint32 level = m_caster->getLevel();
+
+ // level of creature summoned using engineering item based at engineering skill level
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(proto && proto->RequiredSkill == SKILL_ENGINERING)
+ {
+ uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
+ if(skill202)
+ {
+ level = skill202/5;
+ }
+ }
+ }
+
+ // select center of summon position
+ float center_x = m_targets.m_destX;
+ float center_y = m_targets.m_destY;
+ float center_z = m_targets.m_destZ;
+
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ int32 amount = damage > 0 ? damage : 1;
+
+ for(int32 count = 0; count < amount; ++count)
+ {
+ float px, py, pz;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ // Summon 1 unit in dest location
+ if (count == 0)
+ {
+ px = m_targets.m_destX;
+ py = m_targets.m_destY;
+ pz = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(px,py,pz,3.0f);
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
+
+ m_caster->SummonCreature(creature_entry,px,py,pz,m_caster->GetOrientation(),summonType,duration);
+ }
+}
+
+void Spell::EffectSummonGuardian(uint32 i)
+{
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+
+ // Jewelery statue case (totem like)
+ if(m_spellInfo->SpellIconID==2056)
+ {
+ EffectSummonTotem(i);
+ return;
+ }
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ // Search old Guardian only for players (if casted spell not have duration or cooldown)
+ // FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time
+ // so this code hack in fact
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && (duration <= 0 || GetSpellRecoveryTime(m_spellInfo)==0) )
+ if(((Player*)m_caster)->HasGuardianWithEntry(pet_entry))
+ return; // find old guardian, ignore summon
+
+ // in another case summon new
+ uint32 level = m_caster->getLevel();
+
+ // level of pet summoned using engineering item based at engineering skill level
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(proto && proto->RequiredSkill == SKILL_ENGINERING)
+ {
+ uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
+ if(skill202)
+ {
+ level = skill202/5;
+ }
+ }
+ }
+
+ // select center of summon position
+ float center_x = m_targets.m_destX;
+ float center_y = m_targets.m_destY;
+ float center_z = m_targets.m_destZ;
+
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ int32 amount = damage > 0 ? damage : 1;
+
+ for(int32 count = 0; count < amount; ++count)
+ {
+ Pet* spawnCreature = new Pet(GUARDIAN_PET);
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map,m_spellInfo->EffectMiscValue[i], pet_number))
+ {
+ sLog.outError("no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
+ delete spawnCreature;
+ return;
+ }
+
+ float px, py, pz;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ // Summon 1 unit in dest location
+ if (count == 0)
+ {
+ px = m_targets.m_destX;
+ py = m_targets.m_destY;
+ pz = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(px,py,pz,spawnCreature->GetObjectSize());
+
+ spawnCreature->Relocate(px,py,pz,m_caster->GetOrientation());
+
+ if(!spawnCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
+ delete spawnCreature;
+ return;
+ }
+
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ spawnCreature->setPowerType(POWER_MANA);
+ spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ spawnCreature->InitStatsForLevel(level);
+ spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
+
+ spawnCreature->AIM_Initialize();
+
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddGuardian(spawnCreature);
+
+ map->Add((Creature*)spawnCreature);
+ }
+}
+
+void Spell::EffectTeleUnitsFaceCaster(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->isInFlight())
+ return;
+
+ uint32 mapid = m_caster->GetMapId();
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ float fx,fy,fz;
+ m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation());
+}
+
+void Spell::EffectLearnSkill(uint32 i)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(damage < 0)
+ return;
+
+ uint32 skillid = m_spellInfo->EffectMiscValue[i];
+ uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid);
+ ((Player*)unitTarget)->SetSkill(skillid, skillval?skillval:1, damage*75);
+}
+
+void Spell::EffectAddHonor(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ sLog.outDebug("SpellEffect::AddHonor called for spell_id %u , that rewards %d honor points to player: %u", m_spellInfo->Id, this->damage, ((Player*)unitTarget)->GetGUIDLow());
+
+ // TODO: find formula for honor reward based on player's level!
+
+ // now fixed only for level 70 players:
+ if (((Player*)unitTarget)->getLevel() == 70)
+ ((Player*)unitTarget)->RewardHonor(NULL, 1, this->damage);
+}
+
+void Spell::EffectTradeSkill(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // uint32 skillid = m_spellInfo->EffectMiscValue[i];
+ // uint16 skillmax = ((Player*)unitTarget)->(skillid);
+ // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75);
+}
+
+void Spell::EffectEnchantItemPerm(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if (!itemTarget)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ p_caster->UpdateCraftSkill(m_spellInfo->Id);
+
+ if (m_spellInfo->EffectMiscValue[i])
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ // item can be in trade slot and have owner diff. from caster
+ Player* item_owner = itemTarget->GetOwner();
+ if(!item_owner)
+ return;
+
+ if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)",
+ p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
+ itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
+ item_owner->GetName(),item_owner->GetSession()->GetAccountId());
+
+ // remove old enchanting before applying new if equipped
+ item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false);
+
+ itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0);
+
+ // add new enchanting if equipped
+ item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true);
+ }
+}
+
+void Spell::EffectEnchantItemTmp(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ if(!itemTarget)
+ return;
+
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+
+ // Shaman Rockbiter Weapon
+ if(i==0 && m_spellInfo->Effect[1]==SPELL_EFFECT_DUMMY)
+ {
+ int32 enchnting_damage = m_currentBasePoints[1]+1;
+
+ // enchanting id selected by calculated damage-per-sec stored in Effect[1] base value
+ // with already applied percent bonus from Elemental Weapons talent
+ // Note: damage calculated (correctly) with rounding int32(float(v)) but
+ // RW enchantments applied damage int32(float(v)+0.5), this create 0..1 difference sometime
+ switch(enchnting_damage)
+ {
+ // Rank 1
+ case 2: enchant_id = 29; break; // 0% [ 7% == 2, 14% == 2, 20% == 2]
+ // Rank 2
+ case 4: enchant_id = 6; break; // 0% [ 7% == 4, 14% == 4]
+ case 5: enchant_id = 3025; break; // 20%
+ // Rank 3
+ case 6: enchant_id = 1; break; // 0% [ 7% == 6, 14% == 6]
+ case 7: enchant_id = 3027; break; // 20%
+ // Rank 4
+ case 9: enchant_id = 3032; break; // 0% [ 7% == 6]
+ case 10: enchant_id = 503; break; // 14%
+ case 11: enchant_id = 3031; break; // 20%
+ // Rank 5
+ case 15: enchant_id = 3035; break; // 0%
+ case 16: enchant_id = 1663; break; // 7%
+ case 17: enchant_id = 3033; break; // 14%
+ case 18: enchant_id = 3034; break; // 20%
+ // Rank 6
+ case 28: enchant_id = 3038; break; // 0%
+ case 29: enchant_id = 683; break; // 7%
+ case 31: enchant_id = 3036; break; // 14%
+ case 33: enchant_id = 3037; break; // 20%
+ // Rank 7
+ case 40: enchant_id = 3041; break; // 0%
+ case 42: enchant_id = 1664; break; // 7%
+ case 45: enchant_id = 3039; break; // 14%
+ case 48: enchant_id = 3040; break; // 20%
+ // Rank 8
+ case 49: enchant_id = 3044; break; // 0%
+ case 52: enchant_id = 2632; break; // 7%
+ case 55: enchant_id = 3042; break; // 14%
+ case 58: enchant_id = 3043; break; // 20%
+ // Rank 9
+ case 62: enchant_id = 2633; break; // 0%
+ case 66: enchant_id = 3018; break; // 7%
+ case 70: enchant_id = 3019; break; // 14%
+ case 74: enchant_id = 3020; break; // 20%
+ default:
+ sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW",enchnting_damage);
+ return;
+ }
+ }
+
+ if (!enchant_id)
+ {
+ sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,i);
+ return;
+ }
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ {
+ sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id %u ",m_spellInfo->Id,i,enchant_id);
+ return;
+ }
+
+ // select enchantment duration
+ uint32 duration;
+
+ // rogue family enchantments exception by duration
+ if(m_spellInfo->Id==38615)
+ duration = 1800; // 30 mins
+ // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints)
+ else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE)
+ duration = 3600; // 1 hour
+ // shaman family enchantments
+ else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN)
+ duration = 1800; // 30 mins
+ // other cases with this SpellVisual already selected
+ else if(m_spellInfo->SpellVisual==215)
+ duration = 1800; // 30 mins
+ // some fishing pole bonuses
+ else if(m_spellInfo->SpellVisual==563)
+ duration = 600; // 10 mins
+ // shaman rockbiter enchantments
+ else if(m_spellInfo->SpellVisual==0)
+ duration = 1800; // 30 mins
+ else if(m_spellInfo->Id==29702)
+ duration = 300; // 5 mins
+ else if(m_spellInfo->Id==37360)
+ duration = 300; // 5 mins
+ // default case
+ else
+ duration = 3600; // 1 hour
+
+ // item can be in trade slot and have owner diff. from caster
+ Player* item_owner = itemTarget->GetOwner();
+ if(!item_owner)
+ return;
+
+ if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)",
+ p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
+ itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
+ item_owner->GetName(),item_owner->GetSession()->GetAccountId());
+
+ // remove old enchanting before applying new if equipped
+ item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,false);
+
+ itemTarget->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, duration*1000, 0);
+
+ // add new enchanting if equipped
+ item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,true);
+}
+
+void Spell::EffectTameCreature(uint32 /*i*/)
+{
+ if(m_caster->GetPetGUID())
+ return;
+
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+
+ if(creatureTarget->isPet())
+ return;
+
+ if(m_caster->getClass() == CLASS_HUNTER)
+ {
+ // cast finish successfully
+ //SendChannelUpdate(0);
+ finish();
+
+ Pet* pet = new Pet(HUNTER_PET);
+
+ if(!pet->CreateBaseAtCreature(creatureTarget))
+ {
+ delete pet;
+ return;
+ }
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ pet->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
+ pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ if(!pet->InitStatsForLevel(creatureTarget->getLevel()))
+ {
+ sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
+ delete pet;
+ return;
+ }
+
+ // prepare visual effect for levelup
+ pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1);
+
+ pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true);
+ // this enables pet details window (Shift+P)
+ pet->AIM_Initialize();
+ pet->InitPetCreateSpells();
+ pet->SetHealth(pet->GetMaxHealth());
+
+ MapManager::Instance().GetMap(pet->GetMapId(), pet)->Add((Creature*)pet);
+
+ // visual effect for levelup
+ pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel());
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ m_caster->SetPet(pet);
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+ }
+}
+
+void Spell::EffectSummonPet(uint32 i)
+{
+ uint32 petentry = m_spellInfo->EffectMiscValue[i];
+
+ Pet *OldSummon = m_caster->GetPet();
+
+ // if pet requested type already exist
+ if( OldSummon )
+ {
+ if(petentry == 0 || OldSummon->GetEntry() == petentry)
+ {
+ // pet in corpse state can't be summoned
+ if( OldSummon->isDead() )
+ return;
+
+ MapManager::Instance().GetMap(OldSummon->GetMapId(), OldSummon)->Remove((Creature*)OldSummon,false);
+ OldSummon->SetMapId(m_caster->GetMapId());
+
+ float px, py, pz;
+ m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectSize());
+
+ OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
+ MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->Add((Creature*)OldSummon);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled() )
+ {
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+ return;
+ }
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false);
+ else
+ return;
+ }
+
+ Pet* NewSummon = new Pet;
+
+ // petentry==0 for hunter "call pet" (current pet summoned if any)
+ if(NewSummon->LoadPetFromDB(m_caster,petentry))
+ {
+ if(NewSummon->getPetType()==SUMMON_PET)
+ {
+ // Remove Demonic Sacrifice auras (known pet)
+ Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
+ {
+ if((*itr)->GetModifier()->m_miscvalue==2228)
+ {
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+ itr = auraClassScripts.begin();
+ }
+ else
+ ++itr;
+ }
+ }
+
+ return;
+ }
+
+ // not error in case fail hunter call pet
+ if(!petentry)
+ {
+ delete NewSummon;
+ return;
+ }
+
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(petentry);
+
+ if(!cInfo)
+ {
+ sLog.outError("EffectSummonPet: creature entry %u not found.",petentry);
+ delete NewSummon;
+ return;
+ }
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!NewSummon->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, petentry, pet_number))
+ {
+ delete NewSummon;
+ return;
+ }
+
+ float px, py, pz;
+ m_caster->GetClosePoint(px, py, pz, NewSummon->GetObjectSize());
+
+ NewSummon->Relocate(px, py, pz, m_caster->GetOrientation());
+
+ if(!NewSummon->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", NewSummon->GetGUIDLow(), NewSummon->GetEntry(), NewSummon->GetPositionX(), NewSummon->GetPositionY());
+ delete NewSummon;
+ return;
+ }
+
+ uint32 petlevel = m_caster->getLevel();
+ NewSummon->setPetType(SUMMON_PET);
+
+ uint32 faction = m_caster->getFaction();
+ if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isTotem())
+ {
+ Unit* owner = ((Totem*)m_caster)->GetOwner();
+ if(owner)
+ faction = owner->getFaction();
+ NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE);
+ }
+
+ NewSummon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
+ NewSummon->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ NewSummon->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction);
+ NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,time(NULL));
+ NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+ NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true);
+ // this enables pet details window (Shift+P)
+
+ // this enables popup window (pet dismiss, cancel), hunter pet additional flags set later
+ NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
+
+ NewSummon->InitStatsForLevel( petlevel);
+ NewSummon->InitPetCreateSpells();
+
+ if(NewSummon->getPetType()==SUMMON_PET)
+ {
+ // Remove Demonic Sacrifice auras (new pet)
+ Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
+ {
+ if((*itr)->GetModifier()->m_miscvalue==2228)
+ {
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+ itr = auraClassScripts.begin();
+ }
+ else
+ ++itr;
+ }
+
+ // generate new name for summon pet
+ std::string new_name=objmgr.GeneratePetName(petentry);
+ if(!new_name.empty())
+ NewSummon->SetName(new_name);
+ }
+ else if(NewSummon->getPetType()==HUNTER_PET)
+ NewSummon->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+
+ NewSummon->AIM_Initialize();
+ NewSummon->SetHealth(NewSummon->GetMaxHealth());
+ NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA));
+
+ map->Add((Creature*)NewSummon);
+
+ m_caster->SetPet(NewSummon);
+ sLog.outDebug("New Pet has guid %u", NewSummon->GetGUIDLow());
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+}
+
+void Spell::EffectLearnPetSpell(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+ if(!pet->isAlive())
+ return;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+ if(!learn_spellproto)
+ return;
+
+ pet->SetTP(pet->m_TrainingPoints - pet->GetTPForSpell(learn_spellproto->Id));
+ pet->learnSpell(learn_spellproto->Id);
+
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+ _player->PetSpellInitialize();
+}
+
+void Spell::EffectTaunt(uint32 /*i*/)
+{
+ // this effect use before aura Taunt apply for prevent taunt already attacking target
+ // for spell as marked "non effective at already attacking target"
+ if(unitTarget && unitTarget->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(unitTarget->getVictim()==m_caster)
+ {
+ SendCastResult(SPELL_FAILED_DONT_REPORT);
+ return;
+ }
+ }
+
+ // Also use this effect to set the taunter's threat to the taunted creature's highest value
+ if(unitTarget->CanHaveThreatList() && unitTarget->getThreatManager().getCurrentVictim())
+ unitTarget->getThreatManager().addThreat(m_caster,unitTarget->getThreatManager().getCurrentVictim()->getThreat());
+}
+
+void Spell::EffectWeaponDmg(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ // multiple weapon dmg effect workaround
+ // execute only the last weapon damage
+ // and handle all effects at once
+ for (int j = 0; j < 3; j++)
+ {
+ switch(m_spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ if (j < i) // we must calculate only at last weapon effect
+ return;
+ break;
+ }
+ }
+
+ // some spell specific modifiers
+ bool customBonusDamagePercentMod = false;
+ float bonusDamagePercentMod = 1.0f; // applied to fixed effect damage bonus if set customBonusDamagePercentMod
+ float weaponDamagePercentMod = 1.0f; // applied to weapon damage (and to fixed effect damage bonus if customBonusDamagePercentMod not set
+ float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage
+ bool normalized = false;
+
+ int32 spell_bonus = 0; // bonus specific for spell
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Whirlwind, single only spell with 2 weapon white damage apply if have
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x00000400000000LL))
+ {
+ if(((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK,true))
+ spell_bonus += m_caster->CalculateDamage (OFF_ATTACK, normalized);
+ }
+ // Devastate bonus and sunder armor refresh
+ else if(m_spellInfo->SpellVisual == 671 && m_spellInfo->SpellIconID == 1508)
+ {
+ customBonusDamagePercentMod = true;
+ bonusDamagePercentMod = 0.0f; // only applied if auras found
+
+ Unit::AuraList const& list = unitTarget->GetAurasByType(SPELL_AURA_MOD_RESISTANCE);
+ for(Unit::AuraList::const_iterator itr=list.begin();itr!=list.end();++itr)
+ {
+ SpellEntry const *proto = (*itr)->GetSpellProto();
+ if(proto->SpellVisual == 406 && proto->SpellIconID == 565)
+ {
+ int32 duration = GetSpellDuration(proto);
+ (*itr)->SetAuraDuration(duration);
+ (*itr)->UpdateAuraDuration();
+ bonusDamagePercentMod += 1.0f; // +100%
+ }
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Ambush
+ if(m_spellInfo->SpellFamilyFlags & 0x00000200LL)
+ {
+ customBonusDamagePercentMod = true;
+ bonusDamagePercentMod = 2.5f; // 250%
+ }
+ // Mutilate (for each hand)
+ else if(m_spellInfo->SpellFamilyFlags & 0x600000000LL)
+ {
+ bool found = false;
+ // fast check
+ if(unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON))
+ found = true;
+ // full aura scan
+ else
+ {
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
+ {
+ if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(found)
+ totalDamagePercentMod *= 1.5f; // 150% if poisoned
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Seal of Command - receive benefit from Spell Damage and Healing
+ if(m_spellInfo->SpellFamilyFlags & 0x00000002000000LL)
+ {
+ spell_bonus += int32(0.20f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo)));
+ spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget));
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ // Skyshatter Harness item set bonus
+ // Stormstrike
+ if(m_spellInfo->SpellFamilyFlags & 0x001000000000LL)
+ {
+ Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
+ {
+ // Stormstrike AP Buff
+ if ( (*i)->GetModifier()->m_miscvalue == 5634 )
+ {
+ m_caster->CastSpell(m_caster,38430,true,NULL,*i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ int32 fixed_bonus = 0;
+ for (int j = 0; j < 3; j++)
+ {
+ switch(m_spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ fixed_bonus += CalculateDamage(j,unitTarget);
+ break;
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ fixed_bonus += CalculateDamage(j,unitTarget);
+ normalized = true;
+ break;
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ weaponDamagePercentMod *= float(CalculateDamage(j,unitTarget)) / 100.0f;
+
+ // applied only to prev.effects fixed damage
+ if(customBonusDamagePercentMod)
+ fixed_bonus = int32(fixed_bonus*bonusDamagePercentMod);
+ else
+ fixed_bonus = int32(fixed_bonus*weaponDamagePercentMod);
+ break;
+ default:
+ break; // not weapon damage effect, just skip
+ }
+ }
+
+ // non-weapon damage
+ int32 bonus = spell_bonus + fixed_bonus;
+
+ // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage
+ if(bonus)
+ {
+ UnitMods unitMod;
+ switch(m_attackType)
+ {
+ default:
+ case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
+ case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
+ case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
+ }
+
+ float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT);
+ bonus = int32(bonus*weapon_total_pct);
+ }
+
+ // + weapon damage with applied weapon% dmg to base weapon damage in call
+ bonus += int32(m_caster->CalculateDamage(m_attackType, normalized)*weaponDamagePercentMod);
+
+ // total damage
+ bonus = int32(bonus*totalDamagePercentMod);
+
+ // prevent negative damage
+ uint32 eff_damage = uint32(bonus > 0 ? bonus : 0);
+
+ const uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS;
+
+ uint32 hitInfo = 0;
+ VictimState victimState = VICTIMSTATE_NORMAL;
+ uint32 blocked_dmg = 0;
+ uint32 absorbed_dmg = 0;
+ uint32 resisted_dmg = 0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, m_spellSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, m_attackType, m_spellInfo, m_IsTriggeredSpell);
+
+ if ((hitInfo & nohitMask) && m_attackType != RANGED_ATTACK) // not send ranged miss/etc
+ m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, m_spellSchoolMask, eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg);
+
+ bool criticalhit = (hitInfo & HITINFO_CRITICALHIT);
+ m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, m_spellSchoolMask, absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit);
+
+ if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
+ {
+ eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
+ }
+ else
+ {
+ cleanDamage.damage += eff_damage;
+ eff_damage = 0;
+ }
+
+ // SPELL_SCHOOL_NORMAL use for weapon-like threat and rage calculation
+ m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true);
+
+ // Hemorrhage
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & 0x2000000))
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddComboPoints(unitTarget, 1);
+ }
+ // Mangle (Cat): CP
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_DRUID && (m_spellInfo->SpellFamilyFlags==0x0000040000000000LL))
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddComboPoints(unitTarget,1);
+ }
+
+
+ // take ammo
+ if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
+
+ // wands don't have ammo
+ if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND)
+ return;
+
+ if( pItem->GetProto()->InventoryType == INVTYPE_THROWN )
+ {
+ if(pItem->GetMaxStackCount()==1)
+ {
+ // decrease durability for non-stackable throw weapon
+ ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED);
+ }
+ else
+ {
+ // decrease items amount for stackable throw weapon
+ uint32 count = 1;
+ ((Player*)m_caster)->DestroyItemCount( pItem, count, true);
+ }
+ }
+ else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID))
+ ((Player*)m_caster)->DestroyItemCount(ammo, 1, true);
+ }
+}
+
+void Spell::EffectThreat(uint32 /*i*/)
+{
+ if(!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive())
+ return;
+
+ if(!unitTarget->CanHaveThreatList())
+ return;
+
+ unitTarget->AddThreat(m_caster, float(damage));
+}
+
+void Spell::EffectHealMaxHealth(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ uint32 heal = m_caster->GetMaxHealth();
+
+ int32 gain = unitTarget->ModifyHealth(heal);
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ m_caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, heal);
+}
+
+void Spell::EffectInterruptCast(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ // TODO: not all spells that used this effect apply cooldown at school spells
+ // also exist case: apply cooldown to interrupted cast only and to all spells
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (unitTarget->m_currentSpells[i])
+ {
+ // check if we can interrupt spell
+ if ( unitTarget->m_currentSpells[i]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT && unitTarget->m_currentSpells[i]->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE )
+ {
+ unitTarget->ProhibitSpellScholl(GetSpellSchoolMask(unitTarget->m_currentSpells[i]->m_spellInfo), GetSpellDuration(m_spellInfo));
+ unitTarget->InterruptSpell(i,false);
+ }
+ }
+ }
+}
+
+void Spell::EffectSummonObjectWild(uint32 i)
+{
+ uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
+
+ GameObject* pGameObj = new GameObject;
+
+ WorldObject* target = focusObject;
+ if( !target )
+ target = m_caster;
+
+ float x,y,z;
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ else
+ m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
+
+ Map *map = target->GetMap();
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
+ x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 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);
+ map->Add(pGameObj);
+
+ if(pGameObj->GetMapId() == 489 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //WS
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Player *pl = (Player*)m_caster;
+ BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+ if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS)
+ {
+ uint32 team = ALLIANCE;
+
+ if(pl->GetTeam() == team)
+ team = HORDE;
+
+ ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team);
+ }
+ }
+ }
+
+ 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)
+ {
+ ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID());
+ }
+ }
+ }
+
+ if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
+ {
+ GameObject* linkedGO = new GameObject;
+ if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map,
+ x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ linkedGO->SetSpellId(m_spellInfo->Id);
+
+ m_caster->AddGameObject(linkedGO);
+ map->Add(linkedGO);
+ }
+ else
+ {
+ delete linkedGO;
+ linkedGO = NULL;
+ return;
+ }
+ }
+}
+
+void Spell::EffectScriptEffect(uint32 effIndex)
+{
+ // TODO: we must implement hunter pet summon at login there (spell 6962)
+
+ // by spell id
+ switch(m_spellInfo->Id)
+ {
+ // Bending Shinbone
+ case 8856:
+ {
+ if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,5))
+ {
+ case 1: spell_id = 8854; break;
+ default: spell_id = 8855; break;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+
+ // Healthstone creating spells
+ case 6201:
+ case 6202:
+ case 5699:
+ case 11729:
+ case 11730:
+ case 27230:
+ {
+ uint32 itemtype;
+ uint32 rank = 0;
+ Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if((*i)->GetId() == 18692)
+ {
+ rank = 1;
+ break;
+ }
+ else if((*i)->GetId() == 18693)
+ {
+ rank = 2;
+ break;
+ }
+ }
+
+ static uint32 const itypes[6][3] = {
+ { 5512,19004,19005}, // Minor Healthstone
+ { 5511,19006,19007}, // Lesser Healthstone
+ { 5509,19008,19009}, // Healthstone
+ { 5510,19010,19011}, // Greater Healthstone
+ { 9421,19012,19013}, // Major Healthstone
+ {22103,22104,22105} // Master Healthstone
+ };
+
+ switch(m_spellInfo->Id)
+ {
+ case 6201: itemtype=itypes[0][rank];break; // Minor Healthstone
+ case 6202: itemtype=itypes[1][rank];break; // Lesser Healthstone
+ case 5699: itemtype=itypes[2][rank];break; // Healthstone
+ case 11729: itemtype=itypes[3][rank];break; // Greater Healthstone
+ case 11730: itemtype=itypes[4][rank];break; // Major Healthstone
+ case 27230: itemtype=itypes[5][rank];break; // Master Healthstone
+ default:
+ return;
+ }
+ DoCreateItem( effIndex, itemtype );
+ return;
+ }
+ // Brittle Armor - need remove one 24575 Brittle Armor aura
+ case 24590:
+ unitTarget->RemoveSingleAuraFromStack(24575, 0);
+ unitTarget->RemoveSingleAuraFromStack(24575, 1);
+ return;
+ // Mercurial Shield - need remove one 26464 Mercurial Shield aura
+ case 26465:
+ unitTarget->RemoveSingleAuraFromStack(26464, 0);
+ return;
+ // Orb teleport spells
+ case 25140:
+ case 25143:
+ case 25650:
+ case 25652:
+ case 29128:
+ case 29129:
+ case 35376:
+ case 35727:
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spellid;
+ switch(m_spellInfo->Id)
+ {
+ case 25140: spellid = 32571; break;
+ case 25143: spellid = 32572; break;
+ case 25650: spellid = 30140; break;
+ case 25652: spellid = 30141; break;
+ case 29128: spellid = 32568; break;
+ case 29129: spellid = 32569; break;
+ case 35376: spellid = 25649; break;
+ case 35727: spellid = 35730; break;
+ default:
+ return;
+ }
+
+ unitTarget->CastSpell(unitTarget,spellid,false);
+ return;
+ }
+
+ // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell)
+ case 22539:
+ case 22972:
+ case 22975:
+ case 22976:
+ case 22977:
+ case 22978:
+ case 22979:
+ case 22980:
+ case 22981:
+ case 22982:
+ case 22983:
+ case 22984:
+ case 22985:
+ {
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+
+ // Onyxia Scale Cloak
+ if(unitTarget->GetDummyAura(22683))
+ return;
+
+ // Shadow Flame
+ m_caster->CastSpell(unitTarget, 22682, true);
+ return;
+ }
+ break;
+
+ // Summon Black Qiraji Battle Tank
+ case 26656:
+ {
+ if(!unitTarget)
+ return;
+
+ // Prevent stacking of mounts
+ unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ // Two separate mounts depending on area id (allows use both in and out of specific instance)
+ if (unitTarget->GetAreaId() == 3428)
+ unitTarget->CastSpell(unitTarget, 25863, false);
+ else
+ unitTarget->CastSpell(unitTarget, 26655, false);
+ break;
+ }
+ // Piccolo of the Flaming Fire
+ case 17512:
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
+ break;
+ }
+
+ // Dreaming Glory
+ case 28698:
+ {
+ if(!unitTarget)
+ return;
+ unitTarget->CastSpell(unitTarget, 28694, true);
+ break;
+ }
+
+ // Netherbloom
+ case 28702:
+ {
+ if(!unitTarget)
+ return;
+ // 25% chance of casting a random buff
+ if(roll_chance_i(75))
+ return;
+
+ // triggered spells are 28703 to 28707
+ // Note: some sources say, that there was the possibility of
+ // receiving a debuff. However, this seems to be removed by a patch.
+ const uint32 spellid = 28703;
+
+ // don't overwrite an existing aura
+ for(uint8 i=0; i<5; i++)
+ if(unitTarget->HasAura(spellid+i, 0))
+ return;
+ unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
+ break;
+ }
+
+ // Nightmare Vine
+ case 28720:
+ {
+ if(!unitTarget)
+ return;
+ // 25% chance of casting Nightmare Pollen
+ if(roll_chance_i(75))
+ return;
+ unitTarget->CastSpell(unitTarget, 28721, true);
+ break;
+ }
+
+ // Mirren's Drinking Hat
+ case 29830:
+ {
+ uint32 item = 0;
+ switch ( urand(1,6) )
+ {
+ case 1: case 2: case 3: item = 23584; break;// Loch Modan Lager
+ case 4: case 5: item = 23585; break;// Stouthammer Lite
+ case 6: item = 23586; break;// Aerie Peak Pale Ale
+ }
+ if (item)
+ DoCreateItem(effIndex,item);
+ break;
+ }
+ // Improved Sprint
+ case 30918:
+ {
+ // Removes snares and roots.
+ uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE);
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ Aura *aur = iter->second;
+ if (!aur->IsPositive()) //only remove negative spells
+ {
+ // check for mechanic mask
+ if(GetSpellMechanicMask(aur->GetSpellProto(), aur->GetEffIndex()) & mechanic_mask)
+ {
+ unitTarget->RemoveAurasDueToSpell(aur->GetId());
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ break;
+ }
+ case 41126: // Flame Crash
+ {
+ if(!unitTarget)
+ return;
+
+ unitTarget->CastSpell(unitTarget, 41131, true);
+ break;
+ }
+ case 44876: // Force Cast - Portal Effect: Sunwell Isle
+ {
+ if(!unitTarget)
+ return;
+
+ unitTarget->CastSpell(unitTarget, 44870, true);
+ break;
+ }
+
+ // Goblin Weather Machine
+ case 46203:
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spellId;
+ switch(rand()%4)
+ {
+ case 0:
+ spellId=46740;
+ break;
+ case 1:
+ spellId=46739;
+ break;
+ case 2:
+ spellId=46738;
+ break;
+ case 3:
+ spellId=46736;
+ break;
+ }
+ unitTarget->CastSpell(unitTarget, spellId, true);
+ break;
+ }
+ }
+
+ if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN )
+ {
+ switch(m_spellInfo->SpellFamilyFlags)
+ {
+ // Judgement
+ case 0x800000:
+ {
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+ uint32 spellId2 = 0;
+
+ // all seals have aura dummy
+ Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr)->GetSpellProto();
+
+ // search seal (all seals have judgement's aura dummy spell id in 2 effect
+ if ( !spellInfo || !IsSealSpell((*itr)->GetSpellProto()) || (*itr)->GetEffIndex() != 2 )
+ continue;
+
+ // must be calculated base at raw base points in spell proto, GetModifier()->m_value for S.Righteousness modified by SPELLMOD_DAMAGE
+ spellId2 = (*itr)->GetSpellProto()->EffectBasePoints[2]+1;
+
+ if(spellId2 <= 1)
+ continue;
+
+ // found, remove seal
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+
+ // Sanctified Judgement
+ Unit::AuraList const& m_auras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_auras.begin(); i != m_auras.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 205 && (*i)->GetSpellProto()->Attributes == 0x01D0LL)
+ {
+ int32 chance = (*i)->GetModifier()->m_amount;
+ if ( roll_chance_i(chance) )
+ {
+ int32 mana = spellInfo->manaCost;
+ if ( Player* modOwner = m_caster->GetSpellModOwner() )
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, mana);
+ mana = int32(mana* 0.8f);
+ m_caster->CastCustomSpell(m_caster,31930,&mana,NULL,NULL,true,NULL,*i);
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+
+ m_caster->CastSpell(unitTarget,spellId2,true);
+ return;
+ }
+ }
+ }
+
+ // normal DB scripted effect
+ if(!unitTarget)
+ return;
+
+ sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id);
+ sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget);
+}
+
+void Spell::EffectSanctuary(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ //unitTarget->CombatStop();
+
+ unitTarget->CombatStop();
+ unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting
+ // Vanish allows to remove all threat and cast regular stealth so other spells can be used
+ if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_VANISH))
+ {
+ ((Player *)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ }
+}
+
+void Spell::EffectAddComboPoints(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(damage <= 0)
+ return;
+
+ ((Player*)m_caster)->AddComboPoints(unitTarget, damage);
+}
+
+void Spell::EffectDuel(uint32 i)
+{
+ if(!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *caster = (Player*)m_caster;
+ Player *target = (Player*)unitTarget;
+
+ // caster or target already have requested duel
+ if( caster->duel || target->duel || target->GetSocial()->HasIgnore(caster->GetGUIDLow()) )
+ return;
+
+ // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities)
+ // Don't have to check the target's map since you cannot challenge someone across maps
+ if( caster->GetMapId() != 0 && caster->GetMapId() != 1 && caster->GetMapId() != 530)
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetZoneId());
+ if(casterAreaEntry && (casterAreaEntry->flags & AREA_FLAG_CAPITAL) )
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetZoneId());
+ if(targetAreaEntry && (targetAreaEntry->flags & AREA_FLAG_CAPITAL) )
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ //CREATE DUEL FLAG OBJECT
+ GameObject* pGameObj = new GameObject;
+
+ uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
+
+ Map *map = m_caster->GetMap();
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
+ m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2 ,
+ m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2 ,
+ m_caster->GetPositionZ(),
+ m_caster->GetOrientation(), 0, 0, 0, 0, 0, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() );
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 );
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ m_caster->AddGameObject(pGameObj);
+ map->Add(pGameObj);
+ //END
+
+ // Send request
+ WorldPacket data(SMSG_DUEL_REQUESTED, 16);
+ data << pGameObj->GetGUID();
+ data << caster->GetGUID();
+ caster->GetSession()->SendPacket(&data);
+ target->GetSession()->SendPacket(&data);
+
+ // create duel-info
+ DuelInfo *duel = new DuelInfo;
+ duel->initiator = caster;
+ duel->opponent = target;
+ duel->startTime = 0;
+ duel->startTimer = 0;
+ caster->duel = duel;
+
+ DuelInfo *duel2 = new DuelInfo;
+ duel2->initiator = caster;
+ duel2->opponent = caster;
+ duel2->startTime = 0;
+ duel2->startTimer = 0;
+ target->duel = duel2;
+
+ caster->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
+ target->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
+}
+
+void Spell::EffectStuck(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!sWorld.getConfig(CONFIG_CAST_UNSTUCK))
+ return;
+
+ Player* pTarget = (Player*)unitTarget;
+
+ sLog.outDebug("Spell Effect: Stuck");
+ sLog.outDetail("Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", pTarget->GetName(), pTarget->GetGUIDLow(), m_caster->GetMapId(), m_caster->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ());
+
+ if(pTarget->isInFlight())
+ return;
+
+ // 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));
+
+ // Stuck spell trigger Hearthstone cooldown
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690);
+ if(!spellInfo)
+ return;
+ Spell spell(pTarget,spellInfo,true,0);
+ spell.SendSpellCooldown();
+}
+
+void Spell::EffectSummonPlayer(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Evil Twin (ignore player summon, but hide this for summoner)
+ if(unitTarget->GetDummyAura(23445))
+ return;
+
+ float x,y,z;
+ m_caster->GetClosePoint(x,y,z,unitTarget->GetObjectSize());
+
+ ((Player*)unitTarget)->SetSummonPoint(m_caster->GetMapId(),x,y,z);
+
+ WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4);
+ data << uint64(m_caster->GetGUID()); // summoner guid
+ data << uint32(m_caster->GetZoneId()); // summoner zone
+ data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+static ScriptInfo generateActivateCommand()
+{
+ ScriptInfo si;
+ si.command = SCRIPT_COMMAND_ACTIVATE_OBJECT;
+ return si;
+}
+
+void Spell::EffectActivateObject(uint32 effect_idx)
+{
+ if(!gameObjTarget)
+ return;
+
+ static ScriptInfo activateCommand = generateActivateCommand();
+
+ int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx];
+
+ sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget);
+}
+
+void Spell::EffectSummonTotem(uint32 i)
+{
+ uint8 slot = 0;
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break;
+ case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break;
+ case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break;
+ case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break;
+ // Battle standard case
+ case SUMMON_TYPE_TOTEM: slot = 254; break;
+ // jewelery statue case, like totem without slot
+ case SUMMON_TYPE_GUARDIAN: slot = 255; break;
+ default: return;
+ }
+
+ if(slot < MAX_TOTEM)
+ {
+ uint64 guid = m_caster->m_TotemSlot[slot];
+ if(guid != 0)
+ {
+ Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid);
+ if(OldTotem && OldTotem->isTotem())
+ ((Totem*)OldTotem)->UnSummon();
+ }
+ }
+
+ uint32 team = 0;
+ if (m_caster->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_caster)->GetTeam();
+
+ Totem* pTotem = new Totem;
+
+ if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_spellInfo->EffectMiscValue[i], team ))
+ {
+ delete pTotem;
+ return;
+ }
+
+ float angle = slot < MAX_TOTEM ? M_PI/MAX_TOTEM - (slot*2*M_PI/MAX_TOTEM) : 0;
+
+ float x,y,z;
+ m_caster->GetClosePoint(x,y,z,pTotem->GetObjectSize(),2.0f,angle);
+
+ // totem must be at same Z in case swimming caster and etc.
+ if( fabs( z - m_caster->GetPositionZ() ) > 5 )
+ z = m_caster->GetPositionZ();
+
+ pTotem->Relocate(x, y, z, m_caster->GetOrientation());
+
+ if(slot < MAX_TOTEM)
+ m_caster->m_TotemSlot[slot] = pTotem->GetGUID();
+
+ pTotem->SetOwner(m_caster->GetGUID());
+ pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized
+
+ int32 duration=GetSpellDuration(m_spellInfo);
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration);
+ pTotem->SetDuration(duration);
+
+ if (damage) // if not spell info, DB values used
+ {
+ pTotem->SetMaxHealth(damage);
+ pTotem->SetHealth(damage);
+ }
+
+ pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id);
+ pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
+
+ pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_MOD_FEAR,true);
+ pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_TRANSFORM,true);
+
+ pTotem->Summon(m_caster);
+
+ if(slot < MAX_TOTEM && m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4);
+ data << uint8(slot);
+ data << uint64(pTotem->GetGUID());
+ data << uint32(duration);
+ data << uint32(m_spellInfo->Id);
+ ((Player*)m_caster)->SendDirectMessage(&data);
+ }
+}
+
+void Spell::EffectEnchantHeldItem(uint32 i)
+{
+ // this is only item spell effect applied to main-hand weapon of target player (players in area)
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* item_owner = (Player*)unitTarget;
+ Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+
+ if(!item )
+ return;
+
+ // must be equipped
+ if(!item ->IsEquipped())
+ return;
+
+ if (m_spellInfo->EffectMiscValue[i])
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ int32 duration = GetSpellDuration(m_spellInfo); //Try duration index first ..
+ if(!duration)
+ duration = m_currentBasePoints[i]+1; //Base points after ..
+ if(!duration)
+ duration = 10; //10 seconds for enchants which don't have listed duration
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ // Always go to temp enchantment slot
+ EnchantmentSlot slot = TEMP_ENCHANTMENT_SLOT;
+
+ // Enchantment will not be applied if a different one already exists
+ if(item->GetEnchantmentId(slot) && item->GetEnchantmentId(slot) != enchant_id)
+ return;
+
+ // Apply the temporary enchantment
+ item->SetEnchantment(slot, enchant_id, duration*1000, 0);
+ item_owner->ApplyEnchantment(item,slot,true);
+ }
+}
+
+void Spell::EffectDisEnchant(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+ if(!itemTarget || !itemTarget->GetProto()->DisenchantID)
+ return;
+
+ p_caster->UpdateCraftSkill(m_spellInfo->Id);
+
+ ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(),LOOT_DISENCHANTING);
+
+ // item will be removed at disenchanting end
+}
+
+void Spell::EffectInebriate(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *player = (Player*)unitTarget;
+ uint16 currentDrunk = player->GetDrunkValue();
+ uint16 drunkMod = damage * 256;
+ if (currentDrunk + drunkMod > 0xFFFF)
+ currentDrunk = 0xFFFF;
+ else
+ currentDrunk += drunkMod;
+ player->SetDrunkValue(currentDrunk, m_CastItem?m_CastItem->GetEntry():0);
+}
+
+void Spell::EffectFeedPet(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ if(!itemTarget)
+ return;
+
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+
+ if(!pet->isAlive())
+ return;
+
+ int32 benefit = pet->GetCurrentFoodBenefitLevel(itemTarget->GetProto()->ItemLevel);
+ if(benefit <= 0)
+ return;
+
+ uint32 count = 1;
+ _player->DestroyItemCount(itemTarget,count,true);
+ // TODO: fix crash when a spell has two effects, both pointed at the same item target
+
+ m_caster->CastCustomSpell(m_caster,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true);
+}
+
+void Spell::EffectDismissPet(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Pet* pet = m_caster->GetPet();
+
+ // not let dismiss dead pet
+ if(!pet||!pet->isAlive())
+ return;
+
+ ((Player*)m_caster)->RemovePet(pet,PET_SAVE_NOT_IN_SLOT);
+}
+
+void Spell::EffectSummonObject(uint32 i)
+{
+ uint32 go_id = m_spellInfo->EffectMiscValue[i];
+
+ uint8 slot = 0;
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: slot = 0; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: slot = 1; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: slot = 2; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: slot = 3; break;
+ default: return;
+ }
+
+ uint64 guid = m_caster->m_ObjectSlot[slot];
+ if(guid != 0)
+ {
+ GameObject* obj = NULL;
+ if( m_caster )
+ obj = ObjectAccessor::GetGameObject(*m_caster, guid);
+
+ if(obj) obj->Delete();
+ m_caster->m_ObjectSlot[slot] = 0;
+ }
+
+ GameObject* pGameObj = new GameObject;
+
+ float rot2 = sin(m_caster->GetOrientation()/2);
+ float rot3 = cos(m_caster->GetOrientation()/2);
+
+ float x,y,z;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
+
+ Map *map = m_caster->GetMap();
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, x, y, z, m_caster->GetOrientation(), 0, 0, rot2, rot3, 0, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel());
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ pGameObj->SetSpellId(m_spellInfo->Id);
+ m_caster->AddGameObject(pGameObj);
+
+ map->Add(pGameObj);
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << pGameObj->GetGUID();
+ m_caster->SendMessageToSet(&data,true);
+
+ m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID();
+}
+
+void Spell::EffectResurrect(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(unitTarget->isAlive())
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ switch (m_spellInfo->Id)
+ {
+ // Defibrillate (Goblin Jumper Cables) have 33% chance on success
+ case 8342:
+ if (roll_chance_i(67))
+ {
+ m_caster->CastSpell(m_caster, 8338, true, m_CastItem);
+ return;
+ }
+ break;
+ // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
+ case 22999:
+ if (roll_chance_i(50))
+ {
+ m_caster->CastSpell(m_caster, 23055, true, m_CastItem);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ Player* pTarget = ((Player*)unitTarget);
+
+ if(pTarget->isRessurectRequested()) // already have one active request
+ return;
+
+ uint32 health = pTarget->GetMaxHealth() * damage / 100;
+ uint32 mana = pTarget->GetMaxPower(POWER_MANA) * damage / 100;
+
+ pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ SendResurrectRequest(pTarget);
+}
+
+void Spell::EffectAddExtraAttacks(uint32 /*i*/)
+{
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+
+ if( unitTarget->m_extraAttacks )
+ return;
+
+ unitTarget->m_extraAttacks = damage;
+}
+
+void Spell::EffectParry(uint32 /*i*/)
+{
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)unitTarget)->SetCanParry(true);
+ }
+}
+
+void Spell::EffectMomentMove(uint32 i)
+{
+ if(unitTarget->isInFlight())
+ return;
+
+ if( m_spellInfo->rangeIndex== 1) //self range
+ {
+ uint32 mapid = m_caster->GetMapId();
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ // before caster
+ float fx,fy,fz;
+ unitTarget->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
+ float ox,oy,oz;
+ unitTarget->GetPosition(ox,oy,oz);
+
+ float fx2,fy2,fz2; // getObjectHitPos overwrite last args in any result case
+ if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5))
+ {
+ fx = fx2;
+ fy = fy2;
+ fz = fz2;
+ unitTarget->UpdateGroundPositionZ(fx,fy,fz);
+ }
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation());
+ }
+}
+
+void Spell::EffectReputation(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)unitTarget;
+
+ int32 rep_change = m_currentBasePoints[i]+1; // field store reputation change -1
+
+ uint32 faction_id = m_spellInfo->EffectMiscValue[i];
+
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
+
+ if(!factionEntry)
+ return;
+
+ _player->ModifyFactionReputation(factionEntry,rep_change);
+}
+
+void Spell::EffectQuestComplete(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ uint32 quest_id = m_spellInfo->EffectMiscValue[i];
+ _player->AreaExploredOrEventHappens(quest_id);
+}
+
+void Spell::EffectSelfResurrect(uint32 i)
+{
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ uint32 health = 0;
+ uint32 mana = 0;
+
+ // flat case
+ if(damage < 0)
+ {
+ health = uint32(-damage);
+ mana = m_spellInfo->EffectMiscValue[i];
+ }
+ // percent case
+ else
+ {
+ health = uint32(damage/100.0f*unitTarget->GetMaxHealth());
+ if(unitTarget->GetMaxPower(POWER_MANA) > 0)
+ mana = uint32(damage/100.0f*unitTarget->GetMaxPower(POWER_MANA));
+ }
+
+ Player *plr = ((Player*)unitTarget);
+ plr->ResurrectPlayer(0.0f);
+
+ plr->SetHealth( health );
+ plr->SetPower(POWER_MANA, mana );
+ plr->SetPower(POWER_RAGE, 0 );
+ plr->SetPower(POWER_ENERGY, plr->GetMaxPower(POWER_ENERGY) );
+
+ plr->SpawnCorpseBones();
+
+ plr->SaveToDB();
+}
+
+void Spell::EffectSkinning(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_UNIT )
+ return;
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Creature* creature = (Creature*) unitTarget;
+ int32 targetLevel = creature->getLevel();
+
+ uint32 skill;
+ if(creature->GetCreatureInfo()->flag1 & 256)
+ skill = SKILL_HERBALISM; // special case
+ else if(creature->GetCreatureInfo()->flag1 & 512)
+ skill = SKILL_MINING; // special case
+ else
+ skill = SKILL_SKINNING; // normal case
+
+ ((Player*)m_caster)->SendLoot(creature->GetGUID(),LOOT_SKINNING);
+ creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5;
+
+ int32 skillValue = ((Player*)m_caster)->GetPureSkillValue(skill);
+
+ // Double chances for elites
+ ((Player*)m_caster)->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1 );
+}
+
+void Spell::EffectCharge(uint32 /*i*/)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ float x, y, z;
+ unitTarget->GetContactPoint(m_caster, x, y, z);
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ ((Creature *)unitTarget)->StopMoving();
+
+ // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
+ m_caster->SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, 1);
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->CreatureRelocation((Creature*)m_caster,x,y,z,m_caster->GetOrientation());
+
+ // not all charge effects used in negative spells
+ if ( !IsPositiveSpell(m_spellInfo->Id))
+ m_caster->Attack(unitTarget,true);
+}
+
+void Spell::EffectSummonCritter(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player* player = (Player*)m_caster;
+
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+
+ Pet* old_critter = player->GetMiniPet();
+
+ // for same pet just despawn
+ if(old_critter && old_critter->GetEntry() == pet_entry)
+ {
+ player->RemoveMiniPet();
+ return;
+ }
+
+ // despawn old pet before summon new
+ if(old_critter)
+ player->RemoveMiniPet();
+
+ // summon new pet
+ Pet* critter = new Pet(MINI_PET);
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!critter->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),
+ map, pet_entry, pet_number))
+ {
+ sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry);
+ delete critter;
+ return;
+ }
+
+ float x,y,z;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(x,y,z,critter->GetObjectSize());
+
+ critter->Relocate(x,y,z,m_caster->GetOrientation());
+
+ if(!critter->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY());
+ delete critter;
+ return;
+ }
+
+ critter->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ critter->SetUInt64Value(UNIT_FIELD_CREATEDBY,m_caster->GetGUID());
+ critter->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ critter->AIM_Initialize();
+ critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter...
+ critter->SetMaxHealth(1);
+ critter->SetHealth(1);
+ critter->SetLevel(1);
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ critter->SetDuration(duration);
+
+ std::string name = player->GetName();
+ name.append(petTypeSuffix[critter->getPetType()]);
+ critter->SetName( name );
+ player->SetMiniPet(critter);
+
+ map->Add((Creature*)critter);
+}
+
+void Spell::EffectKnockBack(uint32 i)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ // Effect only works on players
+ if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ float vsin = sin(m_caster->GetAngle(unitTarget));
+ float vcos = cos(m_caster->GetAngle(unitTarget));
+
+ WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
+ data.append(unitTarget->GetPackGUID());
+ data << uint32(0); // Sequence
+ data << float(vcos); // x direction
+ data << float(vsin); // y direction
+ data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed
+ data << float(damage/-10); // Z Movement speed (vertical)
+
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+void Spell::EffectSendTaxi(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(m_spellInfo->EffectMiscValue[i]);
+ if(!entry)
+ return;
+
+ std::vector<uint32> nodes;
+
+ nodes.resize(2);
+ nodes[0] = entry->from;
+ nodes[1] = entry->to;
+
+ uint32 mountid = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 31606: //Stormcrow Amulet
+ mountid = 17447;
+ break;
+ case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run
+ case 45113: //Quest - Sunwell Daily - Ship Bombing Run
+ case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return
+ mountid = 22840;
+ break;
+ case 34905: //Stealth Flight
+ mountid = 6851;
+ break;
+ }
+
+ ((Player*)unitTarget)->ActivateTaxiPathTo(nodes,mountid);
+
+}
+
+void Spell::EffectPlayerPull(uint32 i)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ // Effect only works on players
+ if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ float vsin = sin(unitTarget->GetAngle(m_caster));
+ float vcos = cos(unitTarget->GetAngle(m_caster));
+
+ WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
+ data.append(unitTarget->GetPackGUID());
+ data << uint32(0); // Sequence
+ data << float(vcos); // x direction
+ data << float(vsin); // y direction
+ // Horizontal speed
+ data << float(damage ? damage : unitTarget->GetDistance2d(m_caster));
+ data << float(m_spellInfo->EffectMiscValue[i])/-10; // Z Movement speed
+
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+void Spell::EffectDispelMechanic(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ uint32 mechanic = m_spellInfo->EffectMiscValue[i];
+
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = sSpellStore.LookupEntry(iter->second->GetSpellProto()->Id);
+ if(spell->Mechanic == mechanic || spell->EffectMechanic[iter->second->GetEffIndex()] == mechanic)
+ {
+ unitTarget->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ return;
+}
+
+void Spell::EffectSummonDeadPet(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player *_player = (Player*)m_caster;
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+ if(pet->isAlive())
+ return;
+ if(damage < 0)
+ return;
+ pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ pet->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+ pet->setDeathState( ALIVE );
+ pet->clearUnitState(UNIT_STAT_ALL_STATE);
+ pet->SetHealth( uint32(pet->GetMaxHealth()*(float(damage)/100)));
+
+ pet->AIM_Initialize();
+
+ _player->PetSpellInitialize();
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+}
+
+void Spell::EffectDestroyAllTotems(uint32 /*i*/)
+{
+ float mana = 0;
+ for(int slot = 0; slot < MAX_TOTEM; ++slot)
+ {
+ if(!m_caster->m_TotemSlot[slot])
+ continue;
+
+ Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_TotemSlot[slot]);
+ if(totem && totem->isTotem())
+ {
+ uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(spellInfo)
+ mana += spellInfo->manaCost * damage / 100;
+ ((Totem*)totem)->UnSummon();
+ }
+ }
+
+ int32 gain = m_caster->ModifyPower(POWER_MANA,int32(mana));
+ m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id, gain, POWER_MANA);
+}
+
+void Spell::EffectDurabilityDamage(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ int32 slot = m_spellInfo->EffectMiscValue[i];
+
+ // FIXME: some spells effects have value -1/-2
+ // Possibly its mean -1 all player equipped items and -2 all items
+ if(slot < 0)
+ {
+ ((Player*)unitTarget)->DurabilityPointsLossAll(damage,(slot < -1));
+ return;
+ }
+
+ // invalid slot value
+ if(slot >= INVENTORY_SLOT_BAG_END)
+ return;
+
+ if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
+ ((Player*)unitTarget)->DurabilityPointsLoss(item,damage);
+}
+
+void Spell::EffectDurabilityDamagePCT(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ int32 slot = m_spellInfo->EffectMiscValue[i];
+
+ // FIXME: some spells effects have value -1/-2
+ // Possibly its mean -1 all player equipped items and -2 all items
+ if(slot < 0)
+ {
+ ((Player*)unitTarget)->DurabilityLossAll(double(damage)/100.0f,(slot < -1));
+ return;
+ }
+
+ // invalid slot value
+ if(slot >= INVENTORY_SLOT_BAG_END)
+ return;
+
+ if(damage <= 0)
+ return;
+
+ if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
+ ((Player*)unitTarget)->DurabilityLoss(item,double(damage)/100.0f);
+}
+
+void Spell::EffectModifyThreatPercent(uint32 /*effIndex*/)
+{
+ if(!unitTarget)
+ return;
+
+ unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage);
+}
+
+void Spell::EffectTransmitted(uint32 effIndex)
+{
+ uint32 name_id = m_spellInfo->EffectMiscValue[effIndex];
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
+
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast",name_id, m_spellInfo->Id);
+ return;
+ }
+
+ float fx,fy,fz;
+
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ fx = m_targets.m_destX;
+ fy = m_targets.m_destY;
+ fz = m_targets.m_destZ;
+ }
+ //FIXME: this can be better check for most objects but still hack
+ else if(m_spellInfo->EffectRadiusIndex[effIndex] && m_spellInfo->speed==0)
+ {
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex]));
+ m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
+ }
+ else
+ {
+ float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ float dis = rand_norm() * (max_dis - min_dis) + min_dis;
+
+ m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
+ }
+
+ Map *cMap = m_caster->GetMap();
+
+ 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
+ { // but this is not proper, we really need to ignore not materialized objects
+ SendCastResult(SPELL_FAILED_NOT_HERE);
+ SendChannelUpdate(0);
+ return;
+ }
+
+ // replace by water level in this case
+ fz = cMap->GetWaterLevel(fx,fy);
+ }
+ // if gameobject is summoning object, it should be spawned right on caster's position
+ else if(goinfo->type==GAMEOBJECT_TYPE_SUMMONING_RITUAL)
+ {
+ m_caster->GetPosition(fx,fy,fz);
+ }
+
+ GameObject* pGameObj = new GameObject;
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap,
+ fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ switch(goinfo->type)
+ {
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ {
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID());
+ // Orientation3
+ pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 2, 0.88431775569915771 );
+ // Orientation4
+ pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 3, -0.4668855369091033 );
+ m_caster->AddGameObject(pGameObj); // will removed at spell cancel
+
+ // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo))
+ // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME)
+ int32 lastSec;
+ switch(urand(0, 3))
+ {
+ case 0: lastSec = 3; break;
+ case 1: lastSec = 7; break;
+ case 2: lastSec = 13; break;
+ case 3: lastSec = 17; break;
+ }
+
+ duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000;
+ break;
+ }
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL:
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ pGameObj->AddUniqueUse((Player*)m_caster);
+ m_caster->AddGameObject(pGameObj); // will removed at spell cancel
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ case GAMEOBJECT_TYPE_CHEST:
+ default:
+ {
+ break;
+ }
+ }
+
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+
+ pGameObj->SetOwnerGUID(m_caster->GetGUID() );
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted\n");
+ //m_caster->AddGameObject(pGameObj);
+ //m_ObjToDel.push_back(pGameObj);
+
+ cMap->Add(pGameObj);
+
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << uint64(pGameObj->GetGUID());
+ m_caster->SendMessageToSet(&data,true);
+
+ if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
+ {
+ GameObject* linkedGO = new GameObject;
+ if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap,
+ fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ linkedGO->SetSpellId(m_spellInfo->Id);
+ linkedGO->SetOwnerGUID(m_caster->GetGUID() );
+
+ MapManager::Instance().GetMap(linkedGO->GetMapId(), linkedGO)->Add(linkedGO);
+ }
+ else
+ {
+ delete linkedGO;
+ linkedGO = NULL;
+ return;
+ }
+ }
+}
+
+void Spell::EffectProspecting(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+ if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
+ return;
+
+ if(itemTarget->GetCount() < 5)
+ return;
+
+ if( sWorld.getConfig(CONFIG_SKILL_PROSPECTING))
+ {
+ uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_JEWELCRAFTING);
+ uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
+ p_caster->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue);
+ }
+
+ ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING);
+}
+
+void Spell::EffectSkill(uint32 /*i*/)
+{
+ sLog.outDebug("WORLD: SkillEFFECT");
+}
+
+void Spell::EffectSummonDemon(uint32 i)
+{
+ float px = m_targets.m_destX;
+ float py = m_targets.m_destY;
+ float pz = m_targets.m_destZ;
+
+ Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,3600000);
+ if (!Charmed)
+ return;
+
+ // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster?
+ Charmed->SetLevel(m_caster->getLevel());
+
+ // TODO: Add damage/mana/hp according to level
+
+ if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon
+ {
+ // Enslave demon effect, without mana cost and cooldown
+ m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved
+
+ // Inferno effect
+ Charmed->CastSpell(Charmed, 22703, true, 0);
+ }
+}
+
+/* There is currently no need for this effect. We handle it in BattleGround.cpp
+ If we would handle the resurrection here, the spiritguide would instantly disappear as the
+ player revives, and so we wouldn't see the spirit heal visual effect on the npc.
+ This is why we use a half sec delay between the visual effect and the resurrection itself */
+void Spell::EffectSpiritHeal(uint32 /*i*/)
+{
+ /*
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ //m_spellInfo->EffectBasePoints[i]; == 99 (percent?)
+ //((Player*)unitTarget)->setResurrect(m_caster->GetGUID(), unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), unitTarget->GetMaxHealth(), unitTarget->GetMaxPower(POWER_MANA));
+ ((Player*)unitTarget)->ResurrectPlayer(1.0f);
+ ((Player*)unitTarget)->SpawnCorpseBones();
+ */
+}
+
+// remove insignia spell effect
+void Spell::EffectSkinPlayerCorpse(uint32 /*i*/)
+{
+ sLog.outDebug("Effect: SkinPlayerCorpse");
+ if ( (m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive()) )
+ return;
+
+ ((Player*)unitTarget)->RemovedInsignia( (Player*)m_caster );
+}
+
+void Spell::EffectStealBeneficialBuff(uint32 i)
+{
+ sLog.outDebug("Effect: StealBeneficialBuff");
+
+ if(!unitTarget || unitTarget==m_caster) // can't steal from self
+ return;
+
+ std::vector <Aura *> steal_list;
+ // Create dispel mask by dispel type
+ uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) );
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ Aura *aur = (*itr).second;
+ if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
+ {
+ // Need check for passive? this
+ if (aur->IsPositive() && !aur->IsPassive())
+ steal_list.push_back(aur);
+ }
+ }
+ // Ok if exist some buffs for dispel try dispel it
+ if (!steal_list.empty())
+ {
+ std::list < std::pair<uint32,uint64> > success_list;
+ int32 list_size = steal_list.size();
+ // Dispell N = damage buffs (or while exist buffs for dispel)
+ for (int32 count=0; count < damage && list_size > 0; ++count)
+ {
+ // Random select buff for dispel
+ Aura *aur = steal_list[urand(0, list_size-1)];
+ // Not use chance for steal
+ // TODO possible need do it
+ success_list.push_back( std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
+
+ // Remove buff from list for prevent doubles
+ for (std::vector<Aura *>::iterator j = steal_list.begin(); j != steal_list.end(); )
+ {
+ Aura *stealed = *j;
+ if (stealed->GetId() == aur->GetId() && stealed->GetCasterGUID() == aur->GetCasterGUID())
+ {
+ j = steal_list.erase(j);
+ --list_size;
+ }
+ else
+ ++j;
+ }
+ }
+ // Really try steal and send log
+ if (!success_list.empty())
+ {
+ int32 count = success_list.size();
+ WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+count*5);
+ data.append(unitTarget->GetPackGUID()); // Victim GUID
+ data.append(m_caster->GetPackGUID()); // Caster GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ data << uint8(0); // not used
+ data << uint32(count); // count
+ for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
+ data << uint32(spellInfo->Id); // Spell Id
+ data << uint8(0); // 0 - steals !=0 transfers
+ unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster);
+ }
+ m_caster->SendMessageToSet(&data, true);
+ }
+ }
+}
+
+void Spell::EffectKillCredit(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->KilledMonster(m_spellInfo->EffectMiscValue[i], 0);
+}
+
+void Spell::EffectQuestFail(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]);
+}
diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp
new file mode 100644
index 00000000000..7bad99b38b5
--- /dev/null
+++ b/src/game/SpellHandler.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DBCStores.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Spell.h"
+#include "SpellAuras.h"
+#include "BattleGround.h"
+#include "MapManager.h"
+#include "ScriptCalls.h"
+#include "Totem.h"
+
+void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
+{
+ // TODO: add targets.read() check
+ CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8);
+
+ Player* pUser = _player;
+ uint8 bagIndex, slot;
+ uint8 spell_count; // number of spells at item, not used
+ uint8 cast_count; // next cast if exists (single or not)
+ uint64 item_guid;
+
+ recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid;
+
+ Item *pItem = pUser->GetItemByPos(bagIndex, slot);
+ if(!pItem)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size());
+
+ ItemPrototype const *proto = pItem->GetProto();
+ if(!proto)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
+ return;
+ }
+
+ // some item classes can be used only in equipped state
+ if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
+ return;
+ }
+
+ uint8 msg = pUser->CanUseItem(pItem);
+ if( msg != EQUIP_ERR_OK )
+ {
+ pUser->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
+ if( proto->Class == ITEM_CLASS_CONSUMABLE &&
+ !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
+ pUser->InArena())
+ {
+ pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
+ return;
+ }
+
+ if (pUser->isInCombat())
+ {
+ for(int i = 0; i < 5; ++i)
+ {
+ if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
+ {
+ if (IsNonCombatSpell(spellInfo))
+ {
+ pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
+ return;
+ }
+ }
+ }
+ }
+
+ // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
+ if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
+ {
+ if (!pItem->IsSoulBound())
+ {
+ pItem->SetState(ITEM_CHANGED, pUser);
+ pItem->SetBinding( true );
+ }
+ }
+
+ SpellCastTargets targets;
+ if(!targets.read(&recvPacket, pUser))
+ return;
+
+ //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
+ if(!Script->ItemUse(pUser,pItem,targets))
+ {
+ // no script or script not process request by self
+
+ // special learning case
+ if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN)
+ {
+ uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN);
+ if(!spellInfo)
+ {
+ sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN);
+ pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL);
+ return;
+ }
+
+ Spell *spell = new Spell(pUser, spellInfo, false);
+ spell->m_CastItem = pItem;
+ spell->m_cast_count = cast_count; //set count of casts
+ spell->m_currentBasePoints[0] = learning_spell_id;
+ spell->prepare(&targets);
+ return;
+ }
+
+ // use triggered flag only for items with many spell casts and for not first cast
+ int count = 0;
+
+ for(int i = 0; i < 5; ++i)
+ {
+ _Spell const& spellData = pItem->GetProto()->Spells[i];
+
+ // no spell
+ if(!spellData.SpellId)
+ continue;
+
+ // wrong triggering type
+ if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
+ if(!spellInfo)
+ {
+ sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId);
+ continue;
+ }
+
+ Spell *spell = new Spell(pUser, spellInfo, (count > 0));
+ spell->m_CastItem = pItem;
+ spell->m_cast_count = cast_count; //set count of casts
+ spell->prepare(&targets);
+
+ ++count;
+ }
+ }
+}
+
+#define OPEN_CHEST 11437
+#define OPEN_SAFE 11535
+#define OPEN_CAGE 11792
+#define OPEN_BOOTY_CHEST 5107
+#define OPEN_STRONGBOX 8517
+
+void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size());
+
+ Player* pUser = _player;
+ uint8 bagIndex, slot;
+
+ recvPacket >> bagIndex >> slot;
+
+ sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
+
+ Item *pItem = pUser->GetItemByPos(bagIndex, slot);
+ if(!pItem)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ ItemPrototype const *proto = pItem->GetProto();
+ if(!proto)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
+ return;
+ }
+
+ // locked item
+ uint32 lockId = proto->LockID;
+ if(lockId)
+ {
+ LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+
+ if (!lockInfo)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
+ sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
+ return;
+ }
+
+ // required picklocking
+ if(lockInfo->requiredlockskill || lockInfo->requiredminingskill)
+ {
+ pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
+ return;
+ }
+ }
+
+ if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
+ if (result)
+ {
+ Field *fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
+ uint32 flags = fields[1].GetUInt32();
+
+ pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
+ pItem->SetUInt32Value(OBJECT_FIELD_ENTRY, entry);
+ pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
+ pItem->SetState(ITEM_CHANGED, pUser);
+ delete result;
+ }
+ else
+ {
+ sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
+ pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
+ return;
+ }
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
+ }
+ else
+ pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
+}
+
+void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ uint32 spellId = OPEN_CHEST;
+
+ recv_data >> guid;
+
+ sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", guid);
+ GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid);
+
+ if(!obj)
+ return;
+
+ if (Script->GOHello(_player, obj))
+ return;
+
+ obj->Use(_player);
+}
+
+void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4+1+2);
+
+ uint32 spellId;
+ uint8 cast_count;
+ recvPacket >> spellId;
+ recvPacket >> cast_count;
+
+ sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i",
+ spellId, cast_count, recvPacket.size());
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %u", spellId);
+ return;
+ }
+
+ // not have spell or spell passive and not casted by client
+ if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) )
+ {
+ //cheater? kick? ban?
+ return;
+ }
+
+ // client provided targets
+ SpellCastTargets targets;
+ if(!targets.read(&recvPacket,_player))
+ return;
+
+ // auto-selection buff level base at target level (in spellInfo)
+ if(targets.getUnitTarget())
+ {
+ SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
+
+ // if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
+ if(actualSpellInfo)
+ spellInfo = actualSpellInfo;
+ }
+
+ Spell *spell = new Spell(_player, spellInfo, false);
+ spell->m_cast_count = cast_count; //set count of casts
+ spell->prepare(&targets);
+}
+
+void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4);
+
+ uint32 spellId;
+ recvPacket >> spellId;
+
+ //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
+ if(spellId==26679)
+ return;
+
+ if(_player->IsNonMeleeSpellCasted(false))
+ _player->InterruptNonMeleeSpells(false,spellId);
+}
+
+void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4);
+
+ uint32 spellId;
+ recvPacket >> spellId;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo)
+ return;
+
+ // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
+ if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
+ return;
+
+ _player->RemoveAurasDueToSpellByCancel(spellId);
+
+ if (spellId == 2584) // Waiting to resurrect spell cancel, we must remove player from resurrect queue
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(!bg)
+ return;
+ bg->RemovePlayerFromResurrectQueue(_player->GetGUID());
+ }
+}
+
+void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 8+4);
+
+ uint64 guid;
+ uint32 spellId;
+
+ recvPacket >> guid;
+ recvPacket >> spellId;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %u", spellId);
+ return;
+ }
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
+
+ if(!pet)
+ {
+ sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
+ {
+ sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
+ return;
+ }
+
+ if(!pet->isAlive())
+ {
+ pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
+ return;
+ }
+
+ pet->RemoveAurasDueToSpell(spellId);
+
+ pet->AddCreatureSpellCooldown(spellId);
+}
+
+void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
+{
+ // nothing do
+}
+
+void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
+{
+ // may be better send SMSG_CANCEL_AUTO_REPEAT?
+ // cancel and prepare for deleting
+ _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+}
+
+/// \todo Complete HandleCancelChanneling function
+void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
+{
+ /*
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ uint32 spellid;
+ recv_data >> spellid;
+ */
+}
+
+void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket, 1);
+
+ uint8 slotId;
+
+ recvPacket >> slotId;
+
+ if (slotId >= MAX_TOTEM)
+ return;
+
+ if(!_player->m_TotemSlot[slotId])
+ return;
+
+ Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]);
+ if(totem && totem->isTotem())
+ ((Totem*)totem)->UnSummon();
+}
+
+void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
+{
+ sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
+
+ if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
+ if(spellInfo)
+ _player->CastSpell(_player,spellInfo,false,0);
+
+ _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
+ }
+}
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
new file mode 100644
index 00000000000..fc8c5d43b7b
--- /dev/null
+++ b/src/game/SpellMgr.cpp
@@ -0,0 +1,2268 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "SpellMgr.h"
+#include "ObjectMgr.h"
+#include "SpellAuraDefines.h"
+#include "ProgressBar.h"
+#include "Database/DBCStores.h"
+#include "World.h"
+#include "Chat.h"
+#include "Spell.h"
+
+SpellMgr::SpellMgr()
+{
+}
+
+SpellMgr::~SpellMgr()
+{
+}
+
+SpellMgr& SpellMgr::Instance()
+{
+ static SpellMgr spellMgr;
+ return spellMgr;
+}
+
+int32 GetSpellDuration(SpellEntry const *spellInfo)
+{
+ if(!spellInfo)
+ return 0;
+ SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
+ if(!du)
+ return 0;
+ return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]);
+}
+
+int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
+{
+ if(!spellInfo)
+ return 0;
+ SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
+ if(!du)
+ return 0;
+ return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]);
+}
+
+uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell)
+{
+ SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex);
+
+ // not all spells have cast time index and this is all is pasiive abilities
+ if(!spellCastTimeEntry)
+ return 0;
+
+ int32 castTime = spellCastTimeEntry->CastTime;
+
+ if (spell)
+ {
+ if(Player* modOwner = spell->GetCaster()->GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell);
+
+ if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) )
+ castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED));
+ else
+ {
+ if (spell->IsRangedSpell() && !spell->IsAutoRepeat())
+ castTime = int32(castTime * spell->GetCaster()->m_modAttackSpeedPct[RANGED_ATTACK]);
+ }
+ }
+
+ if (spellInfo->Attributes & SPELL_ATTR_RANGED && (!spell || !(spell->IsAutoRepeat())))
+ castTime += 500;
+
+ return (castTime > 0) ? uint32(castTime) : 0;
+}
+
+bool IsPassiveSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo)
+ return false;
+ return (spellInfo->Attributes & SPELL_ATTR_PASSIVE) != 0;
+}
+
+bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2)
+{
+ SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
+ SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+ if(!spellInfo_1 || !spellInfo_2) return false;
+ if(spellInfo_1->Id == spellId_2) return false;
+
+ if (spellInfo_1->Effect[effIndex_1] != spellInfo_2->Effect[effIndex_2] ||
+ spellInfo_1->EffectItemType[effIndex_1] != spellInfo_2->EffectItemType[effIndex_2] ||
+ spellInfo_1->EffectMiscValue[effIndex_1] != spellInfo_2->EffectMiscValue[effIndex_2] ||
+ spellInfo_1->EffectApplyAuraName[effIndex_1] != spellInfo_2->EffectApplyAuraName[effIndex_2])
+ return false;
+
+ return true;
+}
+
+int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2)
+{
+ SpellEntry const*spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
+ SpellEntry const*spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+ if(!spellInfo_1 || !spellInfo_2) return 0;
+ if (spellId_1 == spellId_2) return 0;
+
+ int32 diff = spellInfo_1->EffectBasePoints[effIndex_1] - spellInfo_2->EffectBasePoints[effIndex_2];
+ if (spellInfo_1->EffectBasePoints[effIndex_1]+1 < 0 && spellInfo_2->EffectBasePoints[effIndex_2]+1 < 0) return -diff;
+ else return diff;
+}
+
+SpellSpecific GetSpellSpecific(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return SPELL_NORMAL;
+
+ switch(spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ {
+ // family flags 18(Molten), 25(Frost/Ice), 28(Mage)
+ if (spellInfo->SpellFamilyFlags & 0x12040000)
+ return SPELL_MAGE_ARMOR;
+
+ if ((spellInfo->SpellFamilyFlags & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
+ return SPELL_MAGE_POLYMORPH;
+
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ if (spellInfo->SpellFamilyFlags & 0x00008000010000LL)
+ return SPELL_POSITIVE_SHOUT;
+
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // only warlock curses have this
+ if (spellInfo->Dispel == DISPEL_CURSE)
+ return SPELL_CURSE;
+
+ // family flag 37 (only part spells have family name)
+ if (spellInfo->SpellFamilyFlags & 0x2000000000LL)
+ return SPELL_WARLOCK_ARMOR;
+
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // only hunter stings have this
+ if (spellInfo->Dispel == DISPEL_POISON)
+ return SPELL_STING;
+
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ if (IsSealSpell(spellInfo))
+ return SPELL_SEAL;
+
+ if (spellInfo->SpellFamilyFlags & 0x10000100LL)
+ return SPELL_BLESSING;
+
+ if ((spellInfo->SpellFamilyFlags & 0x00000820180400LL) && (spellInfo->AttributesEx3 & 0x200))
+ return SPELL_JUDGEMENT;
+
+ for (int i = 0; i < 3; i++)
+ {
+ // only paladin auras have this
+ if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY)
+ return SPELL_AURA;
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ if (IsElementalShield(spellInfo))
+ return SPELL_ELEMENTAL_SHIELD;
+
+ break;
+ }
+
+ case SPELLFAMILY_POTION:
+ return spellmgr.GetSpellElixirSpecific(spellInfo->Id);
+ }
+
+ // only warlock armor/skin have this (in additional to family cases)
+ if( spellInfo->SpellVisual == 130 && spellInfo->SpellIconID == 89)
+ {
+ return SPELL_WARLOCK_ARMOR;
+ }
+
+ // only hunter aspects have this (but not all aspects in hunter family)
+ if( spellInfo->activeIconID == 122 && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NATURE) &&
+ (spellInfo->Attributes & 0x50000) != 0 && (spellInfo->Attributes & 0x9000010) == 0)
+ {
+ return SPELL_ASPECT;
+ }
+
+ for(int i = 0; i < 3; i++)
+ if( spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && (
+ spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_CREATURES ||
+ spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_RESOURCES ||
+ spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_STEALTHED ) )
+ return SPELL_TRACKER;
+
+ // elixirs can have different families, but potion most ofc.
+ if(SpellSpecific sp = spellmgr.GetSpellElixirSpecific(spellInfo->Id))
+ return sp;
+
+ return SPELL_NORMAL;
+}
+
+bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2)
+{
+ switch(spellSpec1)
+ {
+ case SPELL_SEAL:
+ case SPELL_BLESSING:
+ case SPELL_AURA:
+ case SPELL_STING:
+ case SPELL_CURSE:
+ case SPELL_ASPECT:
+ case SPELL_TRACKER:
+ case SPELL_WARLOCK_ARMOR:
+ case SPELL_MAGE_ARMOR:
+ case SPELL_MAGE_POLYMORPH:
+ case SPELL_POSITIVE_SHOUT:
+ case SPELL_JUDGEMENT:
+ return spellSpec1==spellSpec2;
+ case SPELL_BATTLE_ELIXIR:
+ return spellSpec2==SPELL_BATTLE_ELIXIR
+ || spellSpec2==SPELL_FLASK_ELIXIR;
+ case SPELL_GUARDIAN_ELIXIR:
+ return spellSpec2==SPELL_GUARDIAN_ELIXIR
+ || spellSpec2==SPELL_FLASK_ELIXIR;
+ case SPELL_FLASK_ELIXIR:
+ return spellSpec2==SPELL_BATTLE_ELIXIR
+ || spellSpec2==SPELL_GUARDIAN_ELIXIR
+ || spellSpec2==SPELL_FLASK_ELIXIR;
+ default:
+ return false;
+ }
+}
+
+bool IsPositiveTarget(uint32 targetA, uint32 targetB)
+{
+ // non-positive targets
+ switch(targetA)
+ {
+ case TARGET_CHAIN_DAMAGE:
+ case TARGET_ALL_ENEMY_IN_AREA:
+ case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
+ case TARGET_IN_FRONT_OF_CASTER:
+ case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
+ case TARGET_CURRENT_ENEMY_COORDINATES:
+ case TARGET_SINGLE_ENEMY:
+ return false;
+ case TARGET_ALL_AROUND_CASTER:
+ return (targetB == TARGET_ALL_PARTY || targetB == TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER);
+ default:
+ break;
+ }
+ if (targetB)
+ return IsPositiveTarget(targetB, 0);
+ return true;
+}
+
+bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ switch(spellId)
+ {
+ case 23333: // BG spell
+ case 23335: // BG spell
+ case 34976: // BG spell
+ return true;
+ case 28441: // not positive dummy spell
+ case 37675: // Chaos Blast
+ return false;
+ }
+
+ switch(spellproto->Effect[effIndex])
+ {
+ // always positive effects (check before target checks that provided non-positive result in some case for positive effects)
+ case SPELL_EFFECT_HEAL:
+ case SPELL_EFFECT_LEARN_SPELL:
+ case SPELL_EFFECT_SKILL_STEP:
+ case SPELL_EFFECT_HEAL_PCT:
+ case SPELL_EFFECT_ENERGIZE_PCT:
+ return true;
+
+ // non-positive aura use
+ case SPELL_EFFECT_APPLY_AURA:
+ case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+ {
+ switch(spellproto->EffectApplyAuraName[effIndex])
+ {
+ case SPELL_AURA_DUMMY:
+ {
+ // dummy aura can be positive or negative dependent from casted spell
+ switch(spellproto->Id)
+ {
+ case 13139: // net-o-matic special effect
+ case 23445: // evil twin
+ return false;
+ default:
+ break;
+ }
+ } break;
+ case SPELL_AURA_MOD_STAT:
+ case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative)
+ case SPELL_AURA_MOD_HEALING_DONE:
+ {
+ if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ return false;
+ break;
+ }
+ case SPELL_AURA_ADD_TARGET_TRIGGER:
+ return true;
+ case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
+ if(spellId != spellproto->EffectTriggerSpell[effIndex])
+ {
+ uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
+ SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
+
+ if(spellTriggeredProto)
+ {
+ // non-positive targets of main spell return early
+ for(int i = 0; i < 3; ++i)
+ {
+ // 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))
+ return false;
+ }
+ }
+ }
+ break;
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example)
+ break;
+ case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment.
+ if(effIndex==0 && spellproto->Effect[1]==0 && spellproto->Effect[2]==0)
+ return false; // but all single stun aura spells is negative
+
+ // Petrification
+ if(spellproto->Id == 17624)
+ return false;
+ break;
+ case SPELL_AURA_MOD_ROOT:
+ case SPELL_AURA_MOD_SILENCE:
+ case SPELL_AURA_GHOST:
+ case SPELL_AURA_PERIODIC_LEECH:
+ case SPELL_AURA_MOD_PACIFY_SILENCE:
+ case SPELL_AURA_MOD_STALKED:
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ return false;
+ case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also.
+ // part of negative spell if casted at self (prevent cancel)
+ if(spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF)
+ return false;
+ break;
+ case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also
+ // part of positive spell if casted at self
+ if(spellproto->EffectImplicitTargetA[effIndex] != TARGET_SELF)
+ return false;
+ // but not this if this first effect (don't found batter check)
+ if(spellproto->Attributes & 0x4000000 && effIndex==0)
+ return false;
+ break;
+ case SPELL_AURA_TRANSFORM:
+ // some spells negative
+ switch(spellproto->Id)
+ {
+ case 36897: // Transporter Malfunction (race mutation to horde)
+ case 36899: // Transporter Malfunction (race mutation to alliance)
+ return false;
+ }
+ break;
+ case SPELL_AURA_MOD_SCALE:
+ // some spells negative
+ switch(spellproto->Id)
+ {
+ case 36900: // Soul Split: Evil!
+ case 36901: // Soul Split: Good
+ case 36893: // Transporter Malfunction (decrease size case)
+ case 36895: // Transporter Malfunction (increase size case)
+ return false;
+ }
+ break;
+ case SPELL_AURA_MECHANIC_IMMUNITY:
+ {
+ // non-positive immunities
+ switch(spellproto->EffectMiscValue[effIndex])
+ {
+ case MECHANIC_BANDAGE:
+ case MECHANIC_SHIELD:
+ case MECHANIC_MOUNT:
+ case MECHANIC_INVULNERABILITY:
+ return false;
+ default:
+ break;
+ }
+ } break;
+ case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
+ case SPELL_AURA_ADD_PCT_MODIFIER:
+ {
+ // non-positive mods
+ switch(spellproto->EffectMiscValue[effIndex])
+ {
+ case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
+ if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) > 0)
+ return false;
+ break;
+ default:
+ break;
+ }
+ } break;
+ case SPELL_AURA_MOD_HEALING_PCT:
+ if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ return false;
+ break;
+ case SPELL_AURA_MOD_SKILL:
+ if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ return false;
+ break;
+ case SPELL_AURA_FORCE_REACTION:
+ if(spellproto->Id==42792) // Recently Dropped Flag (prevent cancel)
+ return false;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // non-positive targets
+ if(!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]))
+ return false;
+
+ // AttributesEx check
+ if(spellproto->AttributesEx & (1<<7))
+ return false;
+
+ // ok, positive
+ return true;
+}
+
+bool IsPositiveSpell(uint32 spellId)
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ // spells with atleast 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))
+ return false;
+ return true;
+}
+
+bool IsSingleTargetSpell(SpellEntry const *spellInfo)
+{
+ // all other single target spells have if it has AttributesEx5
+ if ( spellInfo->AttributesEx5 & SPELL_ATTR_EX5_SINGLE_TARGET_SPELL )
+ return true;
+
+ // TODO - need found Judgements rule
+ switch(GetSpellSpecific(spellInfo->Id))
+ {
+ case SPELL_JUDGEMENT:
+ return true;
+ }
+
+ // single target triggered spell.
+ // Not real client side single target spell, but it' not triggered until prev. aura expired.
+ // This is allow store it in single target spells list for caster for spell proc checking
+ if(spellInfo->Id==38324) // Regeneration (triggered by 38299 (HoTs on Heals))
+ return true;
+
+ return false;
+}
+
+bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2)
+{
+ // TODO - need better check
+ // Equal icon and spellfamily
+ if( spellInfo1->SpellFamilyName == spellInfo2->SpellFamilyName &&
+ spellInfo1->SpellIconID == spellInfo2->SpellIconID )
+ return true;
+
+ // TODO - need found Judgements rule
+ SpellSpecific spec1 = GetSpellSpecific(spellInfo1->Id);
+ // spell with single target specific types
+ switch(spec1)
+ {
+ case SPELL_JUDGEMENT:
+ if(GetSpellSpecific(spellInfo2->Id) == spec1)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
+{
+ // talents that learn spells can have stance requirements that need ignore
+ // (this requirement only for client-side stance show in talent description)
+ if( GetTalentSpellCost(spellInfo->Id) > 0 &&
+ (spellInfo->Effect[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) )
+ return 0;
+
+ uint32 stanceMask = (form ? 1 << (form - 1) : 0);
+
+ if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance
+ return SPELL_FAILED_NOT_SHAPESHIFT;
+
+ if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance
+ return 0;
+
+ bool actAsShifted = false;
+ if (form > 0)
+ {
+ SpellShapeshiftEntry const *shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
+ if (!shapeInfo)
+ {
+ sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form);
+ return 0;
+ }
+ actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells
+ }
+
+ if(actAsShifted)
+ {
+ if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted
+ return SPELL_FAILED_NOT_SHAPESHIFT;
+ else if (spellInfo->Stances != 0) // needs other shapeshift
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ }
+ else
+ {
+ // needs shapeshift
+ if(!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0)
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ }
+
+ return 0;
+}
+
+void SpellMgr::LoadSpellTargetPositions()
+{
+ mSpellTargetPositions.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5
+ QueryResult *result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell target coordinates", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ ++count;
+
+ uint32 Spell_ID = fields[0].GetUInt32();
+
+ SpellTargetPosition st;
+
+ st.target_mapId = fields[1].GetUInt32();
+ st.target_X = fields[2].GetFloat();
+ st.target_Y = fields[3].GetFloat();
+ st.target_Z = fields[4].GetFloat();
+ st.target_Orientation = fields[5].GetFloat();
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(Spell_ID);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.",Spell_ID);
+ continue;
+ }
+
+ bool found = false;
+ for(int i = 0; i < 3; ++i)
+ {
+ if( spellInfo->EffectImplicitTargetA[i]==TARGET_TABLE_X_Y_Z_COORDINATES || spellInfo->EffectImplicitTargetB[i]==TARGET_TABLE_X_Y_Z_COORDINATES )
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_TABLE_X_Y_Z_COORDINATES (17).",Spell_ID);
+ continue;
+ }
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId);
+ if(!mapEntry)
+ {
+ sLog.outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Spell_ID,st.target_mapId);
+ continue;
+ }
+
+ if(st.target_X==0 && st.target_Y==0 && st.target_Z==0)
+ {
+ sLog.outErrorDb("Spell (ID:%u) target coordinates not provided.",Spell_ID);
+ continue;
+ }
+
+ mSpellTargetPositions[Spell_ID] = st;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell teleport coordinates", count );
+}
+
+void SpellMgr::LoadSpellAffects()
+{
+ mSpellAffectMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellFamilyMask 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;
+ }
+
+ if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA ||
+ spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER &&
+ spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER &&
+ spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER )
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_affect` have not SPELL_AURA_ADD_FLAT_MODIFIER (%u) or SPELL_AURA_ADD_PCT_MODIFIER (%u) or SPELL_AURA_ADD_TARGET_TRIGGER (%u) for effect index (%u)", entry,SPELL_AURA_ADD_FLAT_MODIFIER,SPELL_AURA_ADD_PCT_MODIFIER,SPELL_AURA_ADD_TARGET_TRIGGER,effectId);
+ continue;
+ }
+
+ uint64 spellAffectMask = fields[2].GetUInt64();
+
+ // Spell.dbc have own data for low part of SpellFamilyMask
+ if( spellInfo->EffectItemType[effectId])
+ {
+ if(spellInfo->EffectItemType[effectId] == spellAffectMask)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectItemType%d) data for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId);
+ continue;
+ }
+
+ // 24429 have wrong data in EffectItemType and overwrites by DB, possible bug in client
+ if(spellInfo->Id!=24429 && spellInfo->EffectItemType[effectId] != spellAffectMask)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_affect` have different low part from EffectItemType%d for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId);
+ continue;
+ }
+ }
+
+ mSpellAffectMap.insert(SpellAffectMap::value_type((entry<<8) + effectId,spellAffectMask));
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell affect definitions", count );
+
+ /*
+ // Commented for now, as it still produces many errors (still quite many spells miss spell_affect)
+ for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
+ if (!spellInfo)
+ continue;
+
+ for (int effectId = 0; effectId < 3; ++effectId)
+ {
+ if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA ||
+ (spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER &&
+ spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER &&
+ spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER) )
+ continue;
+
+ if(spellInfo->EffectItemType[effectId] != 0)
+ continue;
+
+ if(mSpellAffectMap.find((id<<8) + effectId) != mSpellAffectMap.end())
+ continue;
+
+ sLog.outErrorDb("Spell %u (%s) misses spell_affect for effect %u",id,spellInfo->SpellName[sWorld.GetDBClang()], effectId);
+ }
+ }
+ */
+}
+
+bool SpellMgr::IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const
+{
+ // false for spellInfo == NULL
+ if (!spellInfo)
+ return false;
+
+ SpellEntry const *affect_spell = sSpellStore.LookupEntry(spellId);
+ // false for affect_spell == NULL
+ if (!affect_spell)
+ return false;
+
+ // False if spellFamily not equal
+ if (affect_spell->SpellFamilyName != spellInfo->SpellFamilyName)
+ return false;
+
+ // If familyFlags == 0
+ if (!familyFlags)
+ {
+ // Get it from spellAffect table
+ familyFlags = GetSpellAffectMask(spellId,effectId);
+ // false if familyFlags == 0
+ if (!familyFlags)
+ return false;
+ }
+
+ // true
+ if (familyFlags & spellInfo->SpellFamilyFlags)
+ return true;
+
+ return false;
+}
+
+void SpellMgr::LoadSpellProcEvents()
+{
+ mSpellProcEventMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, Category, SkillID, SpellFamilyName, SpellFamilyMask, procFlags, ppmRate, cooldown FROM spell_proc_event");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell proc event conditions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint16 entry = fields[0].GetUInt16();
+
+ if (!sSpellStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
+ continue;
+ }
+
+ SpellProcEventEntry spe;
+
+ spe.schoolMask = fields[1].GetUInt32();
+ spe.category = fields[2].GetUInt32();
+ spe.skillId = fields[3].GetUInt32();
+ spe.spellFamilyName = fields[4].GetUInt32();
+ spe.spellFamilyMask = fields[5].GetUInt64();
+ spe.procFlags = fields[6].GetUInt32();
+ spe.ppmRate = fields[7].GetFloat();
+ spe.cooldown = fields[8].GetUInt32();
+
+ mSpellProcEventMap[entry] = spe;
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell proc event conditions", count );
+
+ /*
+ // Commented for now, as it still produces many errors (still quite many spells miss spell_proc_event)
+ for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
+ if (!spellInfo)
+ continue;
+
+ bool found = false;
+ for (int effectId = 0; effectId < 3; ++effectId)
+ {
+ // at this moment check only SPELL_AURA_PROC_TRIGGER_SPELL
+ if( spellInfo->EffectApplyAuraName[effectId] == SPELL_AURA_PROC_TRIGGER_SPELL )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+
+ if(GetSpellProcEvent(id))
+ continue;
+
+ sLog.outErrorDb("Spell %u (%s) misses spell_proc_event",id,spellInfo->SpellName[sWorld.GetDBClang()]);
+ }
+ */
+}
+
+bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags )
+{
+ if((procFlags & spellProcEvent->procFlags) == 0)
+ return false;
+
+ // Additional checks in case spell cast/hit/crit is the event
+ // Check (if set) school, category, skill line, spell talent mask
+ if(spellProcEvent->schoolMask && (!procSpell || (GetSpellSchoolMask(procSpell) & spellProcEvent->schoolMask) == 0))
+ return false;
+ if(spellProcEvent->category && (!procSpell || procSpell->Category != spellProcEvent->category))
+ return false;
+ if(spellProcEvent->skillId)
+ {
+ if (!procSpell)
+ return false;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(procSpell->Id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(procSpell->Id);
+
+ bool found = false;
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(_spell_idx->second->skillId == spellProcEvent->skillId)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return false;
+ }
+ if(spellProcEvent->spellFamilyName && (!procSpell || spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
+ return false;
+ if(spellProcEvent->spellFamilyMask && (!procSpell || (spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0))
+ return false;
+
+ return true;
+}
+
+void SpellMgr::LoadSpellElixirs()
+{
+ mSpellElixirs.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1
+ QueryResult *result = WorldDatabase.Query("SELECT entry, mask FROM spell_elixir");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell elixir definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint16 entry = fields[0].GetUInt16();
+ uint8 mask = fields[1].GetUInt8();
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry);
+
+ if (!spellInfo)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_elixir` does not exist", entry);
+ continue;
+ }
+
+ mSpellElixirs[entry] = mask;
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell elixir definitions", count );
+}
+
+void SpellMgr::LoadSpellThreats()
+{
+ sSpellThreatStore.Free(); // for reload
+
+ sSpellThreatStore.Load();
+
+ sLog.outString( ">> Loaded %u aggro generating spells", sSpellThreatStore.RecordCount );
+ sLog.outString();
+}
+
+bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const
+{
+ SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+ if(!spellInfo_1 || !spellInfo_2) return false;
+ if(spellInfo_1->Id == spellId_2) return false;
+
+ return GetFirstSpellInChain(spellInfo_1->Id)==GetFirstSpellInChain(spellId_2);
+}
+
+bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
+{
+ if(spellInfo->powerType != POWER_MANA && spellInfo->powerType != POWER_HEALTH)
+ return false;
+ if(IsProfessionSpell(spellInfo->Id))
+ return false;
+
+ // All stance spells. if any better way, change it.
+ for (int i = 0; i < 3; i++)
+ {
+ // Paladin aura Spell
+ if(spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN
+ && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_PARTY)
+ return false;
+ // Druid form Spell
+ if(spellInfo->SpellFamilyName == SPELLFAMILY_DRUID
+ && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA
+ && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
+ return false;
+ // Rogue Stealth
+ if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE
+ && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA
+ && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
+ return false;
+ }
+ return true;
+}
+
+bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const
+{
+ SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
+ SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+
+ if(!spellInfo_1 || !spellInfo_2)
+ return false;
+
+ if(spellInfo_1->Id == spellId_2)
+ return false;
+
+ //I think we don't check this correctly because i need a exception for spell:
+ //72,11327,18461...(called from 1856,1857...) Call Aura 16,31, after trigger another spell who call aura 77 and 77 remove 16 and 31, this should not happen.
+ if(spellInfo_2->SpellFamilyFlags == 2048)
+ return false;
+
+ // Resurrection sickness
+ if((spellInfo_1->Id == SPELL_ID_PASSIVE_RESURRECTION_SICKNESS) != (spellInfo_2->Id==SPELL_ID_PASSIVE_RESURRECTION_SICKNESS))
+ return false;
+
+ // Specific spell family spells
+ switch(spellInfo_1->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ switch(spellInfo_2->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC: // same family case
+ {
+ // Thunderfury
+ if( spellInfo_1->Id == 21992 && spellInfo_2->Id == 27648 || spellInfo_2->Id == 21992 && spellInfo_1->Id == 27648 )
+ return false;
+
+ // Lightning Speed (Mongoose) and Fury of the Crashing Waves (Tsunami Talisman)
+ if( spellInfo_1->Id == 28093 && spellInfo_2->Id == 42084 ||
+ spellInfo_2->Id == 28093 && spellInfo_1->Id == 42084 )
+ return false;
+
+ // Soulstone Resurrection and Twisting Nether (resurrector)
+ if( spellInfo_1->SpellIconID == 92 && spellInfo_2->SpellIconID == 92 && (
+ spellInfo_1->SpellVisual == 99 && spellInfo_2->SpellVisual == 0 ||
+ spellInfo_2->SpellVisual == 99 && spellInfo_1->SpellVisual == 0 ) )
+ return false;
+
+ // Heart of the Wild and (Primal Instinct (Idol of Terror) triggering spell or Agility)
+ if( spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240 && (
+ spellInfo_1->SpellVisual == 0 && spellInfo_2->SpellVisual == 78 ||
+ spellInfo_2->SpellVisual == 0 && spellInfo_1->SpellVisual == 78 ) )
+ return false;
+
+ // Personalized Weather (thunder effect should overwrite rainy aura)
+ if(spellInfo_1->SpellIconID == 2606 && spellInfo_2->SpellIconID == 2606)
+ return false;
+
+ // Brood Affliction: Bronze
+ if( (spellInfo_1->Id == 23170 && spellInfo_2->Id == 23171) ||
+ (spellInfo_2->Id == 23170 && spellInfo_1->Id == 23171) )
+ return false;
+
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Scroll of Protection and Defensive Stance (multi-family check)
+ if( spellInfo_1->SpellIconID == 276 && spellInfo_1->SpellVisual == 196 && spellInfo_2->Id == 71)
+ return false;
+
+ // Improved Hamstring -> Hamstring (multi-family check)
+ if( (spellInfo_2->SpellFamilyFlags & 2) && spellInfo_1->Id == 23694 )
+ return false;
+
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Scroll of Stamina and Leader of the Pack (multi-family check)
+ if( spellInfo_1->SpellIconID == 312 && spellInfo_1->SpellVisual == 216 && spellInfo_2->Id == 24932 )
+ return false;
+
+ // Dragonmaw Illusion (multi-family check)
+ if (spellId_1 == 40216 && spellId_2 == 42016 )
+ return false;
+
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Garrote-Silence -> Garrote (multi-family check)
+ if( spellInfo_1->SpellIconID == 498 && spellInfo_1->SpellVisual == 0 && spellInfo_2->SpellIconID == 498 )
+ return false;
+
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Concussive Shot and Imp. Concussive Shot (multi-family check)
+ if( spellInfo_1->Id == 19410 && spellInfo_2->Id == 5116 )
+ return false;
+
+ // Improved Wing Clip -> Wing Clip (multi-family check)
+ if( (spellInfo_2->SpellFamilyFlags & 0x40) && spellInfo_1->Id == 19229 )
+ return false;
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Unstable Currents and other -> *Sanctity Aura (multi-family check)
+ if( spellInfo_2->SpellIconID==502 && spellInfo_1->SpellIconID==502 && spellInfo_1->SpellVisual==969 )
+ return false;
+
+ // *Band of Eternal Champion and Seal of Command(multi-family check)
+ if( spellId_1 == 35081 && spellInfo_2->SpellIconID==561 && spellInfo_2->SpellVisual==7992)
+ return false;
+
+ break;
+ }
+ }
+ break;
+ case SPELLFAMILY_MAGE:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_MAGE )
+ {
+ // Blizzard & Chilled (and some other stacked with blizzard spells
+ if( (spellInfo_1->SpellFamilyFlags & 0x80) && (spellInfo_2->SpellFamilyFlags & 0x100000) ||
+ (spellInfo_2->SpellFamilyFlags & 0x80) && (spellInfo_1->SpellFamilyFlags & 0x100000) )
+ return false;
+
+ // Blink & Improved Blink
+ if( (spellInfo_1->SpellFamilyFlags & 0x0000000000010000LL) && (spellInfo_2->SpellVisual == 72 && spellInfo_2->SpellIconID == 1499) ||
+ (spellInfo_2->SpellFamilyFlags & 0x0000000000010000LL) && (spellInfo_1->SpellVisual == 72 && spellInfo_1->SpellIconID == 1499) )
+ return false;
+ }
+ // Detect Invisibility and Mana Shield (multi-family check)
+ if( spellInfo_2->Id == 132 && spellInfo_1->SpellIconID == 209 && spellInfo_1->SpellVisual == 968 )
+ return false;
+
+ // Combustion and Fire Protection Aura (multi-family check)
+ if( spellInfo_1->Id == 11129 && spellInfo_2->SpellIconID == 33 && spellInfo_2->SpellVisual == 321 )
+ return false;
+
+ break;
+ case SPELLFAMILY_WARLOCK:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARLOCK )
+ {
+ // Siphon Life and Drain Life
+ if( spellInfo_1->SpellIconID == 152 && spellInfo_2->SpellIconID == 546 ||
+ spellInfo_2->SpellIconID == 152 && spellInfo_1->SpellIconID == 546 )
+ return false;
+
+ //Corruption & Seed of corruption
+ if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 1932 ||
+ spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 1932 )
+ if(spellInfo_1->SpellVisual != 0 && spellInfo_2->SpellVisual != 0)
+ return true; // can't be stacked
+
+ // Corruption and Unstable Affliction
+ if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 2039 ||
+ spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 2039 )
+ return false;
+
+ // (Corruption or Unstable Affliction) and (Curse of Agony or Curse of Doom)
+ if( (spellInfo_1->SpellIconID == 313 || spellInfo_1->SpellIconID == 2039) && (spellInfo_2->SpellIconID == 544 || spellInfo_2->SpellIconID == 91) ||
+ (spellInfo_2->SpellIconID == 313 || spellInfo_2->SpellIconID == 2039) && (spellInfo_1->SpellIconID == 544 || spellInfo_1->SpellIconID == 91) )
+ return false;
+ }
+ // Detect Invisibility and Mana Shield (multi-family check)
+ if( spellInfo_1->Id == 132 && spellInfo_2->SpellIconID == 209 && spellInfo_2->SpellVisual == 968 )
+ return false;
+ break;
+ case SPELLFAMILY_WARRIOR:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARRIOR )
+ {
+ // Rend and Deep Wound
+ if( (spellInfo_1->SpellFamilyFlags & 0x20) && (spellInfo_2->SpellFamilyFlags & 0x1000000000LL) ||
+ (spellInfo_2->SpellFamilyFlags & 0x20) && (spellInfo_1->SpellFamilyFlags & 0x1000000000LL) )
+ return false;
+
+ // Battle Shout and Rampage
+ if( (spellInfo_1->SpellIconID == 456 && spellInfo_2->SpellIconID == 2006) ||
+ (spellInfo_2->SpellIconID == 456 && spellInfo_1->SpellIconID == 2006) )
+ return false;
+ }
+
+ // Hamstring -> Improved Hamstring (multi-family check)
+ if( (spellInfo_1->SpellFamilyFlags & 2) && spellInfo_2->Id == 23694 )
+ return false;
+
+ // Defensive Stance and Scroll of Protection (multi-family check)
+ if( spellInfo_1->Id == 71 && spellInfo_2->SpellIconID == 276 && spellInfo_2->SpellVisual == 196 )
+ return false;
+
+ // Bloodlust and Bloodthirst (multi-family check)
+ if( spellInfo_2->Id == 2825 && spellInfo_1->SpellIconID == 38 && spellInfo_1->SpellVisual == 0 )
+ return false;
+
+ break;
+ case SPELLFAMILY_PRIEST:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PRIEST )
+ {
+ //Devouring Plague and Shadow Vulnerability
+ if( (spellInfo_1->SpellFamilyFlags & 0x2000000) && (spellInfo_2->SpellFamilyFlags & 0x800000000LL) ||
+ (spellInfo_2->SpellFamilyFlags & 0x2000000) && (spellInfo_1->SpellFamilyFlags & 0x800000000LL) )
+ return false;
+
+ //StarShards and Shadow Word: Pain
+ if( (spellInfo_1->SpellFamilyFlags & 0x200000) && (spellInfo_2->SpellFamilyFlags & 0x8000) ||
+ (spellInfo_2->SpellFamilyFlags & 0x200000) && (spellInfo_1->SpellFamilyFlags & 0x8000) )
+ return false;
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_DRUID )
+ {
+ //Omen of Clarity and Blood Frenzy
+ if( (spellInfo_1->SpellFamilyFlags == 0x0 && spellInfo_1->SpellIconID == 108) && (spellInfo_2->SpellFamilyFlags & 0x20000000000000LL) ||
+ (spellInfo_2->SpellFamilyFlags == 0x0 && spellInfo_2->SpellIconID == 108) && (spellInfo_1->SpellFamilyFlags & 0x20000000000000LL) )
+ return false;
+
+ // Tree of Life (Shapeshift) and 34123 Tree of Life (Passive)
+ if ((spellId_1 == 33891 && spellId_2 == 34123) ||
+ (spellId_2 == 33891 && spellId_1 == 34123))
+ return false;
+
+ // Wrath of Elune and Nature's Grace
+ if( spellInfo_1->Id == 16886 && spellInfo_2->Id == 46833 || spellInfo_2->Id == 16886 && spellInfo_1->Id == 46833 )
+ return false;
+
+ // Bear Rage (Feral T4 (2)) and Omen of Clarity
+ if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37306 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37306 )
+ return false;
+
+ // Cat Energy (Feral T4 (2)) and Omen of Clarity
+ if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37311 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37311 )
+ return false;
+ }
+
+ // Leader of the Pack and Scroll of Stamina (multi-family check)
+ if( spellInfo_1->Id == 24932 && spellInfo_2->SpellIconID == 312 && spellInfo_2->SpellVisual == 216 )
+ return false;
+
+ // Dragonmaw Illusion (multi-family check)
+ if (spellId_1 == 42016 && spellId_2 == 40216 )
+ return false;
+
+ break;
+ case SPELLFAMILY_ROGUE:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_ROGUE )
+ {
+ // Master of Subtlety
+ if (spellId_1 == 31665 && spellId_2 == 31666 || spellId_1 == 31666 && spellId_2 == 31665 )
+ return false;
+ }
+
+ // Garrote -> Garrote-Silence (multi-family check)
+ if( spellInfo_1->SpellIconID == 498 && spellInfo_2->SpellIconID == 498 && spellInfo_2->SpellVisual == 0 )
+ return false;
+ break;
+ case SPELLFAMILY_HUNTER:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_HUNTER )
+ {
+ // Rapid Fire & Quick Shots
+ if( (spellInfo_1->SpellFamilyFlags & 0x20) && (spellInfo_2->SpellFamilyFlags & 0x20000000000LL) ||
+ (spellInfo_2->SpellFamilyFlags & 0x20) && (spellInfo_1->SpellFamilyFlags & 0x20000000000LL) )
+ return false;
+
+ // Serpent Sting & (Immolation/Explosive Trap Effect)
+ if( (spellInfo_1->SpellFamilyFlags & 0x4) && (spellInfo_2->SpellFamilyFlags & 0x00000004000LL) ||
+ (spellInfo_2->SpellFamilyFlags & 0x4) && (spellInfo_1->SpellFamilyFlags & 0x00000004000LL) )
+ return false;
+
+ // Bestial Wrath
+ if( spellInfo_1->SpellIconID == 1680 && spellInfo_2->SpellIconID == 1680 )
+ return false;
+ }
+
+ // Wing Clip -> Improved Wing Clip (multi-family check)
+ if( (spellInfo_1->SpellFamilyFlags & 0x40) && spellInfo_2->Id == 19229 )
+ return false;
+
+ // Concussive Shot and Imp. Concussive Shot (multi-family check)
+ if( spellInfo_2->Id == 19410 && spellInfo_1->Id == 5116 )
+ return false;
+ break;
+ case SPELLFAMILY_PALADIN:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PALADIN )
+ {
+ // Paladin Seals
+ if( IsSealSpell(spellInfo_1) && IsSealSpell(spellInfo_2) )
+ return true;
+ }
+ // Combustion and Fire Protection Aura (multi-family check)
+ if( spellInfo_2->Id == 11129 && spellInfo_1->SpellIconID == 33 && spellInfo_1->SpellVisual == 321 )
+ return false;
+
+ // *Sanctity Aura -> Unstable Currents and other (multi-family check)
+ if( spellInfo_1->SpellIconID==502 && spellInfo_2->SpellFamilyName == SPELLFAMILY_GENERIC && spellInfo_2->SpellIconID==502 && spellInfo_2->SpellVisual==969 )
+ return false;
+
+ // *Seal of Command and Band of Eternal Champion (multi-family check)
+ if( spellInfo_1->SpellIconID==561 && spellInfo_1->SpellVisual==7992 && spellId_2 == 35081)
+ return false;
+ break;
+ case SPELLFAMILY_SHAMAN:
+ if( spellInfo_2->SpellFamilyName == SPELLFAMILY_SHAMAN )
+ {
+ // shaman shields
+ if( IsElementalShield(spellInfo_1) && IsElementalShield(spellInfo_2) )
+ return true;
+
+ // Windfury weapon
+ if( spellInfo_1->SpellIconID==220 && spellInfo_2->SpellIconID==220 &&
+ spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags )
+ return false;
+ }
+ // Bloodlust and Bloodthirst (multi-family check)
+ if( spellInfo_1->Id == 2825 && spellInfo_2->SpellIconID == 38 && spellInfo_2->SpellVisual == 0 )
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ // more generic checks
+ if (spellInfo_1->SpellIconID == spellInfo_2->SpellIconID &&
+ spellInfo_1->SpellIconID != 0 && spellInfo_2->SpellIconID != 0)
+ {
+ bool isModifier = false;
+ for (int i = 0; i < 3; i++)
+ {
+ if (spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER ||
+ spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER ||
+ spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER ||
+ spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER )
+ isModifier = true;
+ }
+
+ if (!isModifier)
+ return true;
+ }
+
+ if (IsRankSpellDueToSpell(spellInfo_1, spellId_2))
+ return true;
+
+ if (spellInfo_1->SpellFamilyName == 0 || spellInfo_2->SpellFamilyName == 0)
+ return false;
+
+ if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
+ return false;
+
+ for (int i = 0; i < 3; ++i)
+ if (spellInfo_1->Effect[i] != spellInfo_2->Effect[i] ||
+ spellInfo_1->EffectItemType[i] != spellInfo_2->EffectItemType[i] ||
+ spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i] ||
+ spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i])
+ return false;
+
+ return true;
+}
+
+bool SpellMgr::IsProfessionSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return false;
+
+ if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
+ return false;
+
+ uint32 skill = spellInfo->EffectMiscValue[1];
+
+ return IsProfessionSkill(skill);
+}
+
+bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return false;
+
+ if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
+ return false;
+
+ uint32 skill = spellInfo->EffectMiscValue[1];
+
+ return IsPrimaryProfessionSkill(skill);
+}
+
+bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
+{
+ return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId)==1;
+}
+
+SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const
+{
+ // ignore passive spells
+ if(IsPassiveSpell(spellInfo->Id))
+ return spellInfo;
+
+ bool needRankSelection = false;
+ for(int i=0;i<3;i++)
+ {
+ if( IsPositiveEffect(spellInfo->Id, i) && (
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY
+ ) )
+ {
+ needRankSelection = true;
+ break;
+ }
+ }
+
+ // not required
+ if(!needRankSelection)
+ return spellInfo;
+
+ for(uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId))
+ {
+ SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId);
+ if(!nextSpellInfo)
+ break;
+
+ // if found appropriate level
+ if(playerLevel + 10 >= nextSpellInfo->spellLevel)
+ return nextSpellInfo;
+
+ // one rank less then
+ }
+
+ // not found
+ return NULL;
+}
+
+void SpellMgr::LoadSpellChains()
+{
+ mSpellChains.clear(); // need for reload case
+ mSpellChainsNext.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT spell_id, prev_spell, first_spell, rank, req_spell FROM spell_chain");
+ if(result == NULL)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 spell chain records" );
+ sLog.outErrorDb("`spell_chains` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+
+ SpellChainNode node;
+ node.prev = fields[1].GetUInt32();
+ node.first = fields[2].GetUInt32();
+ node.rank = fields[3].GetUInt8();
+ node.req = fields[4].GetUInt32();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_chain` does not exist",spell_id);
+ continue;
+ }
+
+ if(node.prev!=0 && !sSpellStore.LookupEntry(node.prev))
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existed previous rank spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ if(!sSpellStore.LookupEntry(node.first))
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing first rank spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ // check basic spell chain data integrity (note: rank can be equal 0 or 1 for first/single spell)
+ if( (spell_id == node.first) != (node.rank <= 1) ||
+ (spell_id == node.first) != (node.prev == 0) ||
+ (node.rank <= 1) != (node.prev == 0) )
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not compatible chain data.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ if(node.req!=0 && !sSpellStore.LookupEntry(node.req))
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing required spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ // talents not required data in spell chain for work, but must be checked if present for intergrity
+ if(TalentSpellPos const* pos = GetTalentSpellPos(spell_id))
+ {
+ if(node.rank!=pos->rank+1)
+ {
+ sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong rank.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ if(TalentEntry const* talentEntry = sTalentStore.LookupEntry(pos->talent_id))
+ {
+ if(node.first!=talentEntry->RankID[0])
+ {
+ sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong first rank spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ if(node.rank > 1 && node.prev != talentEntry->RankID[node.rank-1-1])
+ {
+ sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong prev rank spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+
+ if(node.req!=talentEntry->DependsOnSpell)
+ {
+ sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong required spell.",
+ spell_id,node.prev,node.first,node.rank,node.req);
+ continue;
+ }
+ }
+ }
+
+ mSpellChains[spell_id] = node;
+
+ if(node.prev)
+ mSpellChainsNext.insert(SpellChainMapNext::value_type(node.prev,spell_id));
+
+ if(node.req)
+ mSpellChainsNext.insert(SpellChainMapNext::value_type(node.req,spell_id));
+
+ ++count;
+ } while( result->NextRow() );
+
+ // additional integrity checks
+ for(SpellChainMap::iterator i = mSpellChains.begin(); i != mSpellChains.end(); ++i)
+ {
+ if(i->second.prev)
+ {
+ SpellChainMap::iterator i_prev = mSpellChains.find(i->second.prev);
+ if(i_prev == mSpellChains.end())
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found previous rank spell in table.",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req);
+ }
+ else if( i_prev->second.first != i->second.first )
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different first spell in chain compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
+ i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req);
+ }
+ else if( i_prev->second.rank+1 != i->second.rank )
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different rank compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
+ i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req);
+ }
+ }
+
+ if(i->second.req)
+ {
+ SpellChainMap::iterator i_req = mSpellChains.find(i->second.req);
+ if(i_req == mSpellChains.end())
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found required rank spell in table.",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req);
+ }
+ else if( i_req->second.first == i->second.first )
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell from same spell chain (prev: %u, first: %u, rank: %d, req: %u).",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
+ i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req);
+ }
+ else if( i_req->second.req )
+ {
+ sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell with required spell (prev: %u, first: %u, rank: %d, req: %u).",
+ i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
+ i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req);
+ }
+ }
+ }
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell chain records", count );
+}
+
+void SpellMgr::LoadSpellLearnSkills()
+{
+ mSpellLearnSkills.clear(); // need for reload case
+
+ // search auto-learned skills and add its to map also for use in unlearn spells/talents
+ uint32 dbc_count = 0;
+ for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
+ {
+ SpellEntry const* entry = sSpellStore.LookupEntry(spell);
+
+ if(!entry)
+ continue;
+
+ for(int i = 0; i < 3; ++i)
+ {
+ if(entry->Effect[i]==SPELL_EFFECT_SKILL)
+ {
+ SpellLearnSkillNode dbc_node;
+ dbc_node.skill = entry->EffectMiscValue[i];
+ if ( dbc_node.skill != SKILL_RIDING )
+ dbc_node.value = 1;
+ else
+ dbc_node.value = (entry->EffectBasePoints[i]+1)*75;
+ dbc_node.maxvalue = (entry->EffectBasePoints[i]+1)*75;
+
+ SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell);
+
+ mSpellLearnSkills[spell] = dbc_node;
+ ++dbc_count;
+ break;
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count );
+}
+
+void SpellMgr::LoadSpellLearnSpells()
+{
+ mSpellLearnSpells.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry, SpellID FROM spell_learn_spell");
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 spell learn spells" );
+ sLog.outErrorDb("`spell_learn_spell` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+
+ SpellLearnSpellNode node;
+ node.spell = fields[1].GetUInt32();
+ node.autoLearned= false;
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id);
+ continue;
+ }
+
+ if(!sSpellStore.LookupEntry(node.spell))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",node.spell);
+ continue;
+ }
+
+ mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id,node));
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ // search auto-learned spells and add its to map also for use in unlearn spells/talents
+ uint32 dbc_count = 0;
+ for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
+ {
+ SpellEntry const* entry = sSpellStore.LookupEntry(spell);
+
+ if(!entry)
+ continue;
+
+ for(int i = 0; i < 3; ++i)
+ {
+ if(entry->Effect[i]==SPELL_EFFECT_LEARN_SPELL)
+ {
+ SpellLearnSpellNode dbc_node;
+ dbc_node.spell = entry->EffectTriggerSpell[i];
+ dbc_node.autoLearned = true;
+
+ SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell);
+ SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell);
+
+ bool found = false;
+ for(SpellLearnSpellMap::const_iterator itr = db_node_begin; itr != db_node_end; ++itr)
+ {
+ if(itr->second.spell == dbc_node.spell)
+ {
+ sLog.outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.",
+ spell,dbc_node.spell);
+ found = true;
+ break;
+ }
+ }
+
+ if(!found) // add new spell-spell pair if not found
+ {
+ mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node));
+ ++dbc_count;
+ }
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count );
+}
+
+void SpellMgr::LoadSpellScriptTarget()
+{
+ mSpellScriptTarget.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM spell_script_target");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 spellId = fields[0].GetUInt32();
+ uint32 type = fields[1].GetUInt32();
+ uint32 targetEntry = fields[2].GetUInt32();
+
+ SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
+
+ if(!spellProto)
+ {
+ sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry);
+ continue;
+ }
+
+ bool targetfound = false;
+ for(int i = 0; i <3; ++i)
+ {
+ if( spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT ||
+ spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT ||
+ spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT_COORDINATES ||
+ spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT_COORDINATES )
+ {
+ targetfound = true;
+ break;
+ }
+ }
+ if(!targetfound)
+ {
+ sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_SCRIPT(38) or TARGET_SCRIPT_COORDINATES (46).",spellId,targetEntry);
+ continue;
+ }
+
+ if( type >= MAX_SPELL_TARGET_TYPE )
+ {
+ sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry);
+ continue;
+ }
+
+ switch(type)
+ {
+ case SPELL_TARGET_TYPE_GAMEOBJECT:
+ {
+ if( targetEntry==0 )
+ break;
+
+ if(!sGOStorage.LookupEntry<GameObjectInfo>(targetEntry))
+ {
+ sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry);
+ continue;
+ }
+ break;
+ }
+ default:
+ {
+ if( targetEntry==0 )
+ {
+ sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type);
+ continue;
+ }
+ if(!sCreatureStorage.LookupEntry<CreatureInfo>(targetEntry))
+ {
+ sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry);
+ continue;
+ }
+ const CreatureInfo* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(targetEntry);
+
+ if(spellId == 30427 && !cInfo->SkinLootId)
+ {
+ sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry);
+ continue;
+ }
+ break;
+ }
+ }
+
+ mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellTargetType(type),targetEntry)));
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ // Check all spells
+ /* Disabled (lot errors at this moment)
+ for(uint32 i = 1; i < sSpellStore.nCount; ++i)
+ {
+ SpellEntry const * spellInfo = sSpellStore.LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ bool found = false;
+ for(int j=0; j<3; ++j)
+ {
+ if( spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT || spellInfo->EffectImplicitTargetA[j] != TARGET_SELF && spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT )
+ {
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id);
+ if(lower==upper)
+ {
+ sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_SCRIPT), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_SCRIPT);
+ break; // effects of spell
+ }
+ }
+ }
+ }
+ */
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u Spell Script Targets", count);
+}
+
+void SpellMgr::LoadSpellPetAuras()
+{
+ mSpellPetAuraMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell pet auras", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint16 spell = fields[0].GetUInt16();
+ uint16 pet = fields[1].GetUInt16();
+ uint16 aura = fields[2].GetUInt16();
+
+ SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell);
+ if(itr != mSpellPetAuraMap.end())
+ {
+ itr->second.AddAura(pet, aura);
+ }
+ else
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if (!spellInfo)
+ {
+ 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)
+ {
+ sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell);
+ continue;
+ }
+
+ SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura);
+ if (!spellInfo2)
+ {
+ sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura);
+ continue;
+ }
+
+ PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_PET, spellInfo->EffectBasePoints[i] + spellInfo->EffectBaseDice[i]);
+ mSpellPetAuraMap[spell] = pa;
+ }
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell pet auras", count );
+}
+
+/// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc
+bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
+{
+ // not exist
+ if(!spellInfo)
+ return false;
+
+ bool need_check_reagents = false;
+
+ // check effects
+ for(int i=0; i<3; ++i)
+ {
+ switch(spellInfo->Effect[i])
+ {
+ case 0:
+ continue;
+
+ // craft spell for crafting non-existed item (break client recipes list show)
+ case SPELL_EFFECT_CREATE_ITEM:
+ {
+ if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] ))
+ {
+ if(msg)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]);
+ else
+ sLog.outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]);
+ }
+ return false;
+ }
+
+ need_check_reagents = true;
+ break;
+ }
+ case SPELL_EFFECT_LEARN_SPELL:
+ {
+ SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[0]);
+ if( !IsSpellValid(spellInfo2,pl,msg) )
+ {
+ if(msg)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[0]);
+ else
+ sLog.outErrorDb("Spell %u learn to invalid spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[0]);
+ }
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ if(need_check_reagents)
+ {
+ for(int j = 0; j < 8; ++j)
+ {
+ if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] ))
+ {
+ if(msg)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]);
+ else
+ sLog.outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]);
+ }
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id)
+{
+ // normal case
+ if( spellInfo->AreaId && spellInfo->AreaId != zone_id && spellInfo->AreaId != area_id )
+ return false;
+
+ // elixirs (all area dependent elixirs have family SPELLFAMILY_POTION, use this for speedup)
+ if(spellInfo->SpellFamilyName==SPELLFAMILY_POTION)
+ {
+ if(uint32 mask = spellmgr.GetSpellElixirMask(spellInfo->Id))
+ {
+ if(mask & ELIXIR_UNSTABLE_MASK)
+ {
+ // in the Blade's Edge Mountains Plateaus and Gruul's Lair.
+ return zone_id ==3522 || map_id==565;
+ }
+ if(mask & ELIXIR_UNSTABLE_MASK)
+ {
+ // in Tempest Keep, Serpentshrine Cavern, Caverns of Time: Mount Hyjal, Black Temple
+ // TODO: and the Sunwell Plateau
+ if(zone_id ==3607 || map_id==534 || map_id==564)
+ return true;
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
+ if(!mapEntry)
+ return false;
+
+ return mapEntry->multimap_id==206;
+ }
+
+ // elixirs not have another limitations
+ return true;
+ }
+ }
+
+ // special cases zone check (maps checked by multimap common id)
+ switch(spellInfo->Id)
+ {
+ case 41618:
+ case 41620:
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
+ if(!mapEntry)
+ return false;
+
+ return mapEntry->multimap_id==206;
+ }
+
+ case 41617:
+ case 41619:
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
+ if(!mapEntry)
+ return false;
+
+ return mapEntry->multimap_id==207;
+ }
+ // Dragonmaw Illusion
+ case 40216:
+ case 42016:
+ {
+ if ( area_id != 3759 && area_id != 3966 && area_id != 3939 )
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+void SpellMgr::LoadSkillLineAbilityMap()
+{
+ mSkillLineAbilityMap.clear();
+
+ uint32 count = 0;
+
+ for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); i++)
+ {
+ SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
+ if(!SkillInfo)
+ continue;
+
+ mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo));
+ ++count;
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u SkillLineAbility MultiMap", count);
+}
+
+DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
+{
+ // Explicit Diminishing Groups
+ switch(spellproto->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ {
+ // Polymorph
+ if ((spellproto->SpellFamilyFlags & 0x00001000000LL) && spellproto->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
+ return DIMINISHING_POLYMORPH;
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Kidney Shot
+ if (spellproto->SpellFamilyFlags & 0x00000200000LL)
+ return DIMINISHING_KIDNEYSHOT;
+ // Blind
+ else if (spellproto->SpellFamilyFlags & 0x00001000000LL)
+ return DIMINISHING_BLIND_CYCLONE;
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Death Coil
+ if (spellproto->SpellFamilyFlags & 0x00000080000LL)
+ return DIMINISHING_DEATHCOIL;
+ // Fear
+ else if (spellproto->SpellFamilyFlags & 0x40840000000LL)
+ return DIMINISHING_WARLOCK_FEAR;
+ // Curses/etc
+ else if (spellproto->SpellFamilyFlags & 0x00080000000LL)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Cyclone
+ if (spellproto->SpellFamilyFlags & 0x02000000000LL)
+ return DIMINISHING_BLIND_CYCLONE;
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Hamstring - limit duration to 10s in PvP
+ if (spellproto->SpellFamilyFlags & 0x00000000002LL)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Get by mechanic
+ for (uint8 i=0;i<3;++i)
+ {
+ if (spellproto->Mechanic == MECHANIC_STUN || spellproto->EffectMechanic[i] == MECHANIC_STUN)
+ return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN;
+ else if (spellproto->Mechanic == MECHANIC_SLEEP || spellproto->EffectMechanic[i] == MECHANIC_SLEEP)
+ return DIMINISHING_SLEEP;
+ else if (spellproto->Mechanic == MECHANIC_ROOT || spellproto->EffectMechanic[i] == MECHANIC_ROOT)
+ return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT;
+ else if (spellproto->Mechanic == MECHANIC_FEAR || spellproto->EffectMechanic[i] == MECHANIC_FEAR)
+ return DIMINISHING_FEAR;
+ else if (spellproto->Mechanic == MECHANIC_CHARM || spellproto->EffectMechanic[i] == MECHANIC_CHARM)
+ return DIMINISHING_CHARM;
+ else if (spellproto->Mechanic == MECHANIC_SILENCE || spellproto->EffectMechanic[i] == MECHANIC_SILENCE)
+ return DIMINISHING_SILENCE;
+ else if (spellproto->Mechanic == MECHANIC_DISARM || spellproto->EffectMechanic[i] == MECHANIC_DISARM)
+ return DIMINISHING_DISARM;
+ else if (spellproto->Mechanic == MECHANIC_FREEZE || spellproto->EffectMechanic[i] == MECHANIC_FREEZE)
+ return DIMINISHING_FREEZE;
+ else if (spellproto->Mechanic == MECHANIC_KNOCKOUT|| spellproto->EffectMechanic[i] == MECHANIC_KNOCKOUT ||
+ spellproto->Mechanic == MECHANIC_SAPPED || spellproto->EffectMechanic[i] == MECHANIC_SAPPED)
+ return DIMINISHING_KNOCKOUT;
+ else if (spellproto->Mechanic == MECHANIC_BANISH || spellproto->EffectMechanic[i] == MECHANIC_BANISH)
+ return DIMINISHING_BANISH;
+ }
+
+ return DIMINISHING_NONE;
+}
+
+bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
+{
+ switch(group)
+ {
+ case DIMINISHING_CONTROL_STUN:
+ case DIMINISHING_TRIGGER_STUN:
+ case DIMINISHING_KIDNEYSHOT:
+ case DIMINISHING_SLEEP:
+ case DIMINISHING_CONTROL_ROOT:
+ case DIMINISHING_TRIGGER_ROOT:
+ case DIMINISHING_FEAR:
+ case DIMINISHING_WARLOCK_FEAR:
+ case DIMINISHING_CHARM:
+ case DIMINISHING_POLYMORPH:
+ case DIMINISHING_FREEZE:
+ case DIMINISHING_KNOCKOUT:
+ case DIMINISHING_BLIND_CYCLONE:
+ case DIMINISHING_BANISH:
+ case DIMINISHING_LIMITONLY:
+ return true;
+ }
+ return false;
+}
+
+DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
+{
+ switch(group)
+ {
+ case DIMINISHING_BLIND_CYCLONE:
+ case DIMINISHING_CONTROL_STUN:
+ case DIMINISHING_TRIGGER_STUN:
+ case DIMINISHING_KIDNEYSHOT:
+ return DRTYPE_ALL;
+ case DIMINISHING_SLEEP:
+ 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_FREEZE:
+ case DIMINISHING_BANISH:
+ case DIMINISHING_WARLOCK_FEAR:
+ case DIMINISHING_KNOCKOUT:
+ return DRTYPE_PLAYER;
+ }
+
+ return DRTYPE_NONE;
+}
diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h
new file mode 100644
index 00000000000..7e8ffc82564
--- /dev/null
+++ b/src/game/SpellMgr.h
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _SPELLMGR_H
+#define _SPELLMGR_H
+
+// For static or at-server-startup loaded spell data
+// For more high level function for sSpellStore data
+
+#include "SharedDefines.h"
+#include "Database/DBCStructure.h"
+#include "Database/SQLStorage.h"
+
+#include "Utilities/HashMap.h"
+#include <map>
+
+class Player;
+class Spell;
+
+extern SQLStorage sSpellThreatStore;
+
+enum SpellFailedReason
+{
+ 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_PROSPECTED = 0x0F,
+ SPELL_FAILED_CANT_CAST_ON_TAPPED = 0x10,
+ SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 0x11,
+ SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 0x12,
+ SPELL_FAILED_CANT_STEALTH = 0x13,
+ SPELL_FAILED_CASTER_AURASTATE = 0x14,
+ SPELL_FAILED_CASTER_DEAD = 0x15,
+ SPELL_FAILED_CHARMED = 0x16,
+ SPELL_FAILED_CHEST_IN_USE = 0x17,
+ SPELL_FAILED_CONFUSED = 0x18,
+ SPELL_FAILED_DONT_REPORT = 0x19,
+ SPELL_FAILED_EQUIPPED_ITEM = 0x1A,
+ SPELL_FAILED_EQUIPPED_ITEM_CLASS = 0x1B,
+ SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 0x1C,
+ SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 0x1D,
+ SPELL_FAILED_ERROR = 0x1E,
+ SPELL_FAILED_FIZZLE = 0x1F,
+ SPELL_FAILED_FLEEING = 0x20,
+ SPELL_FAILED_FOOD_LOWLEVEL = 0x21,
+ SPELL_FAILED_HIGHLEVEL = 0x22,
+ SPELL_FAILED_HUNGER_SATIATED = 0x23,
+ SPELL_FAILED_IMMUNE = 0x24,
+ SPELL_FAILED_INTERRUPTED = 0x25,
+ SPELL_FAILED_INTERRUPTED_COMBAT = 0x26,
+ SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 0x27,
+ SPELL_FAILED_ITEM_GONE = 0x28,
+ SPELL_FAILED_ITEM_NOT_FOUND = 0x29,
+ SPELL_FAILED_ITEM_NOT_READY = 0x2A,
+ SPELL_FAILED_LEVEL_REQUIREMENT = 0x2B,
+ SPELL_FAILED_LINE_OF_SIGHT = 0x2C,
+ SPELL_FAILED_LOWLEVEL = 0x2D,
+ SPELL_FAILED_LOW_CASTLEVEL = 0x2E,
+ SPELL_FAILED_MAINHAND_EMPTY = 0x2F,
+ SPELL_FAILED_MOVING = 0x30,
+ SPELL_FAILED_NEED_AMMO = 0x31,
+ SPELL_FAILED_NEED_AMMO_POUCH = 0x32,
+ SPELL_FAILED_NEED_EXOTIC_AMMO = 0x33,
+ SPELL_FAILED_NOPATH = 0x34,
+ SPELL_FAILED_NOT_BEHIND = 0x35,
+ SPELL_FAILED_NOT_FISHABLE = 0x36,
+ SPELL_FAILED_NOT_FLYING = 0x37,
+ SPELL_FAILED_NOT_HERE = 0x38,
+ SPELL_FAILED_NOT_INFRONT = 0x39,
+ SPELL_FAILED_NOT_IN_CONTROL = 0x3A,
+ SPELL_FAILED_NOT_KNOWN = 0x3B,
+ SPELL_FAILED_NOT_MOUNTED = 0x3C,
+ SPELL_FAILED_NOT_ON_TAXI = 0x3D,
+ SPELL_FAILED_NOT_ON_TRANSPORT = 0x3E,
+ SPELL_FAILED_NOT_READY = 0x3F,
+ SPELL_FAILED_NOT_SHAPESHIFT = 0x40,
+ SPELL_FAILED_NOT_STANDING = 0x41,
+ SPELL_FAILED_NOT_TRADEABLE = 0x42,
+ SPELL_FAILED_NOT_TRADING = 0x43,
+ SPELL_FAILED_NOT_UNSHEATHED = 0x44,
+ SPELL_FAILED_NOT_WHILE_GHOST = 0x45,
+ SPELL_FAILED_NO_AMMO = 0x46,
+ SPELL_FAILED_NO_CHARGES_REMAIN = 0x47,
+ SPELL_FAILED_NO_CHAMPION = 0x48,
+ SPELL_FAILED_NO_COMBO_POINTS = 0x49,
+ SPELL_FAILED_NO_DUELING = 0x4A,
+ SPELL_FAILED_NO_ENDURANCE = 0x4B,
+ SPELL_FAILED_NO_FISH = 0x4C,
+ SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 0x4D,
+ SPELL_FAILED_NO_MOUNTS_ALLOWED = 0x4E,
+ SPELL_FAILED_NO_PET = 0x4F,
+ SPELL_FAILED_NO_POWER = 0x50,
+ SPELL_FAILED_NOTHING_TO_DISPEL = 0x51,
+ SPELL_FAILED_NOTHING_TO_STEAL = 0x52,
+ SPELL_FAILED_ONLY_ABOVEWATER = 0x53,
+ SPELL_FAILED_ONLY_DAYTIME = 0x54,
+ SPELL_FAILED_ONLY_INDOORS = 0x55,
+ SPELL_FAILED_ONLY_MOUNTED = 0x56,
+ SPELL_FAILED_ONLY_NIGHTTIME = 0x57,
+ SPELL_FAILED_ONLY_OUTDOORS = 0x58,
+ SPELL_FAILED_ONLY_SHAPESHIFT = 0x59,
+ SPELL_FAILED_ONLY_STEALTHED = 0x5A,
+ SPELL_FAILED_ONLY_UNDERWATER = 0x5B,
+ SPELL_FAILED_OUT_OF_RANGE = 0x5C,
+ SPELL_FAILED_PACIFIED = 0x5D,
+ SPELL_FAILED_POSSESSED = 0x5E,
+ SPELL_FAILED_REAGENTS = 0x5F,
+ SPELL_FAILED_REQUIRES_AREA = 0x60,
+ SPELL_FAILED_REQUIRES_SPELL_FOCUS = 0x61,
+ SPELL_FAILED_ROOTED = 0x62,
+ SPELL_FAILED_SILENCED = 0x63,
+ SPELL_FAILED_SPELL_IN_PROGRESS = 0x64,
+ SPELL_FAILED_SPELL_LEARNED = 0x65,
+ SPELL_FAILED_SPELL_UNAVAILABLE = 0x66,
+ SPELL_FAILED_STUNNED = 0x67,
+ SPELL_FAILED_TARGETS_DEAD = 0x68,
+ SPELL_FAILED_TARGET_AFFECTING_COMBAT = 0x69,
+ SPELL_FAILED_TARGET_AURASTATE = 0x6A,
+ SPELL_FAILED_TARGET_DUELING = 0x6B,
+ SPELL_FAILED_TARGET_ENEMY = 0x6C,
+ SPELL_FAILED_TARGET_ENRAGED = 0x6D,
+ SPELL_FAILED_TARGET_FRIENDLY = 0x6E,
+ SPELL_FAILED_TARGET_IN_COMBAT = 0x6F,
+ SPELL_FAILED_TARGET_IS_PLAYER = 0x70,
+ SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 0x71,
+ SPELL_FAILED_TARGET_NOT_DEAD = 0x72,
+ SPELL_FAILED_TARGET_NOT_IN_PARTY = 0x73,
+ SPELL_FAILED_TARGET_NOT_LOOTED = 0x74,
+ SPELL_FAILED_TARGET_NOT_PLAYER = 0x75,
+ SPELL_FAILED_TARGET_NO_POCKETS = 0x76,
+ SPELL_FAILED_TARGET_NO_WEAPONS = 0x77,
+ SPELL_FAILED_TARGET_UNSKINNABLE = 0x78,
+ SPELL_FAILED_THIRST_SATIATED = 0x79,
+ SPELL_FAILED_TOO_CLOSE = 0x7A,
+ SPELL_FAILED_TOO_MANY_OF_ITEM = 0x7B,
+ SPELL_FAILED_TOTEM_CATEGORY = 0x7C,
+ SPELL_FAILED_TOTEMS = 0x7D,
+ SPELL_FAILED_TRAINING_POINTS = 0x7E,
+ SPELL_FAILED_TRY_AGAIN = 0x7F,
+ SPELL_FAILED_UNIT_NOT_BEHIND = 0x80,
+ SPELL_FAILED_UNIT_NOT_INFRONT = 0x81,
+ SPELL_FAILED_WRONG_PET_FOOD = 0x82,
+ SPELL_FAILED_NOT_WHILE_FATIGUED = 0x83,
+ SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 0x84,
+ SPELL_FAILED_NOT_WHILE_TRADING = 0x85,
+ SPELL_FAILED_TARGET_NOT_IN_RAID = 0x86,
+ SPELL_FAILED_DISENCHANT_WHILE_LOOTING = 0x87,
+ SPELL_FAILED_PROSPECT_WHILE_LOOTING = 0x88,
+ SPELL_FAILED_PROSPECT_NEED_MORE = 0x89,
+ SPELL_FAILED_TARGET_FREEFORALL = 0x8A,
+ SPELL_FAILED_NO_EDIBLE_CORPSES = 0x8B,
+ SPELL_FAILED_ONLY_BATTLEGROUNDS = 0x8C,
+ SPELL_FAILED_TARGET_NOT_GHOST = 0x8D,
+ SPELL_FAILED_TOO_MANY_SKILLS = 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_ONLY_IN_ARENA = 0xA6,
+ SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 0xA7,
+ SPELL_FAILED_UNKNOWN = 0xA8,
+};
+
+enum SpellFamilyNames
+{
+ SPELLFAMILY_GENERIC = 0,
+ SPELLFAMILY_UNK1 = 1, // events, holidays
+ // 2 - unused
+ SPELLFAMILY_MAGE = 3,
+ SPELLFAMILY_WARRIOR = 4,
+ SPELLFAMILY_WARLOCK = 5,
+ SPELLFAMILY_PRIEST = 6,
+ SPELLFAMILY_DRUID = 7,
+ SPELLFAMILY_ROGUE = 8,
+ SPELLFAMILY_HUNTER = 9,
+ SPELLFAMILY_PALADIN = 10,
+ SPELLFAMILY_SHAMAN = 11,
+ SPELLFAMILY_UNK2 = 12,
+ SPELLFAMILY_POTION = 13
+};
+
+//Some SpellFamilyFlags
+#define SPELLFAMILYFLAG_ROGUE_VANISH 0x000000800LL
+#define SPELLFAMILYFLAG_ROGUE_STEALTH 0x000400000LL
+#define SPELLFAMILYFLAG_ROGUE_BACKSTAB 0x000800004LL
+#define SPELLFAMILYFLAG_ROGUE_SAP 0x000000080LL
+#define SPELLFAMILYFLAG_ROGUE_FEINT 0x008000000LL
+#define SPELLFAMILYFLAG_ROGUE_KIDNEYSHOT 0x000200000LL
+#define SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE 0x9003E0000LL
+
+// Spell clasification
+enum SpellSpecific
+{
+ SPELL_NORMAL = 0,
+ SPELL_SEAL = 1,
+ SPELL_BLESSING = 2,
+ SPELL_AURA = 3,
+ SPELL_STING = 4,
+ SPELL_CURSE = 5,
+ SPELL_ASPECT = 6,
+ SPELL_TRACKER = 7,
+ SPELL_WARLOCK_ARMOR = 8,
+ SPELL_MAGE_ARMOR = 9,
+ SPELL_ELEMENTAL_SHIELD = 10,
+ SPELL_MAGE_POLYMORPH = 11,
+ SPELL_POSITIVE_SHOUT = 12,
+ SPELL_JUDGEMENT = 13,
+ SPELL_BATTLE_ELIXIR = 14,
+ SPELL_GUARDIAN_ELIXIR = 15,
+ SPELL_FLASK_ELIXIR = 16
+};
+
+SpellSpecific GetSpellSpecific(uint32 spellId);
+
+// Different spell properties
+inline float GetSpellRadius(SpellRadiusEntry const *radius) { return (radius ? radius->Radius : 0); }
+uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell = NULL);
+inline float GetSpellMinRange(SpellRangeEntry const *range) { return (range ? range->minRange : 0); }
+inline float GetSpellMaxRange(SpellRangeEntry const *range) { return (range ? range->maxRange : 0); }
+inline uint32 GetSpellRecoveryTime(SpellEntry const *spellInfo) { return spellInfo->RecoveryTime > spellInfo->CategoryRecoveryTime ? spellInfo->RecoveryTime : spellInfo->CategoryRecoveryTime; }
+int32 GetSpellDuration(SpellEntry const *spellInfo);
+int32 GetSpellMaxDuration(SpellEntry const *spellInfo);
+
+inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect)
+{
+ for(int i= 0; i < 3; ++i)
+ if(spellInfo->Effect[i]==effect)
+ return true;
+ return false;
+}
+
+bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2);
+
+inline bool IsSealSpell(SpellEntry const *spellInfo)
+{
+ //Collection of all the seal family flags. No other paladin spell has any of those.
+ return spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN &&
+ ( spellInfo->SpellFamilyFlags & 0x4000A000200LL );
+}
+
+inline bool IsElementalShield(SpellEntry const *spellInfo)
+{
+ // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus
+ return (spellInfo->SpellFamilyFlags & 0x42000000400LL) || spellInfo->Id == 23552;
+}
+
+int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2);
+bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2);
+bool IsPassiveSpell(uint32 spellId);
+
+inline bool IsDeathPersistentSpell(SpellEntry const *spellInfo)
+{
+ switch(spellInfo->Id)
+ {
+ case 40214: // Dragonmaw Illusion
+ case 35480: case 35481: case 35482: // Human Illusion
+ case 35483: case 39824: // Human Illusion
+ return true;
+ }
+
+ return spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DEATH_PERSISTENT;
+}
+
+inline bool IsNonCombatSpell(SpellEntry const *spellInfo)
+{
+ return (spellInfo->Attributes & SPELL_ATTR_CANT_USED_IN_COMBAT) != 0;
+}
+
+bool IsPositiveSpell(uint32 spellId);
+bool IsPositiveEffect(uint32 spellId, uint32 effIndex);
+bool IsPositiveTarget(uint32 targetA, uint32 targetB);
+
+bool IsSingleTargetSpell(SpellEntry const *spellInfo);
+bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2);
+
+bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id);
+
+inline bool IsAreaEffectTarget( Targets target )
+{
+ switch (target )
+ {
+ case TARGET_AREAEFFECT_CUSTOM:
+ case TARGET_ALL_ENEMY_IN_AREA:
+ case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
+ case TARGET_ALL_PARTY_AROUND_CASTER:
+ case TARGET_ALL_AROUND_CASTER:
+ case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
+ case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER:
+ case TARGET_ALL_PARTY:
+ case TARGET_ALL_PARTY_AROUND_CASTER_2:
+ case TARGET_AREAEFFECT_PARTY:
+ case TARGET_AREAEFFECT_CUSTOM_2:
+ case TARGET_AREAEFFECT_PARTY_AND_CLASS:
+ case TARGET_IN_FRONT_OF_CASTER:
+ case TARGET_ALL_FRIENDLY_UNITS_IN_AREA:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo)
+{
+ if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[0])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[0])))
+ return true;
+ if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[1])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[1])))
+ return true;
+ if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[2])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[2])))
+ return true;
+ return false;
+}
+
+inline bool IsAreaAuraEffect(uint32 effect)
+{
+ if( effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY ||
+ effect == SPELL_EFFECT_APPLY_AREA_AURA_FRIEND ||
+ effect == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY ||
+ effect == SPELL_EFFECT_APPLY_AREA_AURA_PET ||
+ effect == SPELL_EFFECT_APPLY_AREA_AURA_OWNER)
+ return true;
+ return false;
+}
+
+inline bool IsDispelSpell(SpellEntry const *spellInfo)
+{
+ if (spellInfo->Effect[0] == SPELL_EFFECT_DISPEL ||
+ spellInfo->Effect[1] == SPELL_EFFECT_DISPEL ||
+ spellInfo->Effect[2] == SPELL_EFFECT_DISPEL )
+ return true;
+ return false;
+}
+inline bool isSpellBreakStealth(SpellEntry const* spellInfo)
+{
+ return !(spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH);
+}
+
+uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form);
+
+inline bool IsChanneledSpell(SpellEntry const* spellInfo)
+{
+ return (spellInfo->AttributesEx & (SPELL_ATTR_EX_CHANNELED_1 | SPELL_ATTR_EX_CHANNELED_2));
+}
+
+inline bool NeedsComboPoints(SpellEntry const* spellInfo)
+{
+ return (spellInfo->AttributesEx & (SPELL_ATTR_EX_REQ_COMBO_POINTS1 | SPELL_ATTR_EX_REQ_COMBO_POINTS2));
+}
+
+inline SpellSchoolMask GetSpellSchoolMask(SpellEntry const* spellInfo)
+{
+ return SpellSchoolMask(spellInfo->SchoolMask);
+}
+
+inline uint32 GetSpellMechanicMask(SpellEntry const* spellInfo, int32 effect)
+{
+ uint32 mask = 0;
+ if (spellInfo->Mechanic)
+ mask |= 1<<spellInfo->Mechanic;
+ if (spellInfo->EffectMechanic[effect])
+ mask |= 1<<spellInfo->EffectMechanic[effect];
+ return mask;
+}
+
+inline Mechanics GetEffectMechanic(SpellEntry const* spellInfo, int32 effect)
+{
+ if (spellInfo->EffectMechanic[effect])
+ return Mechanics(spellInfo->EffectMechanic[effect]);
+ if (spellInfo->Mechanic)
+ return Mechanics(spellInfo->Mechanic);
+ return MECHANIC_NONE;
+}
+
+inline uint32 GetDispellMask(DispelType dispel)
+{
+ // If dispell all
+ if (dispel == DISPEL_ALL)
+ return DISPEL_ALL_MASK;
+ else
+ return (1 << dispel);
+}
+
+// Diminishing Returns interaction with spells
+DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered);
+bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group);
+DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group);
+
+// Spell affects related declarations (accessed using SpellMgr functions)
+typedef std::map<uint32, uint64> SpellAffectMap;
+
+// Spell proc event related declarations (accessed using SpellMgr functions)
+enum ProcFlags
+{
+ PROC_FLAG_NONE = 0x00000000, // None
+ PROC_FLAG_HIT_MELEE = 0x00000001, // On melee hit
+ PROC_FLAG_STRUCK_MELEE = 0x00000002, // On being struck melee
+ PROC_FLAG_KILL_XP_GIVER = 0x00000004, // On kill target giving XP or honor
+ PROC_FLAG_SPECIAL_DROP = 0x00000008, //
+ PROC_FLAG_DODGE = 0x00000010, // On dodge melee attack
+ PROC_FLAG_PARRY = 0x00000020, // On parry melee attack
+ PROC_FLAG_BLOCK = 0x00000040, // On block attack
+ PROC_FLAG_TOUCH = 0x00000080, // On being touched (for bombs, probably?)
+ PROC_FLAG_TARGET_LOW_HEALTH = 0x00000100, // On deal damage to enemy with 20% or less health
+ PROC_FLAG_LOW_HEALTH = 0x00000200, // On health dropped below 20%
+ PROC_FLAG_STRUCK_RANGED = 0x00000400, // On being struck ranged
+ PROC_FLAG_HIT_SPECIAL = 0x00000800, // (!)Removed, may be reassigned in future
+ PROC_FLAG_CRIT_MELEE = 0x00001000, // On crit melee
+ PROC_FLAG_STRUCK_CRIT_MELEE = 0x00002000, // On being critically struck in melee
+ PROC_FLAG_CAST_SPELL = 0x00004000, // On cast spell
+ PROC_FLAG_TAKE_DAMAGE = 0x00008000, // On take damage
+ PROC_FLAG_CRIT_SPELL = 0x00010000, // On crit spell
+ PROC_FLAG_HIT_SPELL = 0x00020000, // On hit spell
+ PROC_FLAG_STRUCK_CRIT_SPELL = 0x00040000, // On being critically struck by a spell
+ PROC_FLAG_HIT_RANGED = 0x00080000, // On getting ranged hit
+ PROC_FLAG_STRUCK_SPELL = 0x00100000, // On being struck by a spell
+ PROC_FLAG_TRAP = 0x00200000, // On trap activation (?)
+ PROC_FLAG_CRIT_RANGED = 0x00400000, // On getting ranged crit
+ PROC_FLAG_STRUCK_CRIT_RANGED = 0x00800000, // On being critically struck by a ranged attack
+ PROC_FLAG_RESIST_SPELL = 0x01000000, // On resist enemy spell
+ PROC_FLAG_TARGET_RESISTS = 0x02000000, // On enemy resisted spell
+ PROC_FLAG_TARGET_DODGE_OR_PARRY = 0x04000000, // On enemy dodges/parries
+ PROC_FLAG_HEAL = 0x08000000, // On heal
+ PROC_FLAG_CRIT_HEAL = 0x10000000, // On critical healing effect
+ PROC_FLAG_HEALED = 0x20000000, // On healing
+ PROC_FLAG_TARGET_BLOCK = 0x40000000, // On enemy blocks
+ PROC_FLAG_MISS = 0x80000000 // On miss melee attack
+};
+
+struct SpellProcEventEntry
+{
+ uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2
+ uint32 category; // if nonzero - match proc condition based on candidate spell's category
+ uint32 skillId; // if nonzero - for matching proc condition based on candidate spell's skillId from SkillLineAbility.dbc (Shadow Bolt = Destruction)
+ uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value
+ uint64 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do)
+ uint32 procFlags; // bitmask for matching proc event
+ float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc
+ uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_
+};
+
+typedef HM_NAMESPACE::hash_map<uint32, SpellProcEventEntry> SpellProcEventMap;
+
+#define ELIXIR_BATTLE_MASK 0x1
+#define ELIXIR_GUARDIAN_MASK 0x2
+#define ELIXIR_FLASK_MASK (ELIXIR_BATTLE_MASK|ELIXIR_GUARDIAN_MASK)
+#define ELIXIR_UNSTABLE_MASK 0x4
+#define ELIXIR_SHATTRATH_MASK 0x8
+
+typedef std::map<uint32, uint8> SpellElixirMap;
+
+// Spell script target related declarations (accessed using SpellMgr functions)
+enum SpellTargetType
+{
+ SPELL_TARGET_TYPE_GAMEOBJECT = 0,
+ SPELL_TARGET_TYPE_CREATURE = 1,
+ SPELL_TARGET_TYPE_DEAD = 2
+};
+
+#define MAX_SPELL_TARGET_TYPE 3
+
+struct SpellTargetEntry
+{
+ SpellTargetEntry(SpellTargetType type_,uint32 targetEntry_) : type(type_), targetEntry(targetEntry_) {}
+ SpellTargetType type;
+ uint32 targetEntry;
+};
+
+typedef std::multimap<uint32,SpellTargetEntry> SpellScriptTarget;
+
+// coordinates for spells (accessed using SpellMgr functions)
+struct SpellTargetPosition
+{
+ uint32 target_mapId;
+ float target_X;
+ float target_Y;
+ float target_Z;
+ float target_Orientation;
+};
+
+typedef HM_NAMESPACE::hash_map<uint32, SpellTargetPosition> SpellTargetPositionMap;
+
+// Spell pet auras
+class PetAura
+{
+ public:
+ PetAura()
+ {
+ auras.clear();
+ }
+
+ PetAura(uint16 petEntry, uint16 aura, bool _removeOnChangePet, int _damage) :
+ removeOnChangePet(_removeOnChangePet), damage(_damage)
+ {
+ auras[petEntry] = aura;
+ }
+
+ uint16 GetAura(uint16 petEntry) const
+ {
+ std::map<uint16, uint16>::const_iterator itr = auras.find(petEntry);
+ if(itr != auras.end())
+ return itr->second;
+ else
+ {
+ std::map<uint16, uint16>::const_iterator itr = auras.find(0);
+ if(itr != auras.end())
+ return itr->second;
+ else
+ return 0;
+ }
+ }
+
+ void AddAura(uint16 petEntry, uint16 aura)
+ {
+ auras[petEntry] = aura;
+ }
+
+ bool IsRemovedOnChangePet() const
+ {
+ return removeOnChangePet;
+ }
+
+ int32 GetDamage() const
+ {
+ return damage;
+ }
+
+ private:
+ std::map<uint16, uint16> auras;
+ bool removeOnChangePet;
+ int32 damage;
+};
+typedef std::map<uint16, PetAura> SpellPetAuraMap;
+
+// Spell rank chain (accessed using SpellMgr functions)
+struct SpellChainNode
+{
+ uint32 prev;
+ uint32 first;
+ uint32 req;
+ uint8 rank;
+};
+
+typedef HM_NAMESPACE::hash_map<uint32, SpellChainNode> SpellChainMap;
+typedef std::multimap<uint32, uint32> SpellChainMapNext;
+
+// Spell learning properties (accessed using SpellMgr functions)
+struct SpellLearnSkillNode
+{
+ uint32 skill;
+ uint32 value; // 0 - max skill value for player level
+ uint32 maxvalue; // 0 - max skill value for player level
+};
+
+typedef std::map<uint32, SpellLearnSkillNode> SpellLearnSkillMap;
+
+struct SpellLearnSpellNode
+{
+ uint32 spell;
+ bool autoLearned;
+};
+
+typedef std::multimap<uint32, SpellLearnSpellNode> SpellLearnSpellMap;
+
+typedef std::multimap<uint32, SkillLineAbilityEntry const*> SkillLineAbilityMap;
+
+inline bool IsPrimaryProfessionSkill(uint32 skill)
+{
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill);
+ if(!pSkill)
+ return false;
+
+ if(pSkill->categoryId != SKILL_CATEGORY_PROFESSION)
+ return false;
+
+ return true;
+}
+
+inline bool IsProfessionSkill(uint32 skill)
+{
+ return IsPrimaryProfessionSkill(skill) || skill == SKILL_FISHING || skill == SKILL_COOKING || skill == SKILL_FIRST_AID;
+}
+
+class SpellMgr
+{
+ // Constructors
+ public:
+ SpellMgr();
+ ~SpellMgr();
+
+ // Accessors (const or static functions)
+ public:
+ // Spell affects
+ uint64 GetSpellAffectMask(uint16 spellId, uint8 effectId) const
+ {
+ SpellAffectMap::const_iterator itr = mSpellAffectMap.find((spellId<<8) + effectId);
+ if( itr != mSpellAffectMap.end( ) )
+ return itr->second;
+ return 0;
+ }
+
+ bool IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const;
+
+ SpellElixirMap const& GetSpellElixirMap() const { return mSpellElixirs; }
+
+ uint32 GetSpellElixirMask(uint32 spellid) const
+ {
+ SpellElixirMap::const_iterator itr = mSpellElixirs.find(spellid);
+ if(itr==mSpellElixirs.end())
+ return 0x0;
+
+ return itr->second;
+ }
+
+ SpellSpecific GetSpellElixirSpecific(uint32 spellid) const
+ {
+ uint32 mask = GetSpellElixirMask(spellid);
+ if((mask & ELIXIR_FLASK_MASK)==ELIXIR_FLASK_MASK)
+ return SPELL_FLASK_ELIXIR;
+ else if(mask & ELIXIR_BATTLE_MASK)
+ return SPELL_BATTLE_ELIXIR;
+ else if(mask & ELIXIR_GUARDIAN_MASK)
+ return SPELL_GUARDIAN_ELIXIR;
+ else
+ return SPELL_NORMAL;
+ }
+
+ // Spell proc events
+ SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const
+ {
+ SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId);
+ if( itr != mSpellProcEventMap.end( ) )
+ return &itr->second;
+ return NULL;
+ }
+
+ static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags );
+
+ // Spell target coordinates
+ SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id) const
+ {
+ SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find( spell_id );
+ if( itr != mSpellTargetPositions.end( ) )
+ return &itr->second;
+ return NULL;
+ }
+
+ // Spell ranks chains
+ SpellChainNode const* GetSpellChainNode(uint32 spell_id) const
+ {
+ SpellChainMap::const_iterator itr = mSpellChains.find(spell_id);
+ if(itr == mSpellChains.end())
+ return NULL;
+
+ return &itr->second;
+ }
+
+ uint32 GetFirstSpellInChain(uint32 spell_id) const
+ {
+ if(SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->first;
+
+ return spell_id;
+ }
+
+ uint32 GetPrevSpellInChain(uint32 spell_id) const
+ {
+ if(SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->prev;
+
+ return 0;
+ }
+
+ SpellChainMapNext const& GetSpellChainNext() const { return mSpellChainsNext; }
+
+ // 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))
+ return node->rank;
+
+ return 0;
+ }
+
+ uint8 IsHighRankOfSpell(uint32 spell1,uint32 spell2) const
+ {
+ SpellChainMap::const_iterator itr = mSpellChains.find(spell1);
+
+ uint32 rank2 = GetSpellRank(spell2);
+
+ // not ordered correctly by rank value
+ if(itr == mSpellChains.end() || !rank2 || itr->second.rank <= rank2)
+ return false;
+
+ // check present in same rank chain
+ for(; itr != mSpellChains.end(); itr = mSpellChains.find(itr->second.prev))
+ if(itr->second.prev==spell2)
+ return true;
+
+ return false;
+ }
+
+ bool IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const;
+ static bool canStackSpellRanks(SpellEntry const *spellInfo);
+ bool IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const;
+
+ SpellEntry const* SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const;
+
+ // Spell learning
+ SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const
+ {
+ SpellLearnSkillMap::const_iterator itr = mSpellLearnSkills.find(spell_id);
+ if(itr != mSpellLearnSkills.end())
+ return &itr->second;
+ else
+ return NULL;
+ }
+
+ bool IsSpellLearnSpell(uint32 spell_id) const
+ {
+ return mSpellLearnSpells.count(spell_id)!=0;
+ }
+
+ SpellLearnSpellMap::const_iterator GetBeginSpellLearnSpell(uint32 spell_id) const
+ {
+ return mSpellLearnSpells.lower_bound(spell_id);
+ }
+
+ SpellLearnSpellMap::const_iterator GetEndSpellLearnSpell(uint32 spell_id) const
+ {
+ return mSpellLearnSpells.upper_bound(spell_id);
+ }
+
+ bool IsSpellLearnToSpell(uint32 spell_id1,uint32 spell_id2) const
+ {
+ SpellLearnSpellMap::const_iterator b = GetBeginSpellLearnSpell(spell_id1);
+ SpellLearnSpellMap::const_iterator e = GetEndSpellLearnSpell(spell_id1);
+ for(SpellLearnSpellMap::const_iterator i = b; i != e; ++i)
+ if(i->second.spell==spell_id2)
+ return true;
+ return false;
+ }
+
+ static bool IsProfessionSpell(uint32 spellId);
+ static bool IsPrimaryProfessionSpell(uint32 spellId);
+ bool IsPrimaryProfessionFirstRankSpell(uint32 spellId) const;
+
+ // Spell script targets
+ SpellScriptTarget::const_iterator GetBeginSpellScriptTarget(uint32 spell_id) const
+ {
+ return mSpellScriptTarget.lower_bound(spell_id);
+ }
+
+ SpellScriptTarget::const_iterator GetEndSpellScriptTarget(uint32 spell_id) const
+ {
+ return mSpellScriptTarget.upper_bound(spell_id);
+ }
+
+ // Spell correctess for client using
+ static bool IsSpellValid(SpellEntry const * spellInfo, Player* pl = NULL, bool msg = true);
+
+ SkillLineAbilityMap::const_iterator GetBeginSkillLineAbilityMap(uint32 spell_id) const
+ {
+ return mSkillLineAbilityMap.lower_bound(spell_id);
+ }
+
+ SkillLineAbilityMap::const_iterator GetEndSkillLineAbilityMap(uint32 spell_id) const
+ {
+ return mSkillLineAbilityMap.upper_bound(spell_id);
+ }
+
+ PetAura const* GetPetAura(uint16 spell_id)
+ {
+ SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find(spell_id);
+ if(itr != mSpellPetAuraMap.end())
+ return &itr->second;
+ else
+ return NULL;
+ }
+
+ // Modifiers
+ public:
+ static SpellMgr& Instance();
+
+ // Loading data at server startup
+ void LoadSpellChains();
+ void LoadSpellLearnSkills();
+ void LoadSpellLearnSpells();
+ void LoadSpellScriptTarget();
+ void LoadSpellAffects();
+ void LoadSpellElixirs();
+ void LoadSpellProcEvents();
+ void LoadSpellTargetPositions();
+ void LoadSpellThreats();
+ void LoadSkillLineAbilityMap();
+ void LoadSpellPetAuras();
+
+ private:
+ SpellScriptTarget mSpellScriptTarget;
+ SpellChainMap mSpellChains;
+ SpellChainMapNext mSpellChainsNext;
+ SpellLearnSkillMap mSpellLearnSkills;
+ SpellLearnSpellMap mSpellLearnSpells;
+ SpellTargetPositionMap mSpellTargetPositions;
+ SpellAffectMap mSpellAffectMap;
+ SpellElixirMap mSpellElixirs;
+ SpellProcEventMap mSpellProcEventMap;
+ SkillLineAbilityMap mSkillLineAbilityMap;
+ SpellPetAuraMap mSpellPetAuraMap;
+};
+
+#define spellmgr SpellMgr::Instance()
+#endif
diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp
new file mode 100644
index 00000000000..b89346d937a
--- /dev/null
+++ b/src/game/StatSystem.cpp
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Unit.h"
+#include "Player.h"
+#include "Pet.h"
+#include "Creature.h"
+#include "SharedDefines.h"
+#include "SpellAuras.h"
+
+/*#######################################
+######## ########
+######## PLAYERS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Player::UpdateStats(Stats stat)
+{
+ if(stat > STAT_SPIRIT)
+ return false;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = GetTotalStatValue(stat);
+
+ SetStat(stat, int32(value));
+
+ if(stat == STAT_STAMINA || stat == STAT_INTELLECT)
+ {
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateStats(stat);
+ }
+
+ switch(stat)
+ {
+ case STAT_STRENGTH:
+ UpdateAttackPowerAndDamage();
+ UpdateShieldBlockValue();
+ break;
+ case STAT_AGILITY:
+ UpdateArmor();
+ UpdateAttackPowerAndDamage(true);
+ if(getClass() == CLASS_ROGUE || getClass() == CLASS_HUNTER || getClass() == CLASS_DRUID && m_form==FORM_CAT)
+ UpdateAttackPowerAndDamage();
+
+ UpdateAllCritPercentages();
+ UpdateDodgePercentage();
+ break;
+
+ case STAT_STAMINA: UpdateMaxHealth(); break;
+ case STAT_INTELLECT:
+ UpdateMaxPower(POWER_MANA);
+ UpdateAllSpellCritChances();
+ UpdateAttackPowerAndDamage(true); //SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, only intelect currently
+ UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
+ break;
+
+ case STAT_SPIRIT:
+ break;
+
+ default:
+ break;
+ }
+ UpdateSpellDamageAndHealingBonus();
+ UpdateManaRegen();
+ return true;
+}
+
+void Player::UpdateSpellDamageAndHealingBonus()
+{
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Get healing bonus for all schools
+ SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL));
+ // Get damage bonus for all schools
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i)));
+}
+
+bool Player::UpdateAllStats()
+{
+ for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ float value = GetTotalStatValue(Stats(i));
+ SetStat(Stats(i), (int32)value);
+ }
+
+ UpdateAttackPowerAndDamage();
+ UpdateAttackPowerAndDamage(true);
+ UpdateArmor();
+ UpdateMaxHealth();
+
+ for(int i = POWER_MANA; i < MAX_POWERS; i++)
+ UpdateMaxPower(Powers(i));
+
+ UpdateAllCritPercentages();
+ UpdateAllSpellCritChances();
+ UpdateDefenseBonusesMod();
+ UpdateShieldBlockValue();
+ UpdateSpellDamageAndHealingBonus();
+ UpdateManaRegen();
+ UpdateExpertise(BASE_ATTACK);
+ UpdateExpertise(OFF_ATTACK);
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Player::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+ SetResistance(SpellSchools(school), int32(value));
+
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateResistances(school);
+ }
+ else
+ UpdateArmor();
+}
+
+void Player::UpdateArmor()
+{
+ float value = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ARMOR;
+
+ value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items)
+ value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items
+ value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats
+ value += GetModifierValue(unitMod, TOTAL_VALUE);
+
+ //add dynamic flat mods
+ AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
+ }
+
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetArmor(int32(value));
+
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateArmor();
+}
+
+float Player::GetHealthBonusFromStamina()
+{
+ float stamina = GetStat(STAT_STAMINA);
+
+ float baseStam = stamina < 20 ? stamina : 20;
+ float moreStam = stamina - baseStam;
+
+ return baseStam + (moreStam*10.0f);
+}
+
+float Player::GetManaBonusFromIntellect()
+{
+ float intellect = GetStat(STAT_INTELLECT);
+
+ float baseInt = intellect < 20 ? intellect : 20;
+ float moreInt = intellect - baseInt;
+
+ return baseInt + (moreInt*15.0f);
+}
+
+void Player::UpdateMaxHealth()
+{
+ UnitMods unitMod = UNIT_MOD_HEALTH;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxHealth((uint32)value);
+}
+
+void Player::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+
+ float bonusPower = (power == POWER_MANA) ? GetManaBonusFromIntellect() : 0;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxPower(power, uint32(value));
+}
+
+void Player::UpdateAttackPowerAndDamage(bool ranged )
+{
+ float val2 = 0.0f;
+ float level = float(getLevel());
+
+ UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
+
+ uint16 index = UNIT_FIELD_ATTACK_POWER;
+ uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
+ uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
+
+ if(ranged)
+ {
+ index = UNIT_FIELD_RANGED_ATTACK_POWER;
+ index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
+ index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
+
+ switch(getClass())
+ {
+ case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_ROGUE: val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_WARRIOR:val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_DRUID:
+ switch(m_form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ val2 = 0.0f; break;
+ default:
+ val2 = GetStat(STAT_AGILITY) - 10.0f; break;
+ }
+ break;
+ default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
+ }
+ }
+ else
+ {
+ switch(getClass())
+ {
+ case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
+ case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
+ case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_DRUID:
+ {
+ //Check if Predatory Strikes is skilled
+ float mLevelMult = 0.0;
+ switch(m_form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_MOONKIN:
+ {
+ Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
+ {
+ // Predatory Strikes
+ if ((*itr)->GetSpellProto()->SpellIconID == 1563)
+ {
+ mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ switch(m_form)
+ {
+ case FORM_CAT:
+ val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f; break;
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case FORM_MOONKIN:
+ val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ default:
+ val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ }
+ break;
+ }
+ case CLASS_MAGE: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ case CLASS_PRIEST: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ case CLASS_WARLOCK: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ }
+ }
+
+ SetModifierValue(unitMod, BASE_VALUE, val2);
+
+ float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
+ float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
+
+ //add dynamic flat mods
+ if( ranged && (getClassMask() & CLASSMASK_WAND_USERS)==0)
+ {
+ AuraList const& mRAPbyIntellect = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mRAPbyIntellect.begin();i != mRAPbyIntellect.end(); ++i)
+ attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+
+ float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+
+ SetUInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
+ SetUInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
+ SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+
+ //automatically update weapon damage after attack power modification
+ if(ranged)
+ {
+ UpdateDamagePhysical(RANGED_ATTACK);
+
+ Pet *pet = GetPet(); //update pet's AP
+ if(pet)
+ pet->UpdateAttackPowerAndDamage();
+ }
+ else
+ {
+ UpdateDamagePhysical(BASE_ATTACK);
+ if(CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
+ UpdateDamagePhysical(OFF_ATTACK);
+ }
+}
+
+void Player::UpdateShieldBlockValue()
+{
+ SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
+}
+
+void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
+{
+ UnitMods unitMod;
+ UnitMods attPower;
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ default:
+ unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+ attPower = UNIT_MOD_ATTACK_POWER;
+ break;
+ case OFF_ATTACK:
+ unitMod = UNIT_MOD_DAMAGE_OFFHAND;
+ attPower = UNIT_MOD_ATTACK_POWER;
+ break;
+ case RANGED_ATTACK:
+ unitMod = UNIT_MOD_DAMAGE_RANGED;
+ attPower = UNIT_MOD_ATTACK_POWER_RANGED;
+ break;
+ }
+
+ float att_speed = GetAPMultiplier(attType,normalized);
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
+
+ if (IsInFeralForm()) //check if player is druid and in cat or bear forms
+ {
+ uint32 lvl = getLevel();
+ if ( lvl > 60 ) lvl = 60;
+
+ weapon_mindamage = lvl*0.85*att_speed;
+ weapon_maxdamage = lvl*1.25*att_speed;
+ }
+ else if(!IsUseEquipedWeapon(attType==BASE_ATTACK)) //check if player not in form but still can't use weapon (broken/etc)
+ {
+ weapon_mindamage = BASE_MINDAMAGE;
+ weapon_maxdamage = BASE_MAXDAMAGE;
+ }
+ else if(attType == RANGED_ATTACK) //add ammo DPS to ranged damage
+ {
+ weapon_mindamage += GetAmmoDPS() * att_speed;
+ weapon_maxdamage += GetAmmoDPS() * att_speed;
+ }
+
+ min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
+ max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
+}
+
+void Player::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ float mindamage;
+ float maxdamage;
+
+ CalculateMinMaxDamage(attType,false,mindamage,maxdamage);
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ default:
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
+ break;
+ case OFF_ATTACK:
+ SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
+ break;
+ case RANGED_ATTACK:
+ SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
+ break;
+ }
+}
+
+void Player::UpdateDefenseBonusesMod()
+{
+ UpdateBlockPercentage();
+ UpdateParryPercentage();
+ UpdateDodgePercentage();
+}
+
+void Player::UpdateBlockPercentage()
+{
+ // Base value
+ float value = 5.0f;
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
+ // Increase from rating
+ value += GetRatingBonusValue(CR_BLOCK);
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
+}
+
+void Player::UpdateCritPercentage(WeaponAttackType attType)
+{
+ BaseModGroup modGroup;
+ uint16 index;
+ CombatRating cr;
+
+ switch(attType)
+ {
+ case OFF_ATTACK:
+ modGroup = OFFHAND_CRIT_PERCENTAGE;
+ index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
+ cr = CR_CRIT_MELEE;
+ break;
+ case RANGED_ATTACK:
+ modGroup = RANGED_CRIT_PERCENTAGE;
+ index = PLAYER_RANGED_CRIT_PERCENTAGE;
+ cr = CR_CRIT_RANGED;
+ break;
+ case BASE_ATTACK:
+ default:
+ modGroup = CRIT_PERCENTAGE;
+ index = PLAYER_CRIT_PERCENTAGE;
+ cr = CR_CRIT_MELEE;
+ break;
+ }
+
+ float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
+ // Modify crit from weapon skill and maximized defense skill of same level victim difference
+ value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(index, value);
+}
+
+void Player::UpdateAllCritPercentages()
+{
+ float value = GetMeleeCritFromAgility();
+
+ SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
+ SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
+ SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
+
+ UpdateCritPercentage(BASE_ATTACK);
+ UpdateCritPercentage(OFF_ATTACK);
+ UpdateCritPercentage(RANGED_ATTACK);
+}
+
+void Player::UpdateParryPercentage()
+{
+ // Base parry
+ float value = 5.0f;
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
+ // Parry from rating
+ value += GetRatingBonusValue(CR_PARRY);
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
+}
+
+void Player::UpdateDodgePercentage()
+{
+ // Dodge from agility
+ float value = GetDodgeFromAgility();
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
+ // Dodge from rating
+ value += GetRatingBonusValue(CR_DODGE);
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
+}
+
+void Player::UpdateSpellCritChance(uint32 school)
+{
+ // For normal school set zero crit chance
+ if(school == SPELL_SCHOOL_NORMAL)
+ {
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
+ return;
+ }
+ // For others recalculate it from:
+ float crit = 0.0f;
+ // Crit from Intellect
+ crit += GetSpellCritFromIntellect();
+ // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
+ crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
+ // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
+ crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
+ // Increase crit from spell crit ratings
+ crit += GetRatingBonusValue(CR_CRIT_SPELL);
+
+ // Store crit value
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
+}
+
+void Player::UpdateAllSpellCritChances()
+{
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateSpellCritChance(i);
+}
+
+void Player::UpdateExpertise(WeaponAttackType attack)
+{
+ if(attack==RANGED_ATTACK)
+ return;
+
+ int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
+
+ Item *weapon = GetWeaponForAttack(attack);
+
+ AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
+ for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
+ {
+ // item neutral spell
+ if((*itr)->GetSpellProto()->EquippedItemClass == -1)
+ expertise += (*itr)->GetModifier()->m_amount;
+ // item dependent spell
+ else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
+ expertise += (*itr)->GetModifier()->m_amount;
+ }
+
+ if(expertise < 0)
+ expertise = 0;
+
+ switch(attack)
+ {
+ case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
+ case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
+ default: break;
+ }
+}
+
+void Player::UpdateManaRegen()
+{
+ float Intellect = GetStat(STAT_INTELLECT);
+ // Mana regen from spirit and intellect
+ float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit();
+ // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
+ power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
+
+ // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
+ float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f;
+
+ // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
+ AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
+ for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
+ }
+
+ // Bonus from some dummy auras
+ AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ if((*i)->GetId() == 34074) // Aspect of the Viper
+ {
+ power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f;
+ // Add regen bonus from level in this dummy
+ power_regen_mp5 += getLevel() * 35 / 100;
+ }
+
+ // Set regen rate in cast state apply only on spirit based regen
+ int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
+ if (modManaRegenInterrupt > 100)
+ modManaRegenInterrupt = 100;
+ SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
+
+ SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN, power_regen_mp5 + power_regen);
+}
+
+void Player::_ApplyAllStatBonuses()
+{
+ SetCanModifyStats(false);
+
+ _ApplyAllAuraMods();
+ _ApplyAllItemMods();
+
+ SetCanModifyStats(true);
+
+ UpdateAllStats();
+}
+
+void Player::_RemoveAllStatBonuses()
+{
+ SetCanModifyStats(false);
+
+ _RemoveAllItemMods();
+ _RemoveAllAuraMods();
+
+ SetCanModifyStats(true);
+
+ UpdateAllStats();
+}
+
+/*#######################################
+######## ########
+######## MOBS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Creature::UpdateStats(Stats /*stat*/)
+{
+ return true;
+}
+
+bool Creature::UpdateAllStats()
+{
+ UpdateMaxHealth();
+ UpdateAttackPowerAndDamage();
+
+ for(int i = POWER_MANA; i < MAX_POWERS; ++i)
+ UpdateMaxPower(Powers(i));
+
+ for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Creature::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+ SetResistance(SpellSchools(school), int32(value));
+ }
+ else
+ UpdateArmor();
+}
+
+void Creature::UpdateArmor()
+{
+ float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
+ SetArmor(int32(value));
+}
+
+void Creature::UpdateMaxHealth()
+{
+ float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
+ SetMaxHealth((uint32)value);
+}
+
+void Creature::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+
+ float value = GetTotalAuraModValue(unitMod);
+ SetMaxPower(power, uint32(value));
+}
+
+void Creature::UpdateAttackPowerAndDamage(bool ranged)
+{
+ if(ranged)
+ return;
+
+ //automatically update weapon damage after attack power modification
+ UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Creature::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ if(attType > BASE_ATTACK)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+
+ float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+
+ float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct ;
+ float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct ;
+
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
+}
+
+/*#######################################
+######## ########
+######## PETS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Pet::UpdateStats(Stats stat)
+{
+ if(stat > STAT_SPIRIT)
+ return false;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = GetTotalStatValue(stat);
+
+ Unit *owner = GetOwner();
+ if ( stat == STAT_STAMINA )
+ {
+ if(owner)
+ value += float(owner->GetStat(stat)) * 0.3f;
+ }
+ //warlock's and mage's pets gain 30% of owner's intellect
+ else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
+ {
+ if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
+ value += float(owner->GetStat(stat)) * 0.3f;
+ }
+
+ SetStat(stat, int32(value));
+
+ switch(stat)
+ {
+ case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
+ case STAT_AGILITY: UpdateArmor(); break;
+ case STAT_STAMINA: UpdateMaxHealth(); break;
+ case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
+ case STAT_SPIRIT:
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool Pet::UpdateAllStats()
+{
+ for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ UpdateStats(Stats(i));
+
+ for(int i = POWER_MANA; i < MAX_POWERS; i++)
+ UpdateMaxPower(Powers(i));
+
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Pet::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+
+ Unit *owner = GetOwner();
+ // hunter and warlock pets gain 40% of owner's resistance
+ if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
+ value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
+
+ SetResistance(SpellSchools(school), int32(value));
+ }
+ else
+ UpdateArmor();
+}
+
+void Pet::UpdateArmor()
+{
+ float value = 0.0f;
+ float bonus_armor = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ARMOR;
+
+ Unit *owner = GetOwner();
+ // hunter and warlock pets gain 35% of owner's armor value
+ if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
+ bonus_armor = 0.35f * float(owner->GetArmor());
+
+ value = GetModifierValue(unitMod, BASE_VALUE);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetStat(STAT_AGILITY) * 2.0f;
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetArmor(int32(value));
+}
+
+void Pet::UpdateMaxHealth()
+{
+ UnitMods unitMod = UNIT_MOD_HEALTH;
+ float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxHealth((uint32)value);
+}
+
+void Pet::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+ float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxPower(power, uint32(value));
+}
+
+void Pet::UpdateAttackPowerAndDamage(bool ranged)
+{
+ if(ranged)
+ return;
+
+ float val = 0.0f;
+ float bonusAP = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
+
+ if(GetEntry() == 416) // imp's attack power
+ val = GetStat(STAT_STRENGTH) - 10.0f;
+ else
+ val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
+
+ Unit* owner = GetOwner();
+ if( owner && owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power
+ {
+ bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
+ SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.125f));
+ }
+ //demons benefit from warlocks shadow or fire damage
+ else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
+ {
+ int32 fire = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FIRE);
+ int32 shadow = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_SHADOW);
+ int32 maximum = (fire > shadow) ? fire : shadow;
+ if(maximum < 0)
+ maximum = 0;
+ SetBonusDamage( int32(maximum * 0.15f));
+ bonusAP = maximum * 0.57f;
+ }
+ //water elementals benefit from mage's frost damage
+ else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
+ {
+ int32 frost = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FROST);
+ if(frost < 0)
+ frost = 0;
+ SetBonusDamage( int32(frost * 0.4f));
+ }
+ }
+
+ SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
+
+ //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
+ float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
+ float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
+ float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER field
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower);
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod);
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+ SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
+
+ //automatically update weapon damage after attack power modification
+ UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Pet::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ if(attType > BASE_ATTACK)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+
+ float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+
+ float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
+ float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
+
+ // Pet's base damage changes depending on happiness
+ if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
+ {
+ switch(GetHappinessState())
+ {
+ case HAPPY:
+ // 125% of normal damage
+ mindamage = mindamage * 1.25;
+ maxdamage = maxdamage * 1.25;
+ break;
+ case CONTENT:
+ // 100% of normal damage, nothing to modify
+ break;
+ case UNHAPPY:
+ // 75% of normal damage
+ mindamage = mindamage * 0.75;
+ maxdamage = maxdamage * 0.75;
+ break;
+ }
+ }
+
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
+}
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp
new file mode 100644
index 00000000000..bba7426938d
--- /dev/null
+++ b/src/game/TargetedMovementGenerator.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ByteBuffer.h"
+#include "TargetedMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+
+#define SMALL_ALPHA 0.05f
+
+#include <cmath>
+/*
+struct StackCleaner
+{
+ Creature &i_creature;
+ StackCleaner(Creature &creature) : i_creature(creature) {}
+ void Done(void) { i_creature.StopMoving(); }
+ ~StackCleaner()
+ {
+ i_creature->Clear();
+ }
+};
+*/
+
+template<class T>
+void
+TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if( !i_target.isValid() || !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) )
+ return;
+
+ // prevent redundant micro-movement for pets, other followers.
+ if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
+ return;
+
+ float x, y, z;
+ if(!i_offset)
+ {
+ // to nearest contact position
+ i_target->GetContactPoint( &owner, x, y, z );
+ }
+ else
+ {
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle);
+ }
+
+ /*
+ We MUST not check the distance difference and avoid setting the new location for smaller distances.
+ By that we risk having far too many GetContactPoint() calls freezing the whole system.
+ In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
+ some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
+ If the distance to the target it too large to ignore,
+ but the distance to the new contact point is short enough to be ignored,
+ we will calculate a new contact point each update loop, but will never move to it.
+ The system will freeze.
+ ralf
+
+ //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
+ float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE;
+ if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
+ return;
+ */
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ owner.addUnitState(UNIT_STAT_CHASE);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setTargetLocation(owner);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_CHASE);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if(!i_target.isValid())
+ return false;
+
+ if( !&owner || !owner.isAlive())
+ return true;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED) )
+ return true;
+
+ // prevent movement while casting spells with cast time or channel time
+ if ( owner.IsNonMeleeSpellCasted(false, false, true))
+ {
+ if (!owner.IsStopped())
+ owner.StopMoving();
+ return true;
+ }
+
+ // prevent crash after creature killed pet
+ if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ if( !i_destinationHolder.HasDestination() )
+ _setTargetLocation(owner);
+ if( owner.IsStopped() && !i_destinationHolder.HasArrived() )
+ {
+ owner.addUnitState(UNIT_STAT_CHASE);
+ i_destinationHolder.StartTravel(traveller);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
+ {
+ // put targeted movement generators on a higher priority
+ if (owner.GetObjectSize())
+ i_destinationHolder.ResetUpdate(50);
+
+ float dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+
+ //More distance let have better performance, less distance let have more sensitive reaction at target move.
+
+ // try to counter precision differences
+ if( i_destinationHolder.GetDistance2dFromDestSq(*i_target.getTarget()) >= dist * dist)
+ {
+ owner.SetInFront(i_target.getTarget()); // Set new Angle For Map::
+ _setTargetLocation(owner); //Calculate New Dest and Send data To Player
+ }
+ // Update the Angle of the target only for Map::, no need to send packet for player
+ else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) )
+ owner.SetInFront(i_target.getTarget());
+
+ if(( owner.IsStopped() && !i_destinationHolder.HasArrived() ) || i_recalculateTravel )
+ {
+ i_recalculateTravel = false;
+ //Angle update will take place into owner.StopMoving()
+ owner.SetInFront(i_target.getTarget());
+
+ owner.StopMoving();
+ if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ owner.Attack(i_target.getTarget(),true);
+ }
+ }
+ return true;
+}
+
+template<class T>
+Unit*
+TargetedMovementGenerator<T>::GetTarget() const
+{
+ return i_target.getTarget();
+}
+
+template void TargetedMovementGenerator<Player>::_setTargetLocation(Player &);
+template void TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void TargetedMovementGenerator<Player>::Initialize(Player &);
+template void TargetedMovementGenerator<Creature>::Initialize(Creature &);
+template void TargetedMovementGenerator<Player>::Finalize(Player &);
+template void TargetedMovementGenerator<Creature>::Finalize(Creature &);
+template void TargetedMovementGenerator<Player>::Reset(Player &);
+template void TargetedMovementGenerator<Creature>::Reset(Creature &);
+template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+template Unit* TargetedMovementGenerator<Player>::GetTarget() const;
+template Unit* TargetedMovementGenerator<Creature>::GetTarget() const;
diff --git a/src/game/TargetedMovementGenerator.h b/src/game/TargetedMovementGenerator.h
new file mode 100644
index 00000000000..725f07224ba
--- /dev/null
+++ b/src/game/TargetedMovementGenerator.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_TARGETEDMOVEMENTGENERATOR_H
+#define MANGOS_TARGETEDMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+#include "FollowerReference.h"
+
+class MANGOS_DLL_SPEC TargetedMovementGeneratorBase
+{
+ public:
+ TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ void stopFollowing() { }
+ protected:
+ FollowerReference i_target;
+};
+
+template<class T>
+class MANGOS_DLL_SPEC TargetedMovementGenerator
+: public MovementGeneratorMedium< T, TargetedMovementGenerator<T> >, public TargetedMovementGeneratorBase
+{
+ public:
+
+ TargetedMovementGenerator(Unit &target)
+ : TargetedMovementGeneratorBase(target), i_offset(0), i_angle(0), i_recalculateTravel(false) {}
+ TargetedMovementGenerator(Unit &target, float offset, float angle)
+ : TargetedMovementGeneratorBase(target), i_offset(offset), i_angle(angle), i_recalculateTravel(false) {}
+ ~TargetedMovementGenerator() {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+ MovementGeneratorType GetMovementGeneratorType() { return TARGETED_MOTION_TYPE; }
+
+ Unit* GetTarget() const;
+
+ bool GetDestination(float &x, float &y, float &z) const
+ {
+ if(!i_destinationHolder.HasDestination()) return false;
+ i_destinationHolder.GetDestination(x,y,z);
+ return true;
+ }
+
+ void unitSpeedChanged() { i_recalculateTravel=true; }
+ private:
+
+ void _setTargetLocation(T &);
+
+ float i_offset;
+ float i_angle;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ bool i_recalculateTravel;
+};
+#endif
diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp
new file mode 100644
index 00000000000..e804bec8463
--- /dev/null
+++ b/src/game/TaxiHandler.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "Path.h"
+#include "WaypointMovementGenerator.h"
+#include "DestinationHolderImp.h"
+
+#include <cassert>
+
+void WorldSession::HandleTaxiNodeStatusQueryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug( "WORLD: Received CMSG_TAXINODE_STATUS_QUERY" );
+
+ uint64 guid;
+
+ recv_data >> guid;
+ SendTaxiStatus( guid );
+}
+
+void WorldSession::SendTaxiStatus( uint64 guid )
+{
+ // cheating checks
+ Creature *unit = ObjectAccessor::GetCreature(*_player, guid);
+ if (!unit)
+ {
+ sLog.outDebug( "WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId());
+
+ // not found nearest
+ if(curloc == 0)
+ return;
+
+ sLog.outDebug( "WORLD: current location %u ",curloc);
+
+ WorldPacket data( SMSG_TAXINODE_STATUS, 9 );
+ data << guid;
+ data << uint8( GetPlayer( )->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0 );
+ SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_TAXINODE_STATUS" );
+}
+
+void WorldSession::HandleTaxiQueryAvailableNodesOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug( "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES" );
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // cheating checks
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTaxiQueryAvailableNodesOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // unknown taxi node case
+ if( SendLearnNewTaxiNode(unit) )
+ return;
+
+ // known taxi node case
+ SendTaxiMenu( unit );
+}
+
+void WorldSession::SendTaxiMenu( Creature* unit )
+{
+ // find current node
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId());
+
+ if ( curloc == 0 )
+ return;
+
+ sLog.outDebug( "WORLD: CMSG_TAXINODE_STATUS_QUERY %u ",curloc);
+
+ WorldPacket data( SMSG_SHOWTAXINODES, (4+8+4+8*4) );
+ data << uint32( 1 );
+ data << uint64( unit->GetGUID() );
+ data << uint32( curloc );
+ GetPlayer()->m_taxi.AppendTaximaskTo(data,GetPlayer()->isTaxiCheater());
+ SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_SHOWTAXINODES" );
+}
+
+void WorldSession::SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode )
+{
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ while(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE)
+ GetPlayer()->GetMotionMaster()->MovementExpired(false);
+
+ GetPlayer()->Mount( MountId );
+ GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path,pathNode);
+}
+
+bool WorldSession::SendLearnNewTaxiNode( Creature* unit )
+{
+ // find current node
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId());
+
+ if ( curloc == 0 )
+ return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
+
+ if( GetPlayer()->m_taxi.SetTaximaskNode(curloc) )
+ {
+ WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
+ SendPacket( &msg );
+
+ WorldPacket update( SMSG_TAXINODE_STATUS, 9 );
+ update << uint64( unit->GetGUID() );
+ update << uint8( 1 );
+ SendPacket( &update );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+void WorldSession::HandleActivateTaxiFarOpcode ( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXIEXPRESS" );
+
+ uint64 guid;
+ uint32 node_count, _totalcost;
+
+ recv_data >> guid >> _totalcost >> node_count;
+
+ Creature *npc = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!npc)
+ {
+ sLog.outDebug( "WORLD: HandleActivateTaxiFarOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,8+4+4+node_count*4);
+
+ std::vector<uint32> nodes;
+
+ for(uint32 i = 0; i < node_count; ++i)
+ {
+ uint32 node;
+ recv_data >> node;
+ nodes.push_back(node);
+ }
+
+ if(nodes.empty())
+ return;
+
+ sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d" ,nodes.front(),nodes.back());
+
+ GetPlayer()->ActivateTaxiPathTo(nodes, 0, npc);
+}
+
+void WorldSession::HandleTaxiNextDestinationOpcode(WorldPacket& /*recv_data*/)
+{
+ sLog.outDebug( "WORLD: Received CMSG_MOVE_SPLINE_DONE" );
+
+ // in taxi flight packet received in 2 case:
+ // 1) end taxi path in far (multi-node) flight
+ // 2) switch from one map to other in case multim-map taxi path
+ // we need proccess only (1)
+ uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination();
+ if(!curDest)
+ return;
+
+ TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
+
+ // far teleport case
+ if(curDestNode && curDestNode->map_id != GetPlayer()->GetMapId())
+ {
+ if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE)
+ {
+ // short preparations to continue flight
+ FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
+
+ flight->SetCurrentNodeAfterTeleport();
+ Path::PathNode const& node = flight->GetPath()[flight->GetCurrentNode()];
+ flight->SkipCurrentNode();
+
+ GetPlayer()->TeleportTo(curDestNode->map_id,node.x,node.y,node.z,GetPlayer()->GetOrientation());
+ }
+ return;
+ }
+
+ uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination();
+ if ( destinationnode > 0 ) // if more destinations to go
+ {
+ // current source node for next destination
+ uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource();
+
+ // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
+ if (GetPlayer()->isTaxiCheater())
+ {
+ if(GetPlayer()->m_taxi.SetTaximaskNode(sourcenode))
+ {
+ WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
+ _player->GetSession()->SendPacket( &data );
+ }
+ }
+
+ sLog.outDebug( "WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode );
+
+ uint16 MountId = objmgr.GetTaxiMount(sourcenode, GetPlayer()->GetTeam());
+
+ uint32 path, cost;
+ objmgr.GetTaxiPath( sourcenode, destinationnode, path, cost);
+
+ if(path && MountId)
+ SendDoFlight( MountId, path, 1 ); // skip start fly node
+ else
+ GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
+ }
+ else
+ GetPlayer()->m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
+}
+
+void WorldSession::HandleActivateTaxiOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXI" );
+
+ uint64 guid;
+ std::vector<uint32> nodes;
+ nodes.resize(2);
+
+ recv_data >> guid >> nodes[0] >> nodes[1];
+ sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXI from %d to %d" ,nodes[0],nodes[1]);
+ Creature *npc = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!npc)
+ {
+ sLog.outDebug( "WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)) );
+ return;
+ }
+
+ GetPlayer()->ActivateTaxiPathTo(nodes, 0, npc);
+}
diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp
new file mode 100644
index 00000000000..130c4a7904a
--- /dev/null
+++ b/src/game/TemporarySummon.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "TemporarySummon.h"
+#include "WorldPacket.h"
+#include "MapManager.h"
+#include "Log.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+
+TemporarySummon::TemporarySummon( uint64 summoner ) :
+Creature(), m_type(TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner)
+{
+}
+
+void TemporarySummon::Update( uint32 diff )
+{
+ switch(m_type)
+ {
+ case TEMPSUMMON_MANUAL_DESPAWN:
+ break;
+ case TEMPSUMMON_TIMED_DESPAWN:
+ {
+ if (m_timer <= diff)
+ {
+ UnSummon();
+ return;
+ }
+
+ m_timer -= diff;
+ break;
+ }
+ case TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT:
+ {
+ if (!isInCombat())
+ {
+ if (m_timer <= diff)
+ {
+ UnSummon();
+ return;
+ }
+
+ m_timer -= diff;
+ }
+ else if (m_timer != m_lifetime)
+ m_timer = m_lifetime;
+
+ break;
+ }
+
+ case TEMPSUMMON_CORPSE_TIMED_DESPAWN:
+ {
+ if ( m_deathState == CORPSE)
+ {
+ if (m_timer <= diff)
+ {
+ UnSummon();
+ return;
+ }
+
+ m_timer -= diff;
+ }
+ break;
+ }
+ case TEMPSUMMON_CORPSE_DESPAWN:
+ {
+ // if m_deathState is DEAD, CORPSE was skipped
+ if ( m_deathState == CORPSE || m_deathState == DEAD)
+ {
+ UnSummon();
+ return;
+ }
+
+ break;
+ }
+ case TEMPSUMMON_DEAD_DESPAWN:
+ {
+ if ( m_deathState == DEAD )
+ {
+ UnSummon();
+ return;
+ }
+ break;
+ }
+ case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN:
+ {
+ // if m_deathState is DEAD, CORPSE was skipped
+ if ( m_deathState == CORPSE || m_deathState == DEAD)
+ {
+ UnSummon();
+ return;
+ }
+
+ if (!isInCombat())
+ {
+ if (m_timer <= diff)
+ {
+ UnSummon();
+ return;
+ }
+ else
+ m_timer -= diff;
+ }
+ else if (m_timer != m_lifetime)
+ m_timer = m_lifetime;
+ break;
+ }
+ case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN:
+ {
+ // if m_deathState is DEAD, CORPSE was skipped
+ if (m_deathState == DEAD)
+ {
+ UnSummon();
+ return;
+ }
+
+ if (!isInCombat() && isAlive() )
+ {
+ if (m_timer <= diff)
+ {
+ UnSummon();
+ return;
+ }
+ else
+ m_timer -= diff;
+ }
+ else if (m_timer != m_lifetime)
+ m_timer = m_lifetime;
+ break;
+ }
+ default:
+ UnSummon();
+ sLog.outError("Temporary summoned creature (entry: %u) have unknown type %u of ",GetEntry(),m_type);
+ break;
+ }
+
+ Creature::Update( diff );
+}
+
+void TemporarySummon::Summon(TempSummonType type, uint32 lifetime)
+{
+ m_type = type;
+ m_timer = lifetime;
+ m_lifetime = lifetime;
+
+ MapManager::Instance().GetMap(GetMapId(), this)->Add((Creature*)this);
+
+ AIM_Initialize();
+}
+
+void TemporarySummon::UnSummon()
+{
+ CombatStop();
+
+ CleanupsBeforeDelete();
+ AddObjectToRemoveList();
+
+ Unit* sum = m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL;
+ if (sum && sum->GetTypeId() == TYPEID_UNIT && ((Creature*)sum)->AI())
+ {
+ ((Creature*)sum)->AI()->SummonedCreatureDespawn(this);
+ }
+}
+
+void TemporarySummon::SaveToDB()
+{
+}
diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h
new file mode 100644
index 00000000000..8007b853938
--- /dev/null
+++ b/src/game/TemporarySummon.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_TEMPSUMMON_H
+#define MANGOSSERVER_TEMPSUMMON_H
+
+#include "Creature.h"
+#include "ObjectAccessor.h"
+
+class TemporarySummon : public Creature
+{
+ public:
+ explicit TemporarySummon(uint64 summoner = 0);
+ virtual ~TemporarySummon(){};
+ void Update(uint32 time);
+ void Summon(TempSummonType type, uint32 lifetime);
+ void UnSummon();
+ void SaveToDB();
+ Unit* GetSummoner() const { return m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL; }
+ private:
+ TempSummonType m_type;
+ uint32 m_timer;
+ uint32 m_lifetime;
+ uint64 m_summoner;
+};
+#endif
diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp
new file mode 100644
index 00000000000..b9fd7a44f87
--- /dev/null
+++ b/src/game/ThreatManager.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "ThreatManager.h"
+#include "Unit.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Map.h"
+#include "MapManager.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "UnitEvents.h"
+
+//==============================================================
+//================= ThreatCalcHelper ===========================
+//==============================================================
+
+// 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() )
+ modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat);
+ }
+
+ float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask);
+ return threat;
+}
+
+//============================================================
+//================= HostilReference ==========================
+//============================================================
+
+HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat)
+{
+ iThreat = pThreat;
+ iTempThreatModifyer = 0.0f;
+ link(pUnit, pThreatManager);
+ iUnitGuid = pUnit->GetGUID();
+ iOnline = true;
+ iAccessible = true;
+}
+
+//============================================================
+// Tell our refTo (target) object that we have a link
+void HostilReference::targetObjectBuildLink()
+{
+ getTarget()->addHatedBy(this);
+}
+
+//============================================================
+// Tell our refTo (taget) object, that the link is cut
+void HostilReference::targetObjectDestroyLink()
+{
+ getTarget()->removeHatedBy(this);
+}
+
+//============================================================
+// Tell our refFrom (source) object, that the link is cut (Target destroyed)
+
+void HostilReference::sourceObjectDestroyLink()
+{
+ setOnlineOfflineState(false);
+}
+
+//============================================================
+// Inform the source, that the status of the reference changed
+
+void HostilReference::fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
+{
+ if(getSource())
+ getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
+}
+
+//============================================================
+
+void HostilReference::addThreat(float pMod)
+{
+ iThreat += pMod;
+ // the threat is changed. Source and target unit have to be availabe
+ // if the link was cut before relink it again
+ if(!isOnline())
+ updateOnlineStatus();
+ if(pMod != 0.0f)
+ fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod));
+ if(isValid() && pMod >= 0)
+ {
+ Unit* victim_owner = getTarget()->GetOwner();
+ if(victim_owner && victim_owner->isAlive())
+ getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
+ }
+}
+
+//============================================================
+// check, if source can reach target and set the status
+
+void HostilReference::updateOnlineStatus()
+{
+ bool online = false;
+ bool accessible = false;
+
+ if(!isValid())
+ {
+ Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
+ if(target)
+ link(target, getSource());
+ }
+ // only check for online status if
+ // ref is valid
+ // target is no player or not gamemaster
+ // target is not in flight
+ if(isValid() &&
+ ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
+ !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
+ {
+ Creature* creature = (Creature* ) getSourceUnit();
+ online = getTarget()->isInAccessablePlaceFor(creature);
+ if(!online)
+ {
+ if(creature->AI()->canReachByRangeAttack(getTarget()))
+ online = true; // not accessable but stays online
+ }
+ else
+ accessible = true;
+
+ }
+ setAccessibleState(accessible);
+ setOnlineOfflineState(online);
+}
+
+//============================================================
+// set the status and fire the event on status change
+
+void HostilReference::setOnlineOfflineState(bool pIsOnline)
+{
+ if(iOnline != pIsOnline)
+ {
+ iOnline = pIsOnline;
+ if(!iOnline)
+ setAccessibleState(false); // if not online that not accessable as well
+ fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this));
+ }
+}
+
+//============================================================
+
+void HostilReference::setAccessibleState(bool pIsAccessible)
+{
+ if(iAccessible != pIsAccessible)
+ {
+ iAccessible = pIsAccessible;
+ fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this));
+ }
+}
+
+//============================================================
+// prepare the reference for deleting
+// this is called be the target
+
+void HostilReference::removeReference()
+{
+ invalidate();
+ fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this));
+}
+
+//============================================================
+
+Unit* HostilReference::getSourceUnit()
+{
+ return (getSource()->getOwner());
+}
+
+//============================================================
+//================ ThreatContainer ===========================
+//============================================================
+
+void ThreatContainer::clearReferences()
+{
+ for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); i++)
+ {
+ (*i)->unlink();
+ delete (*i);
+ }
+ iThreatList.clear();
+}
+
+//============================================================
+// Return the HostilReference of NULL, if not found
+HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
+{
+ HostilReference* result = NULL;
+ uint64 guid = pVictim->GetGUID();
+ for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); i++)
+ {
+ if((*i)->getUnitGuid() == guid)
+ {
+ result = (*i);
+ break;
+ }
+ }
+
+ return result;
+}
+
+//============================================================
+// Add the threat, if we find the reference
+
+HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
+{
+ HostilReference* ref = getReferenceByTarget(pVictim);
+ if(ref)
+ ref->addThreat(pThreat);
+ return ref;
+}
+
+//============================================================
+
+void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
+{
+ if(HostilReference* ref = getReferenceByTarget(pVictim))
+ ref->addThreatPercent(pPercent);
+}
+
+//============================================================
+
+bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
+{
+ // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
+ return lhs->getThreat() > rhs->getThreat(); // reverse sorting
+}
+
+//============================================================
+// Check if the list is dirty and sort if necessary
+
+void ThreatContainer::update()
+{
+ if(iDirty && iThreatList.size() >1)
+ {
+ iThreatList.sort(HostilReferenceSortPredicate);
+ }
+ iDirty = false;
+}
+
+//============================================================
+// return the next best victim
+// could be the current victim
+
+HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
+{
+ HostilReference* currentRef = NULL;
+ bool found = false;
+ for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter)
+ {
+ currentRef = (*iter);
+
+ Unit* target = currentRef->getTarget();
+ assert(target); // if the ref has status online the target must be there !
+
+ if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
+ {
+ if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
+ {
+ // list sorted and and we check current target, then this is best case
+ if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
+ {
+ currentRef = pCurrentVictim; // for second case
+ found = true;
+ break;
+ }
+
+ if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
+ currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
+ { //implement 110% threat rule for targets in melee range
+ found = true; //and 130% rule for targets in ranged distances
+ break; //for selecting alive targets
+ }
+ }
+ else // select any
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if(!found)
+ currentRef = NULL;
+
+ return currentRef;
+}
+
+//============================================================
+//=================== ThreatManager ==========================
+//============================================================
+
+ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
+{
+}
+
+//============================================================
+
+void ThreatManager::clearReferences()
+{
+ iThreatContainer.clearReferences();
+ iThreatOfflineContainer.clearReferences();
+ iCurrentVictim = NULL;
+}
+
+//============================================================
+
+void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
+{
+ //function deals with adding threat and adding players and pets into ThreatList
+ //mobs, NPCs, guards have ThreatList and HateOfflineList
+ //players and pets have only InHateListOf
+ //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
+
+ if (pVictim == getOwner()) // only for same creatures :)
+ return;
+
+ if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
+ return;
+
+ assert(getOwner()->GetTypeId()== TYPEID_UNIT);
+
+ float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
+
+ HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
+ // Ref is not in the online refs, search the offline refs next
+ if(!ref)
+ ref = iThreatOfflineContainer.addThreat(pVictim, threat);
+
+ if(!ref) // there was no ref => create a new one
+ {
+ // threat has to be 0 here
+ HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
+ iThreatContainer.addReference(hostilReference);
+ hostilReference->addThreat(threat); // now we add the real threat
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
+ hostilReference->setOnlineOfflineState(false); // GM is always offline
+ }
+}
+
+//============================================================
+
+void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
+{
+ iThreatContainer.modifyThreatPercent(pVictim, pPercent);
+}
+
+//============================================================
+
+Unit* ThreatManager::getHostilTarget()
+{
+ iThreatContainer.update();
+ HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
+ setCurrentVictim(nextVictim);
+ return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
+}
+
+//============================================================
+
+float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
+{
+ float threat = 0.0f;
+ HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
+ if(!ref && pAlsoSearchOfflineList)
+ ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
+ if(ref)
+ threat = ref->getThreat();
+ return threat;
+}
+
+//============================================================
+
+void ThreatManager::tauntApply(Unit* pTaunter)
+{
+ HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
+ if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
+ {
+ if(ref->getTempThreatModifyer() == 0.0f)
+ // Ok, temp threat is unused
+ ref->setTempThreat(getCurrentVictim()->getThreat());
+ }
+}
+
+//============================================================
+
+void ThreatManager::tauntFadeOut(Unit *pTaunter)
+{
+ HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
+ if(ref)
+ ref->resetTempThreat();
+}
+
+//============================================================
+
+void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
+{
+ iCurrentVictim = pHostilReference;
+}
+
+//============================================================
+// The hated unit is gone, dead or deleted
+// return true, if the event is consumed
+
+bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent)
+{
+ bool consumed = false;
+
+ ThreatRefStatusChangeEvent* threatRefStatusChangeEvent;
+ HostilReference* hostilReference;
+
+ threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent;
+ threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
+ hostilReference = threatRefStatusChangeEvent->getReference();
+
+ switch(pUnitBaseEvent->getType())
+ {
+ case UEV_THREAT_REF_THREAT_CHANGE:
+ if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
+ (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
+ setDirty(true); // the order in the threat list might have changed
+ break;
+ case UEV_THREAT_REF_ONLINE_STATUS:
+ if(!hostilReference->isOnline())
+ {
+ if (hostilReference == getCurrentVictim())
+ {
+ setCurrentVictim(NULL);
+ setDirty(true);
+ }
+ iThreatContainer.remove(hostilReference);
+ iThreatOfflineContainer.addReference(hostilReference);
+ }
+ else
+ {
+ if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
+ setDirty(true);
+ iThreatContainer.addReference(hostilReference);
+ iThreatOfflineContainer.remove(hostilReference);
+ }
+ break;
+ case UEV_THREAT_REF_REMOVE_FROM_LIST:
+ if (hostilReference == getCurrentVictim())
+ {
+ setCurrentVictim(NULL);
+ setDirty(true);
+ }
+ if(hostilReference->isOnline())
+ iThreatContainer.remove(hostilReference);
+ else
+ iThreatOfflineContainer.remove(hostilReference);
+ break;
+ }
+ return consumed;
+}
diff --git a/src/game/ThreatManager.h b/src/game/ThreatManager.h
new file mode 100644
index 00000000000..ab0270645f7
--- /dev/null
+++ b/src/game/ThreatManager.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _THREATMANAGER
+#define _THREATMANAGER
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Utilities/LinkedReference/Reference.h"
+#include "UnitEvents.h"
+
+#include <list>
+
+//==============================================================
+
+class Unit;
+class Creature;
+class ThreatManager;
+struct SpellEntry;
+
+//==============================================================
+// Class to calculate the real threat based
+
+class ThreatCalcHelper
+{
+ public:
+ static float calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+};
+
+//==============================================================
+
+class MANGOS_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager>
+{
+ private:
+ float iThreat;
+ float iTempThreatModifyer; // used for taunt
+ uint64 iUnitGuid;
+ bool iOnline;
+ bool iAccessible;
+ private:
+ // Inform the source, that the status of that reference was changed
+ void fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent);
+
+ Unit* getSourceUnit();
+ public:
+ HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat);
+
+ //=================================================
+ void addThreat(float pMod);
+
+ void setThreat(float pThreat) { addThreat(pThreat - getThreat()); }
+
+ void addThreatPercent(int32 pPercent) { float tmpThreat = iThreat; tmpThreat = tmpThreat * (pPercent+100) / 100; addThreat(tmpThreat-iThreat); }
+
+ float getThreat() const { return iThreat; }
+
+ bool isOnline() const { return iOnline; }
+
+ // The Unit might be in water and the creature can not enter the water, but has range attack
+ // in this case online = true, but accessable = false
+ bool isAccessable() const { return iAccessible; }
+
+ // used for temporary setting a threat and reducting it later again.
+ // the threat modification is stored
+ void setTempThreat(float pThreat) { iTempThreatModifyer = pThreat - getThreat(); if(iTempThreatModifyer != 0.0f) addThreat(iTempThreatModifyer); }
+
+ void resetTempThreat()
+ {
+ if(iTempThreatModifyer != 0.0f)
+ {
+ addThreat(-iTempThreatModifyer); iTempThreatModifyer = 0.0f;
+ }
+ }
+
+ float getTempThreatModifyer() { return iTempThreatModifyer; }
+
+ //=================================================
+ // check, if source can reach target and set the status
+ void updateOnlineStatus();
+
+ void setOnlineOfflineState(bool pIsOnline);
+
+ void setAccessibleState(bool pIsAccessible);
+ //=================================================
+
+ bool operator ==(const HostilReference& pHostilReference) const { return pHostilReference.getUnitGuid() == getUnitGuid(); }
+
+ //=================================================
+
+ uint64 getUnitGuid() const { return iUnitGuid; }
+
+ //=================================================
+ // reference is not needed anymore. realy delete it !
+
+ void removeReference();
+
+ //=================================================
+
+ HostilReference* next() { return ((HostilReference* ) Reference<Unit, ThreatManager>::next()); }
+
+ //=================================================
+
+ // Tell our refTo (target) object that we have a link
+ void targetObjectBuildLink();
+
+ // Tell our refTo (taget) object, that the link is cut
+ void targetObjectDestroyLink();
+
+ // Tell our refFrom (source) object, that the link is cut (Target destroyed)
+ void sourceObjectDestroyLink();
+};
+
+//==============================================================
+class ThreatManager;
+
+class MANGOS_DLL_SPEC ThreatContainer
+{
+ private:
+ std::list<HostilReference*> iThreatList;
+ bool iDirty;
+ protected:
+ friend class ThreatManager;
+
+ void remove(HostilReference* pRef) { iThreatList.remove(pRef); }
+ void addReference(HostilReference* pHostilReference) { iThreatList.push_back(pHostilReference); }
+ void clearReferences();
+ // Sort the list if necessary
+ void update();
+ public:
+ ThreatContainer() { iDirty = false; }
+ ~ThreatContainer() { clearReferences(); }
+
+ HostilReference* addThreat(Unit* pVictim, float pThreat);
+
+ void modifyThreatPercent(Unit *pVictim, int32 percent);
+
+ HostilReference* selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim);
+
+ void setDirty(bool pDirty) { iDirty = pDirty; }
+
+ bool isDirty() { return iDirty; }
+
+ bool empty() { return(iThreatList.empty()); }
+
+ HostilReference* getMostHated() { return iThreatList.empty() ? NULL : iThreatList.front(); }
+
+ HostilReference* getReferenceByTarget(Unit* pVictim);
+
+ std::list<HostilReference*>& getThreatList() { return iThreatList; }
+};
+
+//=================================================
+
+class MANGOS_DLL_SPEC ThreatManager
+{
+ private:
+ HostilReference* iCurrentVictim;
+ Unit* iOwner;
+ ThreatContainer iThreatContainer;
+ ThreatContainer iThreatOfflineContainer;
+ public:
+ explicit ThreatManager(Unit *pOwner);
+
+ ~ThreatManager() { clearReferences(); }
+
+ void clearReferences();
+
+ void addThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+ void modifyThreatPercent(Unit *pVictim, int32 pPercent);
+
+ float getThreat(Unit *pVictim, bool pAlsoSearchOfflineList = false);
+
+ bool isThreatListEmpty() { return iThreatContainer.empty();}
+
+ bool processThreatEvent(const UnitBaseEvent* pUnitBaseEvent);
+
+ HostilReference* getCurrentVictim() { return iCurrentVictim; }
+
+ Unit* getOwner() { return iOwner; }
+
+ Unit* getHostilTarget();
+
+ void tauntApply(Unit* pTaunter);
+ void tauntFadeOut(Unit *pTaunter);
+
+ void setCurrentVictim(HostilReference* pHostilReference);
+
+ void setDirty(bool pDirty) { iThreatContainer.setDirty(pDirty); }
+
+ // methods to access the lists from the outside to do sume dirty manipulation (scriping and such)
+ // I hope they are used as little as possible.
+ inline std::list<HostilReference*>& getThreatList() { return iThreatContainer.getThreatList(); }
+ inline std::list<HostilReference*>& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); }
+ inline ThreatContainer& getOnlineContainer() { return iThreatContainer; }
+ inline ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; }
+};
+
+//=================================================
+#endif
diff --git a/src/game/Tools.h b/src/game/Tools.h
new file mode 100644
index 00000000000..d9615b7df63
--- /dev/null
+++ b/src/game/Tools.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+#ifndef MANGOS_TOOLS_H
+#define MANGOS_TOOLS_H
+
+#include "Common.h"
+#include "WorldPacket.h"
+
+bool readGUID(WorldPacket & data, uint64& guid);
+void writeGUID(WorldPacket & data, uint64 & guid);
+#endif
diff --git a/src/game/Totem.cpp b/src/game/Totem.cpp
new file mode 100644
index 00000000000..5415e4dbe87
--- /dev/null
+++ b/src/game/Totem.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Totem.h"
+#include "WorldPacket.h"
+#include "MapManager.h"
+#include "Log.h"
+#include "Group.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+
+Totem::Totem() : Creature()
+{
+ m_isTotem = true;
+ m_duration = 0;
+ m_type = TOTEM_PASSIVE;
+}
+
+void Totem::Update( uint32 time )
+{
+ Unit *owner = GetOwner();
+ if (!owner || !owner->isAlive() || !this->isAlive())
+ {
+ UnSummon(); // remove self
+ return;
+ }
+
+ if (m_duration <= time)
+ {
+ UnSummon(); // remove self
+ return;
+ }
+ else
+ m_duration -= time;
+
+ Creature::Update( time );
+}
+
+void Totem::Summon(Unit* owner)
+{
+ sLog.outDebug("AddObject at Totem.cpp line 49");
+
+ SetInstanceId(owner->GetInstanceId());
+ owner->GetMap()->Add((Creature*)this);
+
+ // select totem model in dependent from owner team
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(owner->GetTypeId()==TYPEID_PLAYER && cinfo)
+ {
+ if(((Player*)owner)->GetTeam()==HORDE)
+ SetDisplayId(cinfo->DisplayID_H);
+ else
+ SetDisplayId(cinfo->DisplayID_A);
+ }
+
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << GetGUID();
+ SendMessageToSet(&data,true);
+
+ AIM_Initialize();
+
+ switch(m_type)
+ {
+ case TOTEM_PASSIVE: CastSpell(this, GetSpell(), true); break;
+ case TOTEM_STATUE: CastSpell(GetOwner(), GetSpell(), true); break;
+ default: break;
+ }
+}
+
+void Totem::UnSummon()
+{
+ SendObjectDeSpawnAnim(GetGUID());
+
+ CombatStop();
+ RemoveAurasDueToSpell(GetSpell());
+ Unit *owner = this->GetOwner();
+ if (owner)
+ {
+ // clear owenr's totem slot
+ for(int i = 0; i < MAX_TOTEM; ++i)
+ {
+ if(owner->m_TotemSlot[i]==GetGUID())
+ {
+ owner->m_TotemSlot[i] = 0;
+ break;
+ }
+ }
+
+ owner->RemoveAurasDueToSpell(GetSpell());
+
+ //remove aura all party members too
+ Group *pGroup = NULL;
+ if (owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Not only the player can summon the totem (scripted AI)
+ pGroup = ((Player*)owner)->GetGroup();
+ if (pGroup)
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+ if(Target && pGroup->SameSubGroup((Player*)owner, Target))
+ Target->RemoveAurasDueToSpell(GetSpell());
+ }
+ }
+ }
+ }
+
+ CleanupsBeforeDelete();
+ AddObjectToRemoveList();
+}
+
+void Totem::SetOwner(uint64 guid)
+{
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY, guid);
+ SetUInt64Value(UNIT_FIELD_CREATEDBY, guid);
+ Unit *owner = this->GetOwner();
+ if (owner)
+ {
+ this->setFaction(owner->getFaction());
+ this->SetLevel(owner->getLevel());
+ }
+}
+
+Unit *Totem::GetOwner()
+{
+ uint64 ownerid = GetOwnerGUID();
+ if(!ownerid)
+ return NULL;
+ return ObjectAccessor::GetUnit(*this, ownerid);
+}
+
+void Totem::SetTypeBySummonSpell(SpellEntry const * spellProto)
+{
+ // Get spell casted by totem
+ SpellEntry const * totemSpell = sSpellStore.LookupEntry(GetSpell());
+ if (totemSpell)
+ {
+ // If spell have cast time -> so its active totem
+ if (GetSpellCastTime(totemSpell))
+ m_type = TOTEM_ACTIVE;
+ }
+ if(spellProto->SpellIconID==2056)
+ m_type = TOTEM_STATUE; //Jewelery statue
+}
diff --git a/src/game/Totem.h b/src/game/Totem.h
new file mode 100644
index 00000000000..34bf440c68e
--- /dev/null
+++ b/src/game/Totem.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOSSERVER_TOTEM_H
+#define MANGOSSERVER_TOTEM_H
+
+#include "Creature.h"
+
+enum TotemType
+{
+ TOTEM_PASSIVE = 0,
+ TOTEM_ACTIVE = 1,
+ TOTEM_STATUE = 2
+};
+
+class Totem : public Creature
+{
+ public:
+ explicit Totem();
+ virtual ~Totem(){};
+ void Update( uint32 time );
+ void Summon(Unit* owner);
+ void UnSummon();
+ uint32 GetSpell() const { return m_spells[0]; }
+ uint32 GetTotemDuration() const { return m_duration; }
+ Unit *GetOwner();
+ TotemType GetTotemType() const { return m_type; }
+ void SetTypeBySummonSpell(SpellEntry const * spellProto);
+ void SetDuration(uint32 dur) { m_duration = dur; }
+ void SetOwner(uint64 guid);
+
+ bool UpdateStats(Stats /*stat*/) { return true; }
+ bool UpdateAllStats() { return true; }
+ void UpdateResistances(uint32 /*school*/) {}
+ void UpdateArmor() {}
+ void UpdateMaxHealth() {}
+ void UpdateMaxPower(Powers /*power*/) {}
+ void UpdateAttackPowerAndDamage(bool /*ranged*/ ) {}
+ void UpdateDamagePhysical(WeaponAttackType /*attType*/) {}
+
+ protected:
+ TotemType m_type;
+ uint32 m_duration;
+};
+#endif
diff --git a/src/game/TotemAI.cpp b/src/game/TotemAI.cpp
new file mode 100644
index 00000000000..8fc038a8946
--- /dev/null
+++ b/src/game/TotemAI.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "TotemAI.h"
+#include "Totem.h"
+#include "Creature.h"
+#include "Player.h"
+#include "Database/DBCStores.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "SpellMgr.h"
+
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+int
+TotemAI::Permissible(const Creature *creature)
+{
+ if( creature->isTotem() )
+ return PERMIT_BASE_PROACTIVE;
+
+ return PERMIT_BASE_NO;
+}
+
+TotemAI::TotemAI(Creature &c) : i_totem(static_cast<Totem&>(c)), i_victimGuid(0)
+{
+}
+
+void
+TotemAI::MoveInLineOfSight(Unit *)
+{
+}
+
+void TotemAI::EnterEvadeMode()
+{
+ i_totem.CombatStop();
+}
+
+void
+TotemAI::UpdateAI(const uint32 /*diff*/)
+{
+ if (i_totem.GetTotemType() != TOTEM_ACTIVE)
+ return;
+
+ if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false))
+ return;
+
+ // Search spell
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell());
+ if (!spellInfo)
+ return;
+
+ // Get spell rangy
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
+ float max_range = GetSpellMaxRange(srange);
+
+ // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems
+
+ // pointer to appropriate target if found any
+ Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(i_totem, i_victimGuid) : NULL;
+
+ // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
+ if( !victim ||
+ !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) ||
+ i_totem.IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(&i_totem,false) )
+ {
+ CellPair p(MaNGOS::ComputeCellPair(i_totem.GetPositionX(),i_totem.GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ victim = NULL;
+
+ MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range);
+ MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
+ TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem));
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem));
+ }
+
+ // If have target
+ if (victim)
+ {
+ // remember
+ i_victimGuid = victim->GetGUID();
+
+ // attack
+ i_totem.SetInFront(victim); // client change orientation by self
+ i_totem.CastSpell(victim, i_totem.GetSpell(), false);
+ }
+ else
+ i_victimGuid = 0;
+}
+
+bool
+TotemAI::IsVisible(Unit *) const
+{
+ return false;
+}
+
+void
+TotemAI::AttackStart(Unit *)
+{
+}
diff --git a/src/game/TotemAI.h b/src/game/TotemAI.h
new file mode 100644
index 00000000000..e113ac441a1
--- /dev/null
+++ b/src/game/TotemAI.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_TOTEMAI_H
+#define MANGOS_TOTEMAI_H
+
+#include "CreatureAI.h"
+#include "Timer.h"
+
+class Creature;
+class Totem;
+
+class MANGOS_DLL_DECL TotemAI : public CreatureAI
+{
+ public:
+
+ TotemAI(Creature &c);
+
+ void MoveInLineOfSight(Unit *);
+ void AttackStart(Unit *);
+ void EnterEvadeMode();
+ bool IsVisible(Unit *) const;
+
+ void UpdateAI(const uint32);
+ static int Permissible(const Creature *);
+
+ private:
+ Totem &i_totem;
+ uint64 i_victimGuid;
+};
+#endif
diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp
new file mode 100644
index 00000000000..eb874f82ffb
--- /dev/null
+++ b/src/game/TradeHandler.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectAccessor.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Player.h"
+#include "Item.h"
+#include "SocialMgr.h"
+
+enum TradeStatus
+{
+ TRADE_STATUS_BUSY = 0,
+ TRADE_STATUS_BEGIN_TRADE = 1,
+ TRADE_STATUS_OPEN_WINDOW = 2,
+ TRADE_STATUS_TRADE_CANCELED = 3,
+ TRADE_STATUS_TRADE_ACCEPT = 4,
+ TRADE_STATUS_BUSY_2 = 5,
+ TRADE_STATUS_NO_TARGET = 6,
+ TRADE_STATUS_BACK_TO_TRADE = 7,
+ TRADE_STATUS_TRADE_COMPLETE = 8,
+ // 9?
+ TRADE_STATUS_TARGET_TO_FAR = 10,
+ TRADE_STATUS_WRONG_FACTION = 11,
+ TRADE_STATUS_CLOSE_WINDOW = 12,
+ // 13?
+ TRADE_STATUS_IGNORE_YOU = 14,
+ TRADE_STATUS_YOU_STUNNED = 15,
+ TRADE_STATUS_TARGET_STUNNED = 16,
+ TRADE_STATUS_YOU_DEAD = 17,
+ TRADE_STATUS_TARGET_DEAD = 18,
+ TRADE_STATUS_YOU_LOGOUT = 19,
+ TRADE_STATUS_TARGET_LOGOUT = 20,
+ TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action
+ TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related).
+};
+
+void WorldSession::SendTradeStatus(uint32 status)
+{
+ WorldPacket data;
+
+ switch(status)
+ {
+ case TRADE_STATUS_BEGIN_TRADE:
+ data.Initialize(SMSG_TRADE_STATUS, 4+8);
+ data << uint32(status);
+ data << uint64(0);
+ break;
+ case TRADE_STATUS_OPEN_WINDOW:
+ data.Initialize(SMSG_TRADE_STATUS, 4+4);
+ data << uint32(status);
+ data << uint32(0); // added in 2.4.0
+ break;
+ case TRADE_STATUS_CLOSE_WINDOW:
+ data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4);
+ data << uint32(status);
+ data << uint32(0);
+ data << uint8(0);
+ data << uint32(0);
+ break;
+ case TRADE_STATUS_ONLY_CONJURED:
+ data.Initialize(SMSG_TRADE_STATUS, 4+1);
+ data << uint32(status);
+ data << uint8(0);
+ break;
+ default:
+ data.Initialize(SMSG_TRADE_STATUS, 4);
+ data << uint32(status);
+ break;
+ }
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug( "WORLD: Ignore Trade %u",_player->GetGUIDLow());
+ // recvPacket.print_storage();
+}
+
+void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug( "WORLD: Busy Trade %u",_player->GetGUIDLow());
+ // recvPacket.print_storage();
+}
+
+void WorldSession::SendUpdateTrade()
+{
+ Item *item = NULL;
+
+ if( !_player || !_player->pTrader )
+ return;
+
+ // reset trade status
+ if (_player->acceptTrade)
+ {
+ _player->acceptTrade = false;
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ }
+
+ if (_player->pTrader->acceptTrade)
+ {
+ _player->pTrader->acceptTrade = false;
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ }
+
+ WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size
+ data << (uint8 ) 1; // can be different (only seen 0 and 1)
+ data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?)
+ data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases
+ data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases
+ data << (uint32) _player->pTrader->tradeGold; // trader gold
+ data << (uint32) 0; // spell casted on lowest slot item
+
+ for(uint8 i = 0; i < TRADE_SLOT_COUNT; i++)
+ {
+ item = (_player->pTrader->tradeItems[i] != NULL_SLOT ? _player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i] ) : NULL);
+
+ data << (uint8) i; // trade slot number, if not specified, then end of packet
+
+ if(item)
+ {
+ data << (uint32) item->GetProto()->ItemId; // entry
+ // display id
+ data << (uint32) item->GetProto()->DisplayInfoID;
+ // stack count
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT);
+ data << (uint32) 0; // probably gift=1, created_by=0?
+ // gift creator
+ data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR);
+ data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT);
+ for(uint8 j = 0; j < 3; ++j)
+ data << (uint32) 0; // enchantment id (permanent/gems?)
+ // creator
+ data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR);
+ data << (uint32) item->GetSpellCharges(); // charges
+ data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor
+ // random properties id
+ data << (uint32) item->GetItemRandomPropertyId();
+ data << (uint32) item->GetProto()->LockID; // lock id
+ // max durability
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ // durability
+ data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+ }
+ else
+ {
+ for(uint8 j = 0; j < 18; j++)
+ data << uint32(0);
+ }
+ }
+ SendPacket(&data);
+}
+
+//==============================================================
+// transfer the items to the players
+
+void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
+{
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ ItemPosCountVec traderDst;
+ ItemPosCountVec playerDst;
+ bool traderCanTrade = (myItems[i]==NULL || _player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, myItems[i], false ) == EQUIP_ERR_OK);
+ bool playerCanTrade = (hisItems[i]==NULL || _player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false ) == EQUIP_ERR_OK);
+ if(traderCanTrade && playerCanTrade )
+ {
+ // Ok, if trade item exists and can be stored
+ // If we trade in both directions we had to check, if the trade will work before we actually do it
+ // A roll back is not possible after we stored it
+ if(myItems[i])
+ {
+ // logging
+ sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow());
+ if( _player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(),
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
+
+ // store
+ _player->pTrader->MoveItemToInventory( traderDst, myItems[i], true, true);
+ }
+ if(hisItems[i])
+ {
+ // logging
+ sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow());
+ if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
+ hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(),
+ _player->GetName(),_player->GetSession()->GetAccountId());
+
+ // store
+ _player->MoveItemToInventory( playerDst, hisItems[i], true, true);
+ }
+ }
+ else
+ {
+ // in case of fatal error log error message
+ // return the already removed items to the original owner
+ if(myItems[i])
+ {
+ if(!traderCanTrade)
+ sLog.outError("trader can't store item: %u",myItems[i]->GetGUIDLow());
+ if(_player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, myItems[i], false ) == EQUIP_ERR_OK)
+ _player->MoveItemToInventory(playerDst, myItems[i], true, true);
+ else
+ sLog.outError("player can't take item back: %u",myItems[i]->GetGUIDLow());
+ }
+ // return the already removed items to the original owner
+ if(hisItems[i])
+ {
+ if(!playerCanTrade)
+ sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow());
+ if(_player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false ) == EQUIP_ERR_OK)
+ _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true);
+ else
+ sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow());
+ }
+ }
+ }
+}
+
+//==============================================================
+
+void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ bool myCanCompleteTrade=true,hisCanCompleteTrade=true;
+
+ if ( !GetPlayer()->pTrader )
+ return;
+
+ // not accept case incorrect money amount
+ if( _player->tradeGold > _player->GetMoney() )
+ {
+ SendNotification( "You do not have enough gold" );
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->acceptTrade = false;
+ return;
+ }
+
+ // not accept case incorrect money amount
+ if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() )
+ {
+ _player->pTrader->GetSession( )->SendNotification( "You do not have enough gold" );
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->acceptTrade = false;
+ return;
+ }
+
+ // not accept if some items now can't be trade (cheating)
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(_player->tradeItems[i] != NULL_SLOT )
+ {
+ if(Item* item =_player->GetItemByPos( _player->tradeItems[i] ))
+ {
+ if(!item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+ }
+ if(_player->pTrader->tradeItems[i] != NULL_SLOT)
+ {
+ if(Item* item =_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]) )
+ {
+ if(!item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+ }
+ }
+
+ _player->acceptTrade = true;
+ if (_player->pTrader->acceptTrade )
+ {
+ // inform partner client
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+
+ // store items in local list and set 'in-trade' flag
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(_player->tradeItems[i] != NULL_SLOT )
+ {
+ sLog.outDebug("player trade item bag: %u slot: %u",_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255 );
+ //Can return NULL
+ myItems[i]=_player->GetItemByPos( _player->tradeItems[i] );
+ if (myItems[i])
+ myItems[i]->SetInTrade();
+ }
+ if(_player->pTrader->tradeItems[i] != NULL_SLOT)
+ {
+ sLog.outDebug("partner trade item bag: %u slot: %u",_player->pTrader->tradeItems[i] >> 8,_player->pTrader->tradeItems[i] & 255);
+ //Can return NULL
+ hisItems[i]=_player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i]);
+ if(hisItems[i])
+ hisItems[i]->SetInTrade();
+ }
+ }
+
+ // test if item will fit in each inventory
+ hisCanCompleteTrade = (_player->pTrader->CanStoreItems( myItems,TRADE_SLOT_TRADED_COUNT )== EQUIP_ERR_OK);
+ myCanCompleteTrade = (_player->CanStoreItems( hisItems,TRADE_SLOT_TRADED_COUNT ) == EQUIP_ERR_OK);
+
+ // clear 'in-trade' flag
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(myItems[i]) myItems[i]->SetInTrade(false);
+ if(hisItems[i]) hisItems[i]->SetInTrade(false);
+ }
+
+ // in case of missing space report error
+ if(!myCanCompleteTrade)
+ {
+ SendNotification("You do not have enough free slots");
+ GetPlayer( )->pTrader->GetSession( )->SendNotification("Your partner does not have enough free bag slots");
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ return;
+ }
+ else if (!hisCanCompleteTrade)
+ {
+ SendNotification("Your partner does not have enough free bag slots");
+ GetPlayer()->pTrader->GetSession()->SendNotification("You do not have enough free slots");
+ SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ return;
+ }
+
+ // execute trade: 1. remove
+ for(int i=0; i<TRADE_SLOT_TRADED_COUNT; i++)
+ {
+ if(myItems[i])
+ {
+ myItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->GetGUID());
+ _player->MoveItemFromInventory(_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255, true);
+ }
+ if(hisItems[i])
+ {
+ hisItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID());
+ _player->pTrader->MoveItemFromInventory(_player->pTrader->tradeItems[i] >> 8, _player->pTrader->tradeItems[i] & 255, true);
+ }
+ }
+
+ // execute trade: 2. store
+ moveItems(myItems, hisItems);
+
+ // logging money
+ if(sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ {
+ if( _player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0)
+ sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),
+ _player->tradeGold,
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
+ if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0)
+ sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
+ _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
+ _player->pTrader->tradeGold,
+ _player->GetName(),_player->GetSession()->GetAccountId());
+ }
+
+ // update money
+ _player->ModifyMoney( -int32(_player->tradeGold) );
+ _player->ModifyMoney(_player->pTrader->tradeGold );
+ _player->pTrader->ModifyMoney( -int32(_player->pTrader->tradeGold) );
+ _player->pTrader->ModifyMoney(_player->tradeGold );
+
+ _player->ClearTrade();
+ _player->pTrader->ClearTrade();
+
+ // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
+ CharacterDatabase.BeginTransaction();
+ _player->SaveInventoryAndGoldToDB();
+ _player->pTrader->SaveInventoryAndGoldToDB();
+ CharacterDatabase.CommitTransaction();
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
+ SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
+
+ _player->pTrader->pTrader = NULL;
+ _player->pTrader = NULL;
+ }
+ else
+ {
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+ }
+}
+
+void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ if ( !GetPlayer()->pTrader )
+ return;
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
+ _player->acceptTrade = false;
+}
+
+void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ if(!_player->pTrader)
+ return;
+
+ _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
+ _player->pTrader->ClearTrade();
+
+ SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
+ _player->ClearTrade();
+}
+
+void WorldSession::SendCancelTrade()
+{
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+}
+
+void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
+{
+ // sended also after LOGOUT COMPLETE
+ if(_player) // needed because STATUS_AUTHED
+ _player->TradeCancel(true);
+}
+
+void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ if( GetPlayer()->pTrader )
+ return;
+
+ uint64 ID;
+
+ if( !GetPlayer()->isAlive() )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_DEAD);
+ return;
+ }
+
+ if( GetPlayer()->hasUnitState(UNIT_STAT_STUNDED) )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
+ return;
+ }
+
+ if( isLogingOut() )
+ {
+ SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
+ return;
+ }
+
+ if( GetPlayer()->isInFlight() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ recvPacket >> ID;
+
+ Player* pOther = ObjectAccessor::FindPlayer( ID );
+
+ if( !pOther )
+ {
+ SendTradeStatus(TRADE_STATUS_NO_TARGET);
+ return;
+ }
+
+ if( pOther == GetPlayer() || pOther->pTrader )
+ {
+ SendTradeStatus(TRADE_STATUS_BUSY);
+ return;
+ }
+
+ if( !pOther->isAlive() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
+ return;
+ }
+
+ if( pOther->isInFlight() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ if( pOther->hasUnitState(UNIT_STAT_STUNDED) )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
+ return;
+ }
+
+ if( pOther->GetSession()->isLogingOut() )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
+ return;
+ }
+
+ if( pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()) )
+ {
+ SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
+ return;
+ }
+
+ if(pOther->GetTeam() !=_player->GetTeam() )
+ {
+ SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
+ return;
+ }
+
+ if( pOther->GetDistance2d( _player ) > 10.0f )
+ {
+ SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
+ return;
+ }
+
+ // OK start trade
+ _player->pTrader = pOther;
+ pOther->pTrader =_player;
+
+ WorldPacket data(SMSG_TRADE_STATUS, 12);
+ data << (uint32) TRADE_STATUS_BEGIN_TRADE;
+ data << (uint64)_player->GetGUID();
+ _player->pTrader->GetSession()->SendPacket(&data);
+}
+
+void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,4);
+
+ if(!_player->pTrader)
+ return;
+
+ uint32 gold;
+
+ recvPacket >> gold;
+
+ // gold can be incorrect, but this is checked at trade finished.
+ _player->tradeGold = gold;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
+
+void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1+1);
+
+ if(!_player->pTrader)
+ return;
+
+ // send update
+ uint8 tradeSlot;
+ uint8 bag;
+ uint8 slot;
+
+ recvPacket >> tradeSlot;
+ recvPacket >> bag;
+ recvPacket >> slot;
+
+ // invalid slot number
+ if(tradeSlot >= TRADE_SLOT_COUNT)
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+
+ // check cheating, can't fail with correct client operations
+ Item* item = _player->GetItemByPos(bag,slot);
+ if(!item || tradeSlot!=TRADE_SLOT_NONTRADED && !item->CanBeTraded())
+ {
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+
+ uint16 pos = (bag << 8) | slot;
+
+ // prevent place single item into many trade slots using cheating and client bugs
+ for(int i = 0; i < TRADE_SLOT_COUNT; ++i)
+ {
+ if(_player->tradeItems[i]==pos)
+ {
+ // cheating attempt
+ SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+ return;
+ }
+ }
+
+ _player->tradeItems[tradeSlot] = pos;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
+
+void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1);
+
+ if(!_player->pTrader)
+ return;
+
+ uint8 tradeSlot;
+ recvPacket >> tradeSlot;
+
+ // invalid slot number
+ if(tradeSlot >= TRADE_SLOT_COUNT)
+ return;
+
+ _player->tradeItems[tradeSlot] = NULL_SLOT;
+
+ _player->pTrader->GetSession()->SendUpdateTrade();
+}
diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp
new file mode 100644
index 00000000000..be5fa09606f
--- /dev/null
+++ b/src/game/Transports.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+
+#include "Transports.h"
+#include "MapManager.h"
+#include "ObjectMgr.h"
+#include "Path.h"
+
+#include "WorldPacket.h"
+#include "Database/DBCStores.h"
+#include "ProgressBar.h"
+
+void MapManager::LoadTransports()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry, name, period FROM transports");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u transports", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Transport *t = new Transport;
+
+ Field *fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ std::string name = fields[1].GetCppString();
+ t->m_period = fields[2].GetUInt32();
+
+ const GameObjectInfo *goinfo = objmgr.GetGameObjectInfo(entry);
+
+ if(!goinfo)
+ {
+ sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str());
+ delete t;
+ continue;
+ }
+
+ if(goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT)
+ {
+ sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str());
+ delete t;
+ continue;
+ }
+
+ // sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name);
+
+ std::set<uint32> mapsUsed;
+
+ if(!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
+ // skip transports with empty waypoints list
+ {
+ sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.",goinfo->moTransport.taxiPathId);
+ delete t;
+ continue;
+ }
+
+ t->m_name = goinfo->name;
+
+ float x, y, z, o;
+ uint32 mapid;
+ x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1;
+
+ // creates the Gameobject
+ if(!t->Create(entry, mapid, x, y, z, o, 100, 0))
+ {
+ delete t;
+ continue;
+ }
+
+ m_Transports.insert(t);
+
+ for (std::set<uint32>::iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i)
+ 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);
+ ++count;
+ } while(result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u transports", count );
+
+ // check transport data DB integrity
+ result = WorldDatabase.PQuery("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
+ if(result) // wrong data found
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 guid = fields[0].GetUInt32();
+ uint32 entry = fields[1].GetUInt32();
+ std::string name = fields[2].GetCppString();
+ sLog.outErrorDb("Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports DON'T must have any records in `gameobject` or its behavior will be unpredictable/bugged.",entry,name.c_str(),guid);
+ }
+ while(result->NextRow());
+
+ delete result;
+ }
+}
+
+Transport::Transport() : GameObject()
+{
+ // 2.3.2 - 0x5A
+ m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+}
+
+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);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",guidlow,x,y);
+ return false;
+ }
+
+ Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT);
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(guidlow);
+
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f",guidlow, mapid, x, y, z, ang);
+ return false;
+ }
+
+ m_goInfo = goinfo;
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
+ SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
+ SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
+
+ SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
+ SetGoState(1);
+ SetGoType(GameobjectTypes(goinfo->type));
+
+ SetGoAnimProgress(animprogress);
+ if(dynflags)
+ SetUInt32Value(GAMEOBJECT_DYN_FLAGS, dynflags);
+
+ return true;
+}
+
+struct keyFrame
+{
+ keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay)
+ {
+ x = _x; y = _y; z = _z; mapid = _mapid; actionflag = _actionflag; delay = _delay; distFromPrev = -1; distSinceStop = -1; distUntilStop = -1;
+ tFrom = 0; tTo = 0;
+ }
+
+ float x;
+ float y;
+ float z;
+ uint32 mapid;
+ int actionflag;
+ int delay;
+ float distSinceStop;
+ float distUntilStop;
+ float distFromPrev;
+ float tFrom, tTo;
+};
+
+bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
+{
+ TransportPath path;
+ objmgr.GetTransportPathNodes(pathid, path);
+
+ if (path.Empty())
+ return false;
+
+ std::vector<keyFrame> keyFrames;
+ int mapChange = 0;
+ mapids.clear();
+ for (size_t i = 1; i < path.Size() - 1; i++)
+ {
+ if (mapChange == 0)
+ {
+ if ((path[i].mapid == path[i+1].mapid))
+ {
+ keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay);
+ keyFrames.push_back(k);
+ mapids.insert(k.mapid);
+ }
+ else
+ {
+ mapChange = 1;
+ }
+ }
+ else
+ {
+ --mapChange;
+ }
+ }
+
+ int lastStop = -1;
+ int firstStop = -1;
+
+ // first cell is arrived at by teleportation :S
+ keyFrames[0].distFromPrev = 0;
+ if (keyFrames[0].actionflag == 2)
+ {
+ lastStop = 0;
+ }
+
+ // find the rest of the distances between key points
+ for (size_t i = 1; i < keyFrames.size(); i++)
+ {
+ if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid))
+ {
+ keyFrames[i].distFromPrev = 0;
+ }
+ else
+ {
+ keyFrames[i].distFromPrev =
+ sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) +
+ pow(keyFrames[i].y - keyFrames[i - 1].y, 2) +
+ pow(keyFrames[i].z - keyFrames[i - 1].z, 2));
+ }
+ if (keyFrames[i].actionflag == 2)
+ {
+ // remember first stop frame
+ if(firstStop == -1)
+ firstStop = i;
+ lastStop = i;
+ }
+ }
+
+ float tmpDist = 0;
+ for (size_t i = 0; i < keyFrames.size(); i++)
+ {
+ int j = (i + lastStop) % keyFrames.size();
+ if (keyFrames[j].actionflag == 2)
+ tmpDist = 0;
+ else
+ tmpDist += keyFrames[j].distFromPrev;
+ keyFrames[j].distSinceStop = tmpDist;
+ }
+
+ for (int i = int(keyFrames.size()) - 1; i >= 0; i--)
+ {
+ int j = (i + (firstStop+1)) % keyFrames.size();
+ tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
+ keyFrames[j].distUntilStop = tmpDist;
+ if (keyFrames[j].actionflag == 2)
+ tmpDist = 0;
+ }
+
+ for (size_t i = 0; i < keyFrames.size(); i++)
+ {
+ if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f))
+ keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
+ else
+ keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30;
+
+ if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f))
+ keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
+ else
+ keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30;
+
+ keyFrames[i].tFrom *= 1000;
+ keyFrames[i].tTo *= 1000;
+ }
+
+ // for (int i = 0; i < keyFrames.size(); i++) {
+ // sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo);
+ // }
+
+ // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
+ // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
+ int t = 0;
+ bool teleport = false;
+ if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid)
+ teleport = true;
+
+ WayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport);
+ m_WayPoints[0] = pos;
+ t += keyFrames[0].delay * 1000;
+
+ uint32 cM = keyFrames[0].mapid;
+ for (size_t i = 0; i < keyFrames.size() - 1; i++)
+ {
+ float d = 0;
+ float tFrom = keyFrames[i].tFrom;
+ float tTo = keyFrames[i].tTo;
+
+ // keep the generation of all these points; we use only a few now, but may need the others later
+ if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
+ {
+ while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
+ {
+ tFrom += 100;
+ tTo -= 100;
+
+ if (d > 0)
+ {
+ float newX, newY, newZ;
+ newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev;
+ newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev;
+ newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev;
+
+ bool teleport = false;
+ if (keyFrames[i].mapid != cM)
+ {
+ teleport = true;
+ cM = keyFrames[i].mapid;
+ }
+
+ // sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
+ WayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport);
+ if (teleport)
+ m_WayPoints[t] = pos;
+ }
+
+ if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
+ {
+ if (tFrom <= 30000)
+ {
+ d = 0.5f * (tFrom / 1000) * (tFrom / 1000);
+ }
+ else
+ {
+ d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
+ }
+ d = d - keyFrames[i].distSinceStop;
+ }
+ else
+ {
+ if (tTo <= 30000)
+ {
+ d = 0.5f * (tTo / 1000) * (tTo / 1000);
+ }
+ else
+ {
+ d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000);
+ }
+ d = keyFrames[i].distUntilStop - d;
+ }
+ t += 100;
+ }
+ t -= 100;
+ }
+
+ if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
+ t += 100 - ((long)keyFrames[i + 1].tTo % 100);
+ else
+ t += (long)keyFrames[i + 1].tTo % 100;
+
+ bool teleport = false;
+ if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid))
+ {
+ teleport = true;
+ cM = keyFrames[i + 1].mapid;
+ }
+
+ WayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport);
+
+ // sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
+
+ //if (teleport)
+ m_WayPoints[t] = pos;
+
+ t += keyFrames[i + 1].delay * 1000;
+ // sLog.outString("------");
+ }
+
+ uint32 timer = t;
+
+ // sLog.outDetail(" Generated %d waypoints, total time %u.", m_WayPoints.size(), timer);
+
+ m_curr = m_WayPoints.begin();
+ m_curr = GetNextWayPoint();
+ m_next = GetNextWayPoint();
+ m_pathTime = timer;
+
+ m_nextNodeTime = m_curr->first;
+
+ return true;
+}
+
+Transport::WayPointMap::iterator Transport::GetNextWayPoint()
+{
+ WayPointMap::iterator iter = m_curr;
+ ++iter;
+ if (iter == m_WayPoints.end())
+ iter = m_WayPoints.begin();
+ return iter;
+}
+
+void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
+{
+ //MapManager::Instance().GetMap(oldMapid)->Remove((GameObject *)this, false);
+ SetMapId(newMapid);
+ //MapManager::Instance().LoadGrid(newMapid,x,y,true);
+ Relocate(x, y, z);
+ //MapManager::Instance().GetMap(newMapid)->Add<GameObject>((GameObject *)this);
+
+ for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();)
+ {
+ PlayerSet::iterator it2 = itr;
+ ++itr;
+
+ Player *plr = *it2;
+ if(!plr)
+ {
+ m_passengers.erase(it2);
+ continue;
+ }
+
+ if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ {
+ plr->ResurrectPlayer(1.0);
+ }
+ plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
+
+ //WorldPacket data(SMSG_811, 4);
+ //data << uint32(0);
+ //plr->GetSession()->SendPacket(&data);
+ }
+}
+
+bool Transport::AddPassenger(Player* passenger)
+{
+ if (m_passengers.find(passenger) == m_passengers.end())
+ {
+ sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), this->m_name.c_str());
+ m_passengers.insert(passenger);
+ }
+ return true;
+}
+
+bool Transport::RemovePassenger(Player* passenger)
+{
+ if (m_passengers.find(passenger) != m_passengers.end())
+ {
+ sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), this->m_name.c_str());
+ m_passengers.erase(passenger);
+ }
+ return true;
+}
+
+void Transport::Update(uint32 /*p_time*/)
+{
+ if (m_WayPoints.size() <= 1)
+ return;
+
+ m_timer = getMSTime() % m_period;
+ while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime))
+ {
+ m_curr = GetNextWayPoint();
+ m_next = GetNextWayPoint();
+
+ // first check help in case client-server transport coordinates de-synchronization
+ if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport)
+ {
+ TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z);
+ }
+ else
+ {
+ //MapManager::Instance().GetMap(m_curr->second.mapid)->GameobjectRelocation((GameObject *)this, m_curr->second.x, m_curr->second.y, m_curr->second.z, this->m_orientation);
+ Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
+ }
+
+ /*
+ for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();)
+ {
+ PlayerSet::iterator it2 = itr;
+ ++itr;
+ //(*it2)->SetPosition( m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO() );
+ }
+ */
+
+ m_nextNodeTime = m_curr->first;
+
+ if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0)
+ sLog.outDetail(" ************ BEGIN ************** %s", this->m_name.c_str());
+
+ // MapManager::Instance().GetMap(m_curr->second.mapid)->Add(&this); // -> // ->Add(t);
+ //MapManager::Instance().GetMap(m_curr->second.mapid)->Remove((GameObject *)this, false); // -> // ->Add(t);
+ //MapManager::Instance().GetMap(m_curr->second.mapid)->Add((GameObject *)this); // -> // ->Add(t);
+
+ if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0)
+ sLog.outDetail("%s moved to %f %f %f %d", this->m_name.c_str(), m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
+ }
+}
diff --git a/src/game/Transports.h b/src/game/Transports.h
new file mode 100644
index 00000000000..e104012c850
--- /dev/null
+++ b/src/game/Transports.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef TRANSPORTS_H
+#define TRANSPORTS_H
+
+#include "GameObject.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+class TransportPath
+{
+ public:
+ struct PathNode
+ {
+ uint32 mapid;
+ float x,y,z;
+ uint32 actionFlag;
+ uint32 delay;
+ };
+
+ inline void SetLength(const unsigned int sz)
+ {
+ i_nodes.resize( sz );
+ }
+
+ inline unsigned int Size(void) const { return i_nodes.size(); }
+ inline bool Empty(void) const { return i_nodes.empty(); }
+ inline void Resize(unsigned int sz) { i_nodes.resize(sz); }
+ inline void Clear(void) { i_nodes.clear(); }
+ inline PathNode* GetNodes(void) { return static_cast<PathNode *>(&i_nodes[0]); }
+
+ PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; }
+ const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; }
+
+ protected:
+ std::vector<PathNode> i_nodes;
+};
+
+class Transport : private 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;
+
+ 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);
+ bool AddPassenger(Player* passenger);
+ bool RemovePassenger(Player* passenger);
+
+ typedef std::set<Player*> PlayerSet;
+ PlayerSet const& GetPassengers() const { return m_passengers; }
+
+ std::string m_name;
+ private:
+ struct WayPoint
+ {
+ WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false) {}
+ WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport) :
+ mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport) {}
+ uint32 mapid;
+ float x;
+ float y;
+ float z;
+ bool teleport;
+ };
+
+ typedef std::map<uint32, WayPoint> WayPointMap;
+
+ WayPointMap::iterator m_curr;
+ WayPointMap::iterator m_next;
+ uint32 m_pathTime;
+ uint32 m_timer;
+
+ PlayerSet m_passengers;
+
+ public:
+ WayPointMap m_WayPoints;
+ uint32 m_nextNodeTime;
+ uint32 m_period;
+
+ private:
+ void TeleportTransport(uint32 newMapid, float x, float y, float z);
+ WayPointMap::iterator GetNextWayPoint();
+};
+#endif
diff --git a/src/game/Traveller.h b/src/game/Traveller.h
new file mode 100644
index 00000000000..07a02bb3056
--- /dev/null
+++ b/src/game/Traveller.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_TRAVELLER_H
+#define MANGOS_TRAVELLER_H
+
+#include "MapManager.h"
+#include "Creature.h"
+#include "Player.h"
+#include <cassert>
+
+/** Traveller is a wrapper for units (creatures or players) that
+ * travel from point A to point B using the destination holder.
+ */
+#define PLAYER_FLIGHT_SPEED 32.0f
+
+template<class T>
+struct MANGOS_DLL_DECL Traveller
+{
+ T &i_traveller;
+ Traveller(T &t) : i_traveller(t) {}
+ Traveller(const Traveller &obj) : i_traveller(obj) {}
+ Traveller& operator=(const Traveller &obj)
+ {
+ this->~Traveller();
+ new (this) Traveller(obj);
+ return *this;
+ }
+
+ operator T&(void) { return i_traveller; }
+ operator const T&(void) { return i_traveller; }
+ inline float GetPositionX() const { return i_traveller.GetPositionX(); }
+ inline float GetPositionY() const { return i_traveller.GetPositionY(); }
+ inline float GetPositionZ() const { return i_traveller.GetPositionZ(); }
+ inline T& GetTraveller(void) { return i_traveller; }
+
+ float Speed(void) { assert(false); return 0.0f; }
+ void Relocation(float x, float y, float z, float orientation) {}
+ void Relocation(float x, float y, float z) { Relocation(x, y, z, i_traveller.GetOrientation()); }
+ void MoveTo(float x, float y, float z, uint32 t) {}
+};
+
+// specialization for creatures
+template<>
+inline float Traveller<Creature>::Speed()
+{
+ return i_traveller.GetSpeed( i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
+}
+
+template<>
+inline void Traveller<Creature>::Relocation(float x, float y, float z, float orientation)
+{
+ MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->CreatureRelocation(&i_traveller, x, y, z, orientation);
+}
+
+template<>
+inline void Traveller<Creature>::MoveTo(float x, float y, float z, uint32 t)
+{
+ i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0);
+}
+
+// specialization for players
+template<>
+inline float Traveller<Player>::Speed()
+{
+ if (i_traveller.isInFlight())
+ return PLAYER_FLIGHT_SPEED;
+ else
+ return i_traveller.GetSpeed(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
+}
+
+template<>
+inline void Traveller<Player>::Relocation(float x, float y, float z, float orientation)
+{
+ MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->PlayerRelocation(&i_traveller, x, y, z, orientation);
+}
+
+template<>
+inline void Traveller<Player>::MoveTo(float x, float y, float z, uint32 t)
+{
+ //Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
+ i_traveller.SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, t);
+}
+
+typedef Traveller<Creature> CreatureTraveller;
+typedef Traveller<Player> PlayerTraveller;
+#endif
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
new file mode 100644
index 00000000000..442dbc1057b
--- /dev/null
+++ b/src/game/Unit.cpp
@@ -0,0 +1,10808 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Unit.h"
+#include "QuestDef.h"
+#include "Player.h"
+#include "Creature.h"
+#include "Spell.h"
+#include "Group.h"
+#include "SpellAuras.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "Formulas.h"
+#include "Pet.h"
+#include "Util.h"
+#include "Totem.h"
+#include "BattleGround.h"
+#include "InstanceSaveMgr.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "Path.h"
+
+#include <math.h>
+
+float baseMoveSpeed[MAX_MOVE_TYPE] =
+{
+ 2.5f, // MOVE_WALK
+ 7.0f, // MOVE_RUN
+ 1.25f, // MOVE_WALKBACK
+ 4.722222f, // MOVE_SWIM
+ 4.5f, // MOVE_SWIMBACK
+ 3.141594f, // MOVE_TURN
+ 7.0f, // MOVE_FLY
+ 4.5f, // MOVE_FLYBACK
+};
+
+// auraTypes contains attacker auras capable of proc'ing cast auras
+static Unit::AuraTypeSet GenerateAttakerProcCastAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_DUMMY);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
+ auraTypes.insert(SPELL_AURA_MOD_HASTE);
+ auraTypes.insert(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ return auraTypes;
+}
+
+// auraTypes contains victim auras capable of proc'ing cast auras
+static Unit::AuraTypeSet GenerateVictimProcCastAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_DUMMY);
+ auraTypes.insert(SPELL_AURA_PRAYER_OF_MENDING);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
+ return auraTypes;
+}
+
+// auraTypes contains auras capable of proc effect/damage (but not cast) for attacker
+static Unit::AuraTypeSet GenerateAttakerProcEffectAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_MOD_DAMAGE_DONE);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
+ auraTypes.insert(SPELL_AURA_MOD_CASTING_SPEED);
+ auraTypes.insert(SPELL_AURA_MOD_RATING);
+ return auraTypes;
+}
+
+// auraTypes contains auras capable of proc effect/damage (but not cast) for victim
+static Unit::AuraTypeSet GenerateVictimProcEffectAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_MOD_RESISTANCE);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
+ auraTypes.insert(SPELL_AURA_MOD_PARRY_PERCENT);
+ auraTypes.insert(SPELL_AURA_MOD_BLOCK_PERCENT);
+ auraTypes.insert(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ return auraTypes;
+}
+
+static Unit::AuraTypeSet attackerProcCastAuraTypes = GenerateAttakerProcCastAuraTypes();
+static Unit::AuraTypeSet attackerProcEffectAuraTypes = GenerateAttakerProcEffectAuraTypes();
+
+static Unit::AuraTypeSet victimProcCastAuraTypes = GenerateVictimProcCastAuraTypes();
+static Unit::AuraTypeSet victimProcEffectAuraTypes = GenerateVictimProcEffectAuraTypes();
+
+// auraTypes contains auras capable of proc'ing for attacker and victim
+static Unit::AuraTypeSet GenerateProcAuraTypes()
+{
+ Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(attackerProcCastAuraTypes.begin(),attackerProcCastAuraTypes.end());
+ auraTypes.insert(attackerProcEffectAuraTypes.begin(),attackerProcEffectAuraTypes.end());
+ auraTypes.insert(victimProcCastAuraTypes.begin(),victimProcCastAuraTypes.end());
+ auraTypes.insert(victimProcEffectAuraTypes.begin(),victimProcEffectAuraTypes.end());
+ return auraTypes;
+}
+
+static Unit::AuraTypeSet procAuraTypes = GenerateProcAuraTypes();
+
+bool IsPassiveStackableSpell( uint32 spellId )
+{
+ if(!IsPassiveSpell(spellId))
+ return false;
+
+ SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
+ if(!spellProto)
+ return false;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ if(std::find(procAuraTypes.begin(),procAuraTypes.end(),spellProto->EffectApplyAuraName[j])!=procAuraTypes.end())
+ return false;
+ }
+
+ return true;
+}
+
+Unit::Unit()
+: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostilRefManager(this)
+{
+ m_objectType |= TYPEMASK_UNIT;
+ m_objectTypeId = TYPEID_UNIT;
+ // 2.3.2 - 0x70
+ m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HASPOSITION);
+
+ m_attackTimer[BASE_ATTACK] = 0;
+ m_attackTimer[OFF_ATTACK] = 0;
+ m_attackTimer[RANGED_ATTACK] = 0;
+ m_modAttackSpeedPct[BASE_ATTACK] = 1.0f;
+ m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
+ m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
+
+ m_extraAttacks = 0;
+
+ m_state = 0;
+ m_form = FORM_NONE;
+ m_deathState = ALIVE;
+
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ m_currentSpells[i] = NULL;
+
+ m_addDmgOnce = 0;
+
+ for(int i = 0; i < MAX_TOTEM; ++i)
+ m_TotemSlot[i] = 0;
+
+ m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0;
+ //m_Aura = NULL;
+ //m_AurasCheck = 2000;
+ //m_removeAuraTimer = 4;
+ //tmpAura = NULL;
+ waterbreath = false;
+
+ m_Visibility = VISIBILITY_ON;
+
+ m_detectInvisibilityMask = 0;
+ m_invisibilityMask = 0;
+ m_transform = 0;
+ m_ShapeShiftFormSpellId = 0;
+ m_canModifyStats = false;
+
+ for (int i = 0; i < MAX_SPELL_IMMUNITY; i++)
+ m_spellImmune[i].clear();
+ for (int i = 0; i < UNIT_MOD_END; i++)
+ {
+ m_auraModifiersGroup[i][BASE_VALUE] = 0.0f;
+ m_auraModifiersGroup[i][BASE_PCT] = 1.0f;
+ m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f;
+ m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f;
+ }
+ // implement 50% base damage from offhand
+ m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
+ m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
+ }
+ for (int i = 0; i < MAX_STATS; i++)
+ m_createStats[i] = 0.0f;
+
+ m_attacking = NULL;
+ m_modMeleeHitChance = 0.0f;
+ m_modRangedHitChance = 0.0f;
+ m_modSpellHitChance = 0.0f;
+ m_baseSpellCritChance = 5;
+
+ m_CombatTimer = 0;
+ m_lastManaUse = 0;
+
+ //m_victimThreat = 0.0f;
+ for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ m_threatModifier[i] = 1.0f;
+ m_isSorted = true;
+ for (int i = 0; i < MAX_MOVE_TYPE; ++i)
+ m_speed_rate[i] = 1.0f;
+
+ m_removedAuras = 0;
+ m_charmInfo = NULL;
+ m_unit_movement_flags = 0;
+
+ // remove aurastates allowing special moves
+ for(int i=0; i < MAX_REACTIVE; ++i)
+ m_reactiveTimer[i] = 0;
+}
+
+Unit::~Unit()
+{
+ // set current spells as deletable
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ // spell may be safely deleted now
+ if (m_currentSpells[i]) m_currentSpells[i]->SetDeletable(true);
+ m_currentSpells[i] = NULL;
+ }
+
+ RemoveAllGameObjects();
+ RemoveAllDynObjects();
+
+ if(m_charmInfo) delete m_charmInfo;
+}
+
+void Unit::Update( uint32 p_time )
+{
+ /*if(p_time > m_AurasCheck)
+ {
+ m_AurasCheck = 2000;
+ _UpdateAura();
+ }else
+ m_AurasCheck -= p_time;*/
+
+ // WARNING! Order of execution here is important, do not change.
+ // Spells must be processed with event system BEFORE they go to _UpdateSpells.
+ // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
+ m_Events.Update( p_time );
+ _UpdateSpells( p_time );
+
+ // update combat timer only for players and pets
+ if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet() || ((Creature*)this)->isCharmed()))
+ {
+ // 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() )
+ {
+ // m_CombatTimer set at aura start and it will be freeze until aura removing
+ if ( m_CombatTimer <= p_time )
+ ClearInCombat();
+ else
+ m_CombatTimer -= p_time;
+ }
+ }
+
+ if(uint32 base_att = getAttackTimer(BASE_ATTACK))
+ {
+ setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time) );
+ }
+
+ // update abilities available only for fraction of time
+ UpdateReactives( p_time );
+
+ ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth()*0.20f);
+ ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, GetHealth() < GetMaxHealth()*0.35f);
+
+ i_motionMaster.UpdateMotion(p_time);
+}
+
+bool Unit::haveOffhandWeapon() const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ return ((Player*)this)->GetWeaponForAttack(OFF_ATTACK,true);
+ else
+ return false;
+}
+
+void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
+{
+ float x, y, z;
+ if(GetMotionMaster()->GetDestination(x, y, z))
+ SendMonsterMoveWithSpeed(x, y, z, GetUnitMovementFlags(), 0, player);
+}
+
+void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime, Player* player)
+{
+ if (!transitTime)
+ {
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+ float dz = z - GetPositionZ();
+
+ float dist = ((dx*dx) + (dy*dy) + (dz*dz));
+ if(dist<0)
+ dist = 0;
+ else
+ dist = sqrt(dist);
+
+ double speed = GetSpeed((MovementFlags & MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
+ if(speed<=0)
+ speed = 2.5f;
+ speed *= 0.001f;
+ transitTime = static_cast<uint32>(dist / speed + 0.5);
+ }
+ //float orientation = (float)atan2((double)dy, (double)dx);
+ SendMonsterMove(x, y, z, 0, MovementFlags, transitTime, player);
+}
+
+void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player)
+{
+ WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) );
+ data.append(GetPackGUID());
+
+ // Point A, starting location
+ data << GetPositionX() << GetPositionY() << GetPositionZ();
+ // unknown field - unrelated to orientation
+ // seems to increment about 1000 for every 1.7 seconds
+ // for now, we'll just use mstime
+ data << getMSTime();
+
+ data << uint8(type); // unknown
+ switch(type)
+ {
+ case 0: // normal packet
+ break;
+ case 1: // stop packet
+ SendMessageToSet( &data, true );
+ return;
+ case 3: // not used currently
+ data << uint64(0); // probably target guid
+ break;
+ case 4: // not used currently
+ data << float(0); // probably orientation
+ break;
+ }
+
+ //Movement Flags (0x0 = walk, 0x100 = run, 0x200 = fly/swim)
+ data << uint32(MovementFlags);
+
+ data << Time; // Time in between points
+ data << uint32(1); // 1 single waypoint
+ data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B
+
+ if(player)
+ player->GetSession()->SendPacket(&data);
+ else
+ SendMessageToSet( &data, true );
+}
+
+void Unit::SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags)
+{
+ uint32 traveltime = uint32(path.GetTotalLength(start, end) * 32);
+
+ uint32 pathSize = end-start;
+
+ WorldPacket data( SMSG_MONSTER_MOVE, (GetPackGUID().size()+4+4+4+4+1+4+4+4+pathSize*4*3) );
+ data.append(GetPackGUID());
+ data << GetPositionX( )
+ << GetPositionY( )
+ << GetPositionZ( );
+ data << GetOrientation( );
+ data << uint8( 0 );
+ data << uint32( MovementFlags );
+ data << uint32( traveltime );
+ data << uint32( pathSize );
+ data.append( (char*)path.GetNodes(start), pathSize * 4 * 3 );
+
+ //WPAssert( data.size() == 37 + pathnodes.Size( ) * 4 * 3 );
+ SendMessageToSet(&data, true);
+}
+
+void Unit::resetAttackTimer(WeaponAttackType type)
+{
+ m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
+}
+
+bool Unit::canReachWithAttack(Unit *pVictim) const
+{
+ assert(pVictim);
+ float reach = GetFloatValue(UNIT_FIELD_COMBATREACH);
+ if( reach <= 0.0f )
+ reach = 1.0f;
+ return IsWithinDistInMap(pVictim, reach);
+}
+
+void Unit::RemoveSpellsCausingAura(AuraType auraType)
+{
+ if (auraType >= TOTAL_AURAS) return;
+ AuraList::iterator iter, next;
+ for (iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end(); iter = next)
+ {
+ next = iter;
+ ++next;
+
+ if (*iter)
+ {
+ RemoveAurasDueToSpell((*iter)->GetId());
+ if (!m_modAuras[auraType].empty())
+ next = m_modAuras[auraType].begin();
+ else
+ return;
+ }
+ }
+}
+
+bool Unit::HasAuraType(AuraType auraType) const
+{
+ return (!m_modAuras[auraType].empty());
+}
+
+/* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */
+void Unit::RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage)
+{
+ if(!HasAuraType(auraType))
+ return;
+
+ // The chance to dispell 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;
+ if (roll_chance_f(chance))
+ RemoveSpellsCausingAura(auraType);
+}
+
+uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss)
+{
+ if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return 0;
+
+ //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)
+ {
+ const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId());
+ if(area && area->flags & AREA_FLAG_SANCTUARY) //sanctuary
+ return 0;
+ }
+
+ // remove affects from victim (including from 0 damage and DoTs)
+ if(pVictim != this)
+ pVictim->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ // remove affects from attacker at any non-DoT damage (including 0 damage)
+ if( damagetype != DOT)
+ {
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if(pVictim != this)
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY);
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->IsStandState() && !pVictim->hasUnitState(UNIT_STAT_STUNDED))
+ pVictim->SetStandState(PLAYER_STATE_NONE);
+ }
+
+ //Script Event damage Deal
+ if( GetTypeId()== TYPEID_UNIT && ((Creature *)this)->AI())
+ ((Creature *)this)->AI()->DamageDeal(pVictim, damage);
+ //Script Event damage taken
+ if( pVictim->GetTypeId()== TYPEID_UNIT && ((Creature *)pVictim)->AI() )
+ ((Creature *)pVictim)->AI()->DamageTaken(this, damage);
+
+ 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);
+
+ return 0;
+ }
+
+ pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR, damage);
+ // root type spells do not dispell the root effect
+ if(!spellProto || spellProto->Mechanic != MECHANIC_ROOT)
+ pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT, damage);
+
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ {
+ // no xp,health if type 8 /critters/
+ if ( pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER)
+ {
+ pVictim->setDeathState(JUST_DIED);
+ pVictim->SetHealth(0);
+
+ // allow loot only if has loot_id in creature_template
+ CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo();
+ if(cInfo && cInfo->lootid)
+ pVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+
+ // some critters required for quests
+ if(GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
+
+ return damage;
+ }
+
+ if(!pVictim->isInCombat() && ((Creature*)pVictim)->AI())
+ ((Creature*)pVictim)->AI()->AttackStart(this);
+ }
+
+ DEBUG_LOG("DealDamageStart");
+
+ uint32 health = pVictim->GetHealth();
+ sLog.outDetail("deal dmg:%d to health:%d ",damage,health);
+
+ // duel ends when player has 1 or less hp
+ bool duel_hasEnded = false;
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->duel && damage >= (health-1))
+ {
+ // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
+ if(((Player*)pVictim)->duel->opponent==this || ((Player*)pVictim)->duel->opponent->GetGUID() == GetOwnerGUID())
+ damage = health-1;
+
+ duel_hasEnded = true;
+ }
+ //Get in CombatState
+ if(pVictim != this && damagetype != DOT)
+ {
+ SetInCombatWith(pVictim);
+ pVictim->SetInCombatWith(this);
+
+ if(Player* attackedPlayer = pVictim->GetCharmerOrOwnerPlayerOrPlayerItself())
+ SetContestedPvP(attackedPlayer);
+ }
+
+ // Rage from Damage made (only from direct weapon damage)
+ if( cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && GetTypeId() == TYPEID_PLAYER && (getPowerType() == POWER_RAGE))
+ {
+ uint32 weaponSpeedHitFactor;
+
+ switch(cleanDamage->attackType)
+ {
+ case BASE_ATTACK:
+ {
+ if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 7);
+ else
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
+
+ ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
+
+ break;
+ }
+ case OFF_ATTACK:
+ {
+ if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
+ else
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 1.75f);
+
+ ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
+
+ break;
+ }
+ case RANGED_ATTACK:
+ break;
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)pVictim)->InBattleGround())
+ {
+ Player *killer = ((Player*)this);
+ if(killer != ((Player*)pVictim))
+ if(BattleGround *bg = killer->GetBattleGround())
+ bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage);
+ }
+ }
+
+ if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->hasLootRecipient())
+ ((Creature*)pVictim)->SetLootRecipient(this);
+ if (health <= damage)
+ {
+ // battleground things
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround()))
+ {
+ Player *killed = ((Player*)pVictim);
+ Player *killer = NULL;
+ if(GetTypeId() == TYPEID_PLAYER)
+ killer = ((Player*)this);
+ else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Unit *owner = GetOwner();
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ killer = ((Player*)owner);
+ }
+
+ if(killer)
+ if(BattleGround *bg = killed->GetBattleGround())
+ bg->HandleKillPlayer(killed, killer); // drop flags and etc
+ }
+
+ DEBUG_LOG("DealDamage: victim just died");
+
+ // find player: owner of controlled `this` or `this` itself maybe
+ Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient())
+ player = ((Creature*)pVictim)->GetLootRecipient();
+ // Reward player, his pets, and group/raid members
+ // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
+ if(player && player!=pVictim)
+ if(player->RewardPlayerAndGroupAtKill(pVictim))
+ player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_XP_GIVER,PROC_FLAG_NONE);
+
+ DEBUG_LOG("DealDamageAttackStop");
+
+ // stop combat
+ pVictim->CombatStop();
+ pVictim->getHostilRefManager().deleteReferences();
+
+ bool damageFromSpiritOfRedemtionTalent = spellProto && spellProto->Id == 27795;
+
+ // if talent known but not triggered (check priest class for speedup check)
+ Aura* spiritOfRedemtionTalentReady = NULL;
+ if( !damageFromSpiritOfRedemtionTalent && // not called from SPELL_AURA_SPIRIT_OF_REDEMPTION
+ pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST )
+ {
+ AuraList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr)
+ {
+ if((*itr)->GetSpellProto()->SpellIconID==1654)
+ {
+ spiritOfRedemtionTalentReady = *itr;
+ break;
+ }
+ }
+ }
+
+ DEBUG_LOG("SET JUST_DIED");
+ if(!spiritOfRedemtionTalentReady)
+ pVictim->setDeathState(JUST_DIED);
+
+ DEBUG_LOG("DealDamageHealth1");
+
+ if(spiritOfRedemtionTalentReady)
+ {
+ // save value before aura remove
+ uint32 ressSpellId = pVictim->GetUInt32Value(PLAYER_SELF_RES_SPELL);
+ if(!ressSpellId)
+ ressSpellId = ((Player*)pVictim)->GetResurrectionSpellId();
+
+ //Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers)
+ pVictim->RemoveAllAurasOnDeath();
+
+ // restore for use at real death
+ pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL,ressSpellId);
+
+ // FORM_SPIRITOFREDEMPTION and related auras
+ pVictim->CastSpell(pVictim,27827,true,NULL,spiritOfRedemtionTalentReady);
+ }
+ else
+ pVictim->SetHealth(0);
+
+ // remember victim PvP death for corpse type and corpse reclaim delay
+ // at original death (not at SpiritOfRedemtionTalent timeout)
+ if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent )
+ ((Player*)pVictim)->SetPvPDeath(player!=NULL);
+
+ // Call KilledUnit for creatures
+ if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->KilledUnit(pVictim);
+
+ // 10% durability loss on death
+ // clean InHateListOf
+ if (pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // only if not player and not controlled by player pet. And not at BG
+ if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround())
+ {
+ DEBUG_LOG("We are dead, loosing 10 percents durability");
+ ((Player*)pVictim)->DurabilityLossAll(0.10f,false);
+ // durability lost message
+ WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
+ ((Player*)pVictim)->GetSession()->SendPacket(&data);
+ }
+ }
+ else // creature died
+ {
+ DEBUG_LOG("DealDamageNotPlayer");
+ Creature *cVictim = (Creature*)pVictim;
+
+ if(!cVictim->isPet())
+ {
+ cVictim->DeleteThreatList();
+ cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+ }
+ // Call creature just died function
+ if (cVictim->AI())
+ cVictim->AI()->JustDied(this);
+
+ // Dungeon specific stuff, only applies to players killing creatures
+ if(cVictim->GetInstanceId())
+ {
+ Map *m = cVictim->GetMap();
+ Player *creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
+ // TODO: do instance binding anyway if the charmer/owner is offline
+
+ if(m->IsDungeon() && creditedPlayer)
+ {
+ if(m->IsRaid() || m->IsHeroic())
+ {
+ if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
+ ((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer);
+ }
+ else
+ {
+ // the reset time is set but not added to the scheduler
+ // until the players leave the instance
+ time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR;
+ if(InstanceSave *save = sInstanceSaveManager.GetInstanceSave(cVictim->GetInstanceId()))
+ if(save->GetResetTime() < resettime) save->SetResetTime(resettime);
+ }
+ }
+ }
+ }
+
+ // last damage from non duel opponent or opponent controlled creature
+ if(duel_hasEnded)
+ {
+ assert(pVictim->GetTypeId()==TYPEID_PLAYER);
+ Player *he = (Player*)pVictim;
+
+ assert(he->duel);
+
+ he->duel->opponent->CombatStopWithPets(true);
+ he->CombatStopWithPets(true);
+
+ he->DuelComplete(DUEL_INTERUPTED);
+ }
+ }
+ else // if (health <= damage)
+ {
+ DEBUG_LOG("DealDamageAlive");
+
+ pVictim->ModifyHealth(- (int32)damage);
+
+ // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage
+ if(pVictim->GetHealth()*5 < pVictim->GetMaxHealth())
+ {
+ uint32 procVictim = PROC_FLAG_NONE;
+
+ // if just dropped below 20% (for CheatDeath)
+ if((pVictim->GetHealth()+damage)*5 > pVictim->GetMaxHealth())
+ procVictim = PROC_FLAG_LOW_HEALTH;
+
+ ProcDamageAndSpell(pVictim,PROC_FLAG_TARGET_LOW_HEALTH,procVictim);
+ }
+
+ if(damagetype != DOT)
+ {
+ if(getVictim())
+ {
+ // if have target and damage pVictim just call AI recation
+ if(pVictim != getVictim() && pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI())
+ ((Creature*)pVictim)->AI()->AttackedBy(this);
+ }
+ else
+ {
+ // if not have main target then attack state with target (including AI call)
+ //start melee attacks only after melee hit
+ Attack(pVictim,(damagetype == DIRECT_DAMAGE));
+ }
+ }
+
+ // polymorphed and other negative transformed cases
+ if(pVictim->getTransForm() && pVictim->hasUnitState(UNIT_STAT_CONFUSED))
+ pVictim->RemoveAurasDueToSpell(pVictim->getTransForm());
+
+ if(damagetype == DIRECT_DAMAGE|| damagetype == SPELL_DIRECT_DAMAGE)
+ pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE);
+
+ if (pVictim->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(spellProto && IsDamageToThreatSpell(spellProto))
+ pVictim->AddThreat(this, damage*2, damageSchoolMask, spellProto);
+ else
+ pVictim->AddThreat(this, damage, damageSchoolMask, spellProto);
+ }
+ 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)))
+ {
+ EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(slot);
+ }
+ }
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ // random durability for items (HIT DONE)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE)))
+ {
+ EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
+ ((Player*)this)->DurabilityPointLossForEquipSlot(slot);
+ }
+ }
+
+ // TODO: Store auras by interrupt flag to speed this up.
+ AuraMap& vAuras = pVictim->GetAuras();
+ for (AuraMap::iterator i = vAuras.begin(), next; i != vAuras.end(); i = next)
+ {
+ const SpellEntry *se = i->second->GetSpellProto();
+ next = i; ++next;
+ if( se->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE )
+ {
+ bool remove = true;
+ if (se->procFlags & (1<<3))
+ {
+ if (!roll_chance_i(se->procChance))
+ remove = false;
+ }
+ if (remove)
+ {
+ pVictim->RemoveAurasDueToSpell(i->second->GetId());
+ // FIXME: this may cause the auras with proc chance to be rerolled several times
+ next = vAuras.begin();
+ }
+ }
+ }
+
+ if (damagetype != NODAMAGE && damage && pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( damagetype != DOT )
+ {
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ // skip channeled spell (processed differently below)
+ if (i == CURRENT_CHANNELED_SPELL)
+ continue;
+
+ if(Spell* spell = pVictim->m_currentSpells[i])
+ if(spell->getState() == SPELL_STATE_PREPARING)
+ spell->Delayed();
+ }
+ }
+
+ if(Spell* spell = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ if (spell->getState() == SPELL_STATE_CASTING)
+ {
+ uint32 channelInterruptFlags = spell->m_spellInfo->ChannelInterruptFlags;
+ if( channelInterruptFlags & CHANNEL_FLAG_DELAY )
+ {
+ if(pVictim!=this) //don't shorten the duration of channeling if you damage yourself
+ spell->DelayedChannel();
+ }
+ else if( (channelInterruptFlags & (CHANNEL_FLAG_DAMAGE | CHANNEL_FLAG_DAMAGE2)) )
+ {
+ sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
+ pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ }
+ }
+ else if (spell->getState() == SPELL_STATE_DELAYED)
+ // break channeled spell in delayed state on damage
+ {
+ sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
+ pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ }
+ }
+ }
+
+ // last damage from duel opponent
+ if(duel_hasEnded)
+ {
+ assert(pVictim->GetTypeId()==TYPEID_PLAYER);
+ Player *he = (Player*)pVictim;
+
+ assert(he->duel);
+
+ he->SetHealth(1);
+
+ he->duel->opponent->CombatStopWithPets(true);
+ he->CombatStopWithPets(true);
+
+ he->CastSpell(he, 7267, true); // beg
+ he->DuelComplete(DUEL_WON);
+ }
+ }
+
+ DEBUG_LOG("DealDamageEnd returned %d damage", damage);
+
+ return damage;
+}
+
+void Unit::CastStop(uint32 except_spellid)
+{
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id!=except_spellid)
+ InterruptSpell(i,false);
+}
+
+void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell: unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ CastSpell(Victim,spellInfo,triggered,castItem,triggredByAura, originalCaster);
+}
+
+void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell: unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( Victim );
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+void Unit::CastCustomSpell(Unit* Victim,uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastCustomSpell: unknown spell id %i\n", spellId);
+ return;
+ }
+
+ CastCustomSpell(Victim,spellInfo,bp0,bp1,bp2,triggered,castItem,triggredByAura, originalCaster);
+}
+
+void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastCustomSpell: unknown spell");
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster);
+
+ if(bp0)
+ spell->m_currentBasePoints[0] = *bp0-int32(spellInfo->EffectBaseDice[0]);
+
+ if(bp1)
+ spell->m_currentBasePoints[1] = *bp1-int32(spellInfo->EffectBaseDice[1]);
+
+ if(bp2)
+ spell->m_currentBasePoints[2] = *bp2-int32(spellInfo->EffectBaseDice[2]);
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( Victim );
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+// used for scripting
+void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ CastSpell(x, y, z,spellInfo,triggered,castItem,triggredByAura, originalCaster);
+}
+
+// used for scripting
+void Unit::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
+ SpellCastTargets targets;
+ targets.setDestination(x, y, z);
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+void Unit::DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit, bool isTriggeredSpell)
+{
+ // TODO this in only generic way, check for exceptions
+ DEBUG_LOG("DealFlatDamage (BEFORE) >> DMG:%u", *damage);
+
+ // Per-damage calss calculation
+ switch (spellInfo->DmgClass)
+ {
+ // Melee and Ranged Spells
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+ {
+ // Calculate physical outcome
+ MeleeHitOutcome outcome = RollPhysicalOutcomeAgainst(pVictim, BASE_ATTACK, spellInfo);
+
+ //Used to store the Hit Outcome
+ cleanDamage->hitOutCome = outcome;
+
+ // Return miss/evade first (sends miss message)
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ {
+ SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_EVADES,0);
+ *damage = 0;
+ return;
+ }
+ case MELEE_HIT_MISS:
+ {
+ SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_NORMAL,0);
+ *damage = 0;
+
+ if(GetTypeId()== TYPEID_PLAYER)
+ ((Player*)this)->UpdateWeaponSkill(BASE_ATTACK);
+
+ CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,MELEE_HIT_MISS,spellInfo,isTriggeredSpell);
+ return;
+ }
+ }
+
+ // Hitinfo, Victimstate
+ uint32 hitInfo = HITINFO_NORMALSWING;
+ VictimState victimState = VICTIMSTATE_NORMAL;
+
+ // Physical Damage
+ if ( GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NORMAL )
+ {
+ uint32 modDamage=*damage;
+
+ // apply spellmod to Done damage
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_DAMAGE, *damage);
+
+ //Calculate armor mitigation
+ uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
+
+ // random durability for main hand weapon (ABSORB)
+ if(damageAfterArmor < *damage)
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
+
+ cleanDamage->damage += *damage - damageAfterArmor;
+ *damage = damageAfterArmor;
+ }
+ // Magical Damage
+ else
+ {
+ // Calculate damage bonus
+ *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
+ }
+
+ // Classify outcome
+ switch (outcome)
+ {
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ {
+ uint32 bonusDmg = *damage;
+
+ // Apply crit_damage bonus
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, bonusDmg);
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ bonusDmg = uint32(bonusDmg * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
+
+ *damage += bonusDmg;
+
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
+ cleanDamage->damage += resilienceReduction;
+ *damage -= resilienceReduction;
+ }
+
+ *crit = true;
+ hitInfo |= HITINFO_CRITICALHIT;
+
+ ModifyAuraState(AURA_STATE_CRIT, true);
+ StartReactiveTimer( REACTIVE_CRIT );
+
+ if(getClass()==CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
+ StartReactiveTimer( REACTIVE_HUNTER_CRIT );
+ }
+
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
+ if (blocked_amount >= *damage)
+ {
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_BLOCKS;
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ }
+ else
+ {
+ // To Help Calculate Rage
+ cleanDamage->damage += blocked_amount;
+ *damage = *damage - blocked_amount;
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+ }
+ break;
+ }
+ case MELEE_HIT_PARRY:
+ {
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ victimState = VICTIMSTATE_PARRY;
+
+ // Counter-attack ( explained in Unit::DoAttackDamage() )
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN) )
+ {
+ // Get attack timers
+ float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
+ float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
+
+ // Reduce attack time
+ if (pVictim->haveOffhandWeapon() && offtime < basetime)
+ {
+ float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(offtime > percent20 && offtime <= percent60)
+ {
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(percent20));
+ }
+ else if(offtime > percent60)
+ {
+ offtime -= 2 * percent20;
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(offtime));
+ }
+ }
+ else
+ {
+ float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(basetime > percent20 && basetime <= percent60)
+ {
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(percent20));
+ }
+ else if(basetime > percent60)
+ {
+ basetime -= 2 * percent20;
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(basetime));
+ }
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update victim defense ?
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (PARRY)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
+ }
+
+ // Set parry flags
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ // Mongoose bite - set only Counterattack here
+ if (pVictim->getClass() == CLASS_HUNTER)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ break;
+ }
+ case MELEE_HIT_DODGE:
+ {
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateDefense();
+
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_DODGE;
+
+ // Set dodge flags
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ // Overpower
+ if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
+ {
+ ((Player*)this)->AddComboPoints(pVictim, 1);
+ StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ // Riposte
+ if (pVictim->getClass() != CLASS_ROGUE)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ break;
+ }
+ case MELEE_HIT_BLOCK:
+ {
+ uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
+ if (blocked_amount >= *damage)
+ {
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_BLOCKS;
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ }
+ else
+ {
+ // To Help Calculate Rage
+ cleanDamage->damage += blocked_amount;
+ *damage = *damage - blocked_amount;
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+ break;
+ }
+ case MELEE_HIT_EVADE: // already processed early
+ case MELEE_HIT_MISS: // already processed early
+ case MELEE_HIT_GLANCING:
+ case MELEE_HIT_CRUSHING:
+ case MELEE_HIT_NORMAL:
+ break;
+ }
+
+ // do all damage=0 cases here
+ if(*damage == 0)
+ CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,outcome,spellInfo,isTriggeredSpell);
+
+ break;
+ }
+ // Magical Attacks
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ {
+ // Calculate damage bonus
+ *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
+
+ *crit = isSpellCrit(pVictim, spellInfo, GetSpellSchoolMask(spellInfo), BASE_ATTACK);
+ if (*crit)
+ {
+ *damage = SpellCriticalBonus(spellInfo, *damage, pVictim);
+
+ // Resilience - reduce crit damage
+ if (pVictim && pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ uint32 damage_reduction = ((Player *)pVictim)->GetSpellCritDamageReduction(*damage);
+ if(*damage > damage_reduction)
+ *damage -= damage_reduction;
+ else
+ *damage = 0;
+ }
+
+ cleanDamage->hitOutCome = MELEE_HIT_CRIT;
+ }
+ // spell proc all magic damage==0 case in this function
+ if(*damage == 0)
+ {
+ // Procflags
+ uint32 procAttacker = PROC_FLAG_HIT_SPELL;
+ uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
+
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, 0, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
+ }
+
+ break;
+ }
+ }
+
+ // TODO this in only generic way, check for exceptions
+ DEBUG_LOG("DealFlatDamage (AFTER) >> DMG:%u", *damage);
+}
+
+uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell, bool useSpellDamage)
+{
+ if(!this || !pVictim)
+ return 0;
+ if(!this->isAlive() || !pVictim->isAlive())
+ return 0;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
+ if(!spellInfo)
+ return 0;
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL);
+ bool crit = false;
+
+ if (useSpellDamage)
+ DealFlatDamage(pVictim, spellInfo, &damage, &cleanDamage, &crit, isTriggeredSpell);
+
+ // If we actually dealt some damage (spell proc's for 0 damage (normal and magic) called in DealFlatDamage)
+ if(damage > 0)
+ {
+ // Calculate absorb & resists
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ CalcAbsorbResist(pVictim,GetSpellSchoolMask(spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ //No more damage left, target absorbed and/or resisted all damage
+ if (damage > absorb + resist)
+ damage -= absorb + resist; //Remove Absorbed and Resisted from damage actually dealt
+ else
+ {
+ uint32 HitInfo = HITINFO_SWINGNOHITSOUND;
+
+ if (absorb)
+ HitInfo |= HITINFO_ABSORB;
+ if (resist)
+ {
+ HitInfo |= HITINFO_RESIST;
+ ProcDamageAndSpell(pVictim, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, GetSpellSchoolMask(spellInfo), spellInfo,isTriggeredSpell);
+ }
+
+ //Send resist
+ SendAttackStateUpdate(HitInfo, pVictim, 1, GetSpellSchoolMask(spellInfo), damage, absorb,resist,VICTIMSTATE_NORMAL,0);
+ return 0;
+ }
+
+ // Deal damage done
+ damage = DealDamage(pVictim, damage, &cleanDamage, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(spellInfo), spellInfo, true);
+
+ // Send damage log
+ sLog.outDetail("SpellNonMeleeDamageLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u,absorb is %u,resist is %u",
+ GetGUIDLow(), GetTypeId(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, spellID, absorb,resist);
+
+ // Actual log sent to client
+ SendSpellNonMeleeDamageLog(pVictim, spellID, damage, GetSpellSchoolMask(spellInfo), absorb, resist, false, 0, crit);
+
+ // Procflags
+ uint32 procAttacker = PROC_FLAG_HIT_SPELL;
+ uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
+
+ if (crit)
+ {
+ procAttacker |= PROC_FLAG_CRIT_SPELL;
+ procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
+ }
+
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
+
+ return damage;
+ }
+ else
+ {
+ // all spell proc for 0 normal and magic damage called in DealFlatDamage
+
+ //Check for rage
+ if(cleanDamage.damage)
+ // Rage from damage received.
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE))
+ ((Player*)pVictim)->RewardRage(cleanDamage.damage, 0, false);
+
+ return 0;
+ }
+}
+
+void Unit::HandleEmoteCommand(uint32 anim_id)
+{
+ WorldPacket data( SMSG_EMOTE, 12 );
+ data << anim_id << GetGUID();
+ WPAssert(data.size() == 12);
+
+ SendMessageToSet(&data, true);
+}
+
+uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage)
+{
+ uint32 newdamage = 0;
+ float armor = pVictim->GetArmor();
+ // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
+ armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL);
+
+ if (armor<0.0f) armor=0.0f;
+
+ float tmpvalue = 0.0f;
+ if(getLevel() <= 59) //Level 1-59
+ tmpvalue = armor / (armor + 400.0f + 85.0f * getLevel());
+ else if(getLevel() < 70) //Level 60-69
+ tmpvalue = armor / (armor - 22167.5f + 467.5f * getLevel());
+ else //Level 70+
+ tmpvalue = armor / (armor + 10557.5f);
+
+ if(tmpvalue < 0.0f)
+ tmpvalue = 0.0f;
+ if(tmpvalue > 0.75f)
+ tmpvalue = 0.75f;
+ newdamage = uint32(damage - (damage * tmpvalue));
+
+ return (newdamage > 1) ? newdamage : 1;
+}
+
+void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist)
+{
+ if(!pVictim || !pVictim->isAlive() || !damage)
+ return;
+
+ // Magic damage, check for resists
+ if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL)==0)
+ {
+ // Get base victim resistance for school
+ float tmpvalue2 = (float)pVictim->GetResistance(GetFirstSchoolInMask(schoolMask));
+ // Ignore resistance by self SPELL_AURA_MOD_TARGET_RESISTANCE aura
+ tmpvalue2 += (float)GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask);
+
+ tmpvalue2 *= (float)(0.15f / getLevel());
+ if (tmpvalue2 < 0.0f)
+ tmpvalue2 = 0.0f;
+ if (tmpvalue2 > 0.75f)
+ tmpvalue2 = 0.75f;
+ uint32 ran = urand(0, 100);
+ uint32 faq[4] = {24,6,4,6};
+ uint8 m = 0;
+ float Binom = 0.0f;
+ for (uint8 i = 0; i < 4; i++)
+ {
+ Binom += 2400 *( powf(tmpvalue2, i) * powf( (1-tmpvalue2), (4-i)))/faq[i];
+ if (ran > Binom )
+ ++m;
+ else
+ break;
+ }
+ if (damagetype == DOT && m == 4)
+ *resist += uint32(damage - 1);
+ else
+ *resist += uint32(damage * m / 4);
+ if(*resist > damage)
+ *resist = damage;
+ }
+ else
+ *resist = 0;
+
+ int32 RemainingDamage = damage - *resist;
+
+ // absorb without mana cost
+ int32 reflectDamage = 0;
+ Aura* reflectAura = NULL;
+ AuraList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB);
+ for(AuraList::const_iterator i = vSchoolAbsorb.begin(), next; i != vSchoolAbsorb.end() && RemainingDamage > 0; i = next)
+ {
+ next = i; ++next;
+
+ if (((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Cheat Death
+ if((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && (*i)->GetSpellProto()->SpellIconID == 2109)
+ {
+ if (((Player*)pVictim)->HasSpellCooldown(31231))
+ continue;
+ if (pVictim->GetHealth() <= RemainingDamage)
+ {
+ int32 chance = (*i)->GetModifier()->m_amount;
+ if (roll_chance_i(chance))
+ {
+ pVictim->CastSpell(pVictim,31231,true);
+ ((Player*)pVictim)->AddSpellCooldown(31231,0,time(NULL)+60);
+
+ // with health > 10% lost health until health==10%, in other case no losses
+ uint32 health10 = pVictim->GetMaxHealth()/10;
+ RemainingDamage = pVictim->GetHealth() > health10 ? pVictim->GetHealth() - health10 : 0;
+ }
+ }
+ continue;
+ }
+
+ int32 currentAbsorb;
+
+ //Reflective Shield
+ if ((pVictim != this) && (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && (*i)->GetSpellProto()->SpellFamilyFlags == 0x1)
+ {
+ if(Unit* caster = (*i)->GetCaster())
+ {
+ AuraList const& vOverRideCS = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator k = vOverRideCS.begin(); k != vOverRideCS.end(); ++k)
+ {
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 5065: // Rank 1
+ case 5064: // Rank 2
+ case 5063: // Rank 3
+ case 5062: // Rank 4
+ case 5061: // Rank 5
+ {
+ if(RemainingDamage >= (*i)->GetModifier()->m_amount)
+ reflectDamage = (*i)->GetModifier()->m_amount * (*k)->GetModifier()->m_amount/100;
+ else
+ reflectDamage = (*k)->GetModifier()->m_amount * RemainingDamage/100;
+ reflectAura = *i;
+
+ } break;
+ default: break;
+ }
+
+ if(reflectDamage)
+ break;
+ }
+ }
+ }
+
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ {
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ next = vSchoolAbsorb.begin();
+ }
+ else
+ {
+ currentAbsorb = RemainingDamage;
+ (*i)->GetModifier()->m_amount -= RemainingDamage;
+ }
+
+ RemainingDamage -= currentAbsorb;
+ }
+ // do not cast spells while looping auras; auras can get invalid otherwise
+ if (reflectDamage)
+ pVictim->CastCustomSpell(this, 33619, &reflectDamage, NULL, NULL, true, NULL, reflectAura);
+
+ // absorb by mana cost
+ AuraList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD);
+ for(AuraList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ int32 currentAbsorb;
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ else
+ currentAbsorb = RemainingDamage;
+
+ float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()];
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
+
+ int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
+ if (currentAbsorb > maxAbsorb)
+ currentAbsorb = maxAbsorb;
+
+ (*i)->GetModifier()->m_amount -= currentAbsorb;
+ if((*i)->GetModifier()->m_amount <= 0)
+ {
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ next = vManaShield.begin();
+ }
+
+ int32 manaReduction = int32(currentAbsorb * manaMultiplier);
+ pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
+
+ RemainingDamage -= currentAbsorb;
+ }
+
+ AuraList const& vSplitDamageFlat = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_FLAT);
+ for(AuraList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Damage can be splitted only if aura has an alive caster
+ Unit *caster = (*i)->GetCaster();
+ if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
+ continue;
+
+ int32 currentAbsorb;
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ else
+ currentAbsorb = RemainingDamage;
+
+ RemainingDamage -= currentAbsorb;
+
+ SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, currentAbsorb, schoolMask, 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(currentAbsorb, BASE_ATTACK, MELEE_HIT_NORMAL);
+ DealDamage(caster, currentAbsorb, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
+ }
+
+ AuraList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT);
+ for(AuraList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Damage can be splitted only if aura has an alive caster
+ Unit *caster = (*i)->GetCaster();
+ if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
+ continue;
+
+ int32 splitted = int32(RemainingDamage * (*i)->GetModifier()->m_amount / 100.0f);
+
+ RemainingDamage -= splitted;
+
+ SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(splitted, BASE_ATTACK, MELEE_HIT_NORMAL);
+ DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
+ }
+
+ *absorb = damage - RemainingDamage - *resist;
+}
+
+void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell)
+{
+ MeleeHitOutcome outcome;
+
+ // If is casted Melee spell, calculate like physical
+ if(!spellCasted)
+ outcome = RollMeleeOutcomeAgainst (pVictim, attType);
+ else
+ outcome = RollPhysicalOutcomeAgainst (pVictim, attType, spellCasted);
+
+ if(outcome == MELEE_HIT_MISS ||outcome == MELEE_HIT_DODGE ||outcome == MELEE_HIT_BLOCK ||outcome == MELEE_HIT_PARRY)
+ pVictim->AddThreat(this, 0.0f);
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ {
+ *hitInfo |= HITINFO_MISS;
+ *damage = 0;
+ cleanDamage->damage = 0;
+ return;
+ }
+ case MELEE_HIT_MISS:
+ {
+ *hitInfo |= HITINFO_MISS;
+ *damage = 0;
+ cleanDamage->damage = 0;
+ if(GetTypeId()== TYPEID_PLAYER)
+ ((Player*)this)->UpdateWeaponSkill(attType);
+ return;
+ }
+ }
+
+ /// If this is a creature and it attacks from behind it has a probability to daze it's victim
+ if( (outcome==MELEE_HIT_CRIT || outcome==MELEE_HIT_CRUSHING || outcome==MELEE_HIT_NORMAL || outcome==MELEE_HIT_GLANCING) &&
+ GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) )
+ {
+ // -probability is between 0% and 40%
+ // 20% base chance
+ float Probability = 20;
+
+ //there is a newbie protection, at level 10 just 7% base chance; assuming linear function
+ if( pVictim->getLevel() < 30 )
+ Probability = 0.65f*pVictim->getLevel()+0.5;
+
+ uint32 VictimDefense=pVictim->GetDefenseSkillValue(this);
+ uint32 AttackerMeleeSkill=GetUnitMeleeSkill(pVictim);
+
+ Probability *= AttackerMeleeSkill/(float)VictimDefense;
+
+ if(Probability > 40.0f)
+ Probability = 40.0f;
+
+ if(roll_chance_f(Probability))
+ CastSpell(pVictim, 1604, true);
+ }
+
+ //Calculate the damage after armor mitigation if SPELL_SCHOOL_NORMAL
+ if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
+
+ // random durability for main hand weapon (ABSORB)
+ if(damageAfterArmor < *damage)
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
+
+ cleanDamage->damage += *damage - damageAfterArmor;
+ *damage = damageAfterArmor;
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
+ ((Player*)this)->UpdateCombatSkills(pVictim, attType, outcome, false);
+
+ if(GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateCombatSkills(this, attType, outcome, true);
+
+ switch (outcome)
+ {
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ {
+ //*hitInfo = 0xEA;
+ // 0xEA
+ *hitInfo = HITINFO_CRITICALHIT | HITINFO_NORMALSWING2 | 0x8;
+
+ // Crit bonus calc
+ uint32 crit_bonus;
+ crit_bonus = *damage;
+
+ // Apply crit_damage bonus for melee spells
+ if (spellCasted)
+ {
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellCasted->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ crit_bonus = uint32(crit_bonus * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
+ }
+
+ *damage += crit_bonus;
+
+ uint32 resilienceReduction = 0;
+
+ if(attType == RANGED_ATTACK)
+ {
+ int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
+ *damage = int32((*damage) * float((100.0f + mod)/100.0f));
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ resilienceReduction = ((Player*)pVictim)->GetRangedCritDamageReduction(*damage);
+ }
+ else
+ {
+ int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
+ mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
+ *damage = int32((*damage) * float((100.0f + mod)/100.0f));
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
+ }
+
+ *damage -= resilienceReduction;
+ cleanDamage->damage += resilienceReduction;
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
+ ((Player*)this)->UpdateWeaponSkill(attType);
+
+ ModifyAuraState(AURA_STATE_CRIT, true);
+ StartReactiveTimer( REACTIVE_CRIT );
+
+ if(getClass()==CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
+ StartReactiveTimer( REACTIVE_HUNTER_CRIT );
+ }
+
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ *blocked_amount = pVictim->GetShieldBlockValue();
+
+ if (pVictim->GetUnitBlockChance())
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+ else
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ //Only set VICTIMSTATE_BLOCK on a full block
+ if (*blocked_amount >= uint32(*damage))
+ {
+ *victimState = VICTIMSTATE_BLOCKS;
+ *blocked_amount = uint32(*damage);
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ break;
+ }
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL);
+ break;
+ }
+ case MELEE_HIT_PARRY:
+ {
+ if(attType == RANGED_ATTACK) //range attack - no parry
+ {
+ outcome = MELEE_HIT_NORMAL;
+ break;
+ }
+
+ cleanDamage->damage += *damage;
+ *damage = 0;
+ *victimState = VICTIMSTATE_PARRY;
+
+ // instant (maybe with small delay) counter attack
+ {
+ float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
+ float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
+
+ // after parry nearest next attack time will reduced at %40 from full attack time.
+ // The delay cannot be reduced to less than 20% of your weapon's base swing delay.
+ if (pVictim->haveOffhandWeapon() && offtime < basetime)
+ {
+ float percent20 = pVictim->GetAttackTime(OFF_ATTACK)*0.20;
+ float percent60 = 3*percent20;
+ // set to 20% if in range 20%...20+40% of full time
+ if(offtime > percent20 && offtime <= percent60)
+ {
+ pVictim->setAttackTimer(OFF_ATTACK,uint32(percent20));
+ }
+ // decrease at %40 from full time
+ else if(offtime > percent60)
+ {
+ offtime -= 2*percent20;
+ pVictim->setAttackTimer(OFF_ATTACK,uint32(offtime));
+ }
+ // ELSE not changed
+ }
+ else
+ {
+ float percent20 = pVictim->GetAttackTime(BASE_ATTACK)*0.20;
+ float percent60 = 3*percent20;
+ // set to 20% if in range 20%...20+40% of full time
+ if(basetime > percent20 && basetime <= percent60)
+ {
+ pVictim->setAttackTimer(BASE_ATTACK,uint32(percent20));
+ }
+ // decrease at %40 from full time
+ else if(basetime > percent60)
+ {
+ basetime -= 2*percent20;
+ pVictim->setAttackTimer(BASE_ATTACK,uint32(basetime));
+ }
+ // ELSE not changed
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update victim defense ?
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (PARRY)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
+ }
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ if (pVictim->getClass() == CLASS_HUNTER)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+ case MELEE_HIT_DODGE:
+ {
+ if(attType == RANGED_ATTACK) //range attack - no dodge
+ {
+ outcome = MELEE_HIT_NORMAL;
+ break;
+ }
+
+ cleanDamage->damage += *damage;
+ *damage = 0;
+ *victimState = VICTIMSTATE_DODGE;
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateDefense();
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ if (pVictim->getClass() != CLASS_ROGUE) // Riposte
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ // Overpower
+ if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
+ {
+ ((Player*)this)->AddComboPoints(pVictim, 1);
+ StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+ case MELEE_HIT_BLOCK:
+ {
+ *blocked_amount = pVictim->GetShieldBlockValue();
+
+ if (pVictim->GetUnitBlockChance())
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+ else
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ //Only set VICTIMSTATE_BLOCK on a full block
+ if (*blocked_amount >= uint32(*damage))
+ {
+ *victimState = VICTIMSTATE_BLOCKS;
+ *blocked_amount = uint32(*damage);
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ break;
+ }
+ case MELEE_HIT_GLANCING:
+ {
+ float reducePercent = 1.0f; //damage factor
+
+ // calculate base values and mods
+ float baseLowEnd = 1.3;
+ float baseHighEnd = 1.2;
+ switch(getClass()) // lowering base values for casters
+ {
+ case CLASS_SHAMAN:
+ case CLASS_PRIEST:
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ case CLASS_DRUID:
+ baseLowEnd -= 0.7;
+ baseHighEnd -= 0.3;
+ break;
+ }
+
+ float maxLowEnd = 0.6;
+ switch(getClass()) // upper for melee classes
+ {
+ case CLASS_WARRIOR:
+ case CLASS_ROGUE:
+ maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91
+ }
+
+ // calculate values
+ int32 diff = int32(pVictim->GetDefenseSkillValue(this)) - int32(GetWeaponSkillValue(attType,pVictim));
+ float lowEnd = baseLowEnd - ( 0.05f * diff );
+ float highEnd = baseHighEnd - ( 0.03f * diff );
+
+ // apply max/min bounds
+ if ( lowEnd < 0.01f ) //the low end must not go bellow 0.01f
+ lowEnd = 0.01f;
+ else if ( lowEnd > maxLowEnd ) //the smaller value of this and 0.6 is kept as the low end
+ lowEnd = maxLowEnd;
+
+ if ( highEnd < 0.2f ) //high end limits
+ highEnd = 0.2f;
+ if ( highEnd > 0.99f )
+ highEnd = 0.99f;
+
+ if(lowEnd > highEnd) // prevent negative range size
+ lowEnd = highEnd;
+
+ reducePercent = lowEnd + rand_norm() * ( highEnd - lowEnd );
+
+ *damage = uint32(reducePercent * *damage);
+ cleanDamage->damage += *damage;
+ *hitInfo |= HITINFO_GLANCING;
+ break;
+ }
+ case MELEE_HIT_CRUSHING:
+ {
+ // 150% normal damage
+ *damage += (*damage / 2);
+ cleanDamage->damage = *damage;
+ *hitInfo |= HITINFO_CRUSHING;
+ // TODO: victimState, victim animation?
+ break;
+ }
+ default:
+ break;
+ }
+
+ // apply melee damage bonus and absorb only if base damage not fully blocked to prevent negative damage or damage with full block
+ if(*victimState != VICTIMSTATE_BLOCKS)
+ {
+ MeleeDamageBonus(pVictim, damage,attType,spellCasted);
+ CalcAbsorbResist(pVictim, damageSchoolMask, DIRECT_DAMAGE, *damage-*blocked_amount, absorbDamage, resistDamage);
+ }
+
+ if (*absorbDamage) *hitInfo |= HITINFO_ABSORB;
+ if (*resistDamage) *hitInfo |= HITINFO_RESIST;
+
+ cleanDamage->damage += *blocked_amount;
+
+ if (*damage <= *absorbDamage + *resistDamage + *blocked_amount)
+ {
+ //*hitInfo = 0x00010020;
+ //*hitInfo |= HITINFO_SWINGNOHITSOUND;
+ //*damageType = 0;
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+
+ // update at damage Judgement aura duration that applied by attacker at victim
+ if(*damage)
+ {
+ AuraMap const& vAuras = pVictim->GetAuras();
+ for(AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+ if( (spellInfo->AttributesEx3 & 0x40000) && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN &&
+ ((*itr).second->GetCasterGUID() == GetGUID() && (!spellCasted || spellCasted->Id == 35395)) )
+ {
+ (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration());
+ (*itr).second->UpdateAuraDuration();
+ }
+ }
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, (*damage - *absorbDamage - *resistDamage - *blocked_amount), damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+
+ // victim's damage shield
+ // yet another hack to fix crashes related to the aura getting removed during iteration
+ std::set<Aura*> alreadyDone;
+ uint32 removedAuras = pVictim->m_removedAuras;
+ AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD);
+ for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next)
+ {
+ ++next;
+ if (alreadyDone.find(*i) == alreadyDone.end())
+ {
+ alreadyDone.insert(*i);
+ pVictim->SpellNonMeleeDamageLog(this, (*i)->GetId(), (*i)->GetModifier()->m_amount, false, false);
+ if (pVictim->m_removedAuras > removedAuras)
+ {
+ removedAuras = pVictim->m_removedAuras;
+ next = vDamageShields.begin();
+ }
+ }
+ }
+}
+
+void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
+{
+ if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
+ return;
+
+ if (!pVictim->isAlive())
+ return;
+
+ if(IsNonMeleeSpellCasted(false))
+ return;
+
+ uint32 hitInfo;
+ if (attType == BASE_ATTACK)
+ hitInfo = HITINFO_NORMALSWING2;
+ else if (attType == OFF_ATTACK)
+ hitInfo = HITINFO_LEFTSWING;
+ else
+ return; // ignore ranaged case
+
+ uint32 extraAttacks = m_extraAttacks;
+
+ // melee attack spell casted at main hand attack only
+ if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL])
+ {
+ m_currentSpells[CURRENT_MELEE_SPELL]->cast();
+
+ // not recent extra attack only at any non extra attack (melee spell case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+
+ return;
+ }
+
+ VictimState victimState = VICTIMSTATE_NORMAL;
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+ uint32 blocked_dmg = 0;
+ uint32 absorbed_dmg = 0;
+ uint32 resisted_dmg = 0;
+
+ SpellSchoolMask meleeSchoolMask = GetMeleeDamageSchoolMask();
+
+ if(pVictim->IsImmunedToDamage(meleeSchoolMask,true)) // use charges
+ {
+ SendAttackStateUpdate (HITINFO_NORMALSWING, pVictim, 1, meleeSchoolMask, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0);
+
+ // not recent extra attack only at any non extra attack (miss case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+
+ return;
+ }
+
+ uint32 damage = CalculateDamage (attType, false);
+
+ DoAttackDamage (pVictim, &damage, &cleanDamage, &blocked_dmg, meleeSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType);
+
+ if (hitInfo & HITINFO_MISS)
+ //send miss
+ SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
+ else
+ {
+ //do animation
+ SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
+
+ if (damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
+ damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
+ else
+ damage = 0;
+
+ DealDamage (pVictim, damage, &cleanDamage, DIRECT_DAMAGE, meleeSchoolMask, NULL, true);
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive())
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i),pVictim,attType);
+ }
+ }
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+ else
+ DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+
+ // extra attack only at any non extra attack (normal case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+}
+
+MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo)
+{
+ // Miss chance based on melee
+ float miss_chance = MeleeMissChanceCalc(pVictim, attType);
+
+ // Critical hit chance
+ float crit_chance = GetUnitCriticalChance(attType, pVictim);
+ // this is to avoid compiler issue when declaring variables inside if
+ float block_chance, parry_chance, dodge_chance;
+
+ // cannot be dodged/parried/blocked
+ if(spellInfo->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
+ {
+ block_chance = 0.0f;
+ parry_chance = 0.0f;
+ dodge_chance = 0.0f;
+ }
+ else
+ {
+ // parry can be avoided only by some abilites
+ parry_chance = pVictim->GetUnitParryChance();
+ // block might be bypassed by it as well
+ block_chance = pVictim->GetUnitBlockChance();
+ // stunned target cannot dodge and this is check in GetUnitDodgeChance()
+ dodge_chance = pVictim->GetUnitDodgeChance();
+ }
+
+ // Only players can have Talent&Spell bonuses
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Increase from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL aura
+ crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, spellInfo->SchoolMask);
+
+ if( dodge_chance != 0.0f ) // if dodge chance is already 0, ignore talents fpr speed
+ {
+ AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
+ for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
+ {
+ // can't be dodged rogue finishing move
+ if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE)
+ {
+ if(spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
+ {
+ dodge_chance = 0.0f;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Spellmods
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
+
+ DEBUG_LOG("PHYSICAL OUTCOME: miss %f crit %f dodge %f parry %f block %f",miss_chance,crit_chance,dodge_chance,parry_chance, block_chance);
+
+ return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100),int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), true);
+}
+
+MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const
+{
+ // This is only wrapper
+
+ // Miss chance based on melee
+ float miss_chance = MeleeMissChanceCalc(pVictim, attType);
+
+ // Critical hit chance
+ float crit_chance = GetUnitCriticalChance(attType, pVictim);
+
+ // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case)
+ float dodge_chance = pVictim->GetUnitDodgeChance();
+ float block_chance = pVictim->GetUnitBlockChance();
+ float parry_chance = pVictim->GetUnitParryChance();
+
+ // Useful if want to specify crit & miss chances for melee, else it could be removed
+ DEBUG_LOG("MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance,crit_chance,dodge_chance,parry_chance,block_chance);
+
+ return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), false);
+}
+
+MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const
+{
+ if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return MELEE_HIT_EVADE;
+
+ int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(pVictim);
+ int32 victimMaxSkillValueForLevel = pVictim->GetMaxSkillValueForLevel(this);
+
+ int32 attackerWeaponSkill = GetWeaponSkillValue(attType,pVictim);
+ int32 victimDefenseSkill = pVictim->GetDefenseSkillValue(this);
+
+ // bonus from skills is 0.04%
+ int32 skillBonus = 4 * ( attackerWeaponSkill - victimMaxSkillValueForLevel );
+ int32 skillBonus2 = 4 * ( attackerMaxSkillValueForLevel - victimDefenseSkill );
+ int32 sum = 0, tmp = 0;
+ int32 roll = urand (0, 10000);
+
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus);
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d",
+ roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance);
+
+ tmp = miss_chance;
+
+ if (tmp > 0 && roll < (sum += tmp ))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: MISS");
+ return MELEE_HIT_MISS;
+ }
+
+ // always crit against a sitting target (except 0 crit chance)
+ if( pVictim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !pVictim->IsStandState() )
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT (sitting victim)");
+ return MELEE_HIT_CRIT;
+ }
+
+ // Dodge chance
+
+ // only players can't dodge if attacker is behind
+ if (pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->HasInArc(M_PI,this))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind and victim was a player.");
+ }
+ else
+ {
+ // Reduce dodge chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ dodge_chance -= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
+
+ // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
+ dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
+
+ tmp = dodge_chance;
+ if ( (tmp > 0) // check if unit _can_ dodge
+ && ((tmp -= skillBonus) > 0)
+ && roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_DODGE;
+ }
+ }
+
+ // parry & block chances
+
+ // check if attack comes from behind, nobody can parry or block if attacker is behind
+ if (!pVictim->HasInArc(M_PI,this))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind.");
+ }
+ else
+ {
+ // Reduce parry chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ parry_chance-= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
+
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY) )
+ {
+ int32 tmp = int32(parry_chance);
+ if ( (tmp > 0) // check if unit _can_ parry
+ && ((tmp -= skillBonus) > 0)
+ && (roll < (sum += tmp)))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_PARRY;
+ }
+ }
+
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) )
+ {
+ tmp = block_chance;
+ if ( (tmp > 0) // check if unit _can_ block
+ && ((tmp -= skillBonus) > 0)
+ && (roll < (sum += tmp)))
+ {
+ // Critical chance
+ tmp = crit_chance + skillBonus2;
+ if ( GetTypeId() == TYPEID_PLAYER && SpellCasted && tmp > 0 )
+ {
+ if ( roll_chance_i(tmp/100))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCKED CRIT");
+ return MELEE_HIT_BLOCK_CRIT;
+ }
+ }
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_BLOCK;
+ }
+ }
+ }
+
+ // Critical chance
+ tmp = crit_chance + skillBonus2;
+
+ if (tmp > 0 && roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_CRIT;
+ }
+
+ // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon)
+ if( attType != RANGED_ATTACK && !SpellCasted &&
+ (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet()) &&
+ pVictim->GetTypeId() != TYPEID_PLAYER && !((Creature*)pVictim)->isPet() &&
+ getLevel() < pVictim->getLevelForTarget(this) )
+ {
+ // cap possible value (with bonuses > max skill)
+ int32 skill = attackerWeaponSkill;
+ int32 maxskill = attackerMaxSkillValueForLevel;
+ skill = (skill > maxskill) ? maxskill : skill;
+
+ tmp = (10 + (victimDefenseSkill - skill)) * 100;
+ tmp = tmp > 4000 ? 4000 : tmp;
+ if (roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum);
+ return MELEE_HIT_GLANCING;
+ }
+ }
+
+ if(GetTypeId()!=TYPEID_PLAYER && !(((Creature*)this)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH) && !((Creature*)this)->isPet() )
+ {
+ // mobs can score crushing blows if they're 3 or more levels above victim
+ // or when their weapon skill is 15 or more above victim's defense skill
+ tmp = victimDefenseSkill;
+ int32 tmpmax = victimMaxSkillValueForLevel;
+ // having defense above your maximum (from items, talents etc.) has no effect
+ tmp = tmp > tmpmax ? tmpmax : tmp;
+ // tmp = mob's level * 5 - player's current defense skill
+ tmp = attackerMaxSkillValueForLevel - tmp;
+ if(tmp >= 15)
+ {
+ // add 2% chance per lacking skill point, min. is 15%
+ tmp = tmp * 200 - 1500;
+ if (roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_CRUSHING;
+ }
+ }
+ }
+
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: NORMAL");
+ return MELEE_HIT_NORMAL;
+}
+
+uint32 Unit::CalculateDamage (WeaponAttackType attType, bool normalized)
+{
+ float min_damage, max_damage;
+
+ if (normalized && GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->CalculateMinMaxDamage(attType,normalized,min_damage, max_damage);
+ else
+ {
+ switch (attType)
+ {
+ case RANGED_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE);
+ break;
+ case BASE_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXDAMAGE);
+ break;
+ case OFF_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE);
+ break;
+ // Just for good manner
+ default:
+ min_damage = 0.0f;
+ max_damage = 0.0f;
+ break;
+ }
+ }
+
+ if (min_damage > max_damage)
+ {
+ std::swap(min_damage,max_damage);
+ }
+
+ if(max_damage == 0.0f)
+ max_damage = 5.0f;
+
+ return urand((uint32)min_damage, (uint32)max_damage);
+}
+
+float Unit::CalculateLevelPenalty(SpellEntry const* spellProto) const
+{
+ if(spellProto->spellLevel <= 0)
+ return 1.0f;
+
+ float LvlPenalty = 0.0f;
+
+ if(spellProto->spellLevel < 20)
+ LvlPenalty = 20.0f - spellProto->spellLevel * 3.75f;
+ float LvlFactor = (float(spellProto->spellLevel) + 6.0f) / float(getLevel());
+ if(LvlFactor > 1.0f)
+ LvlFactor = 1.0f;
+
+ return (100.0f - LvlPenalty) * LvlFactor / 100.0f;
+}
+
+void Unit::SendAttackStart(Unit* pVictim)
+{
+ WorldPacket data( SMSG_ATTACKSTART, 16 );
+ data << GetGUID();
+ data << pVictim->GetGUID();
+
+ SendMessageToSet(&data, true);
+ DEBUG_LOG( "WORLD: Sent SMSG_ATTACKSTART" );
+}
+
+void Unit::SendAttackStop(Unit* victim)
+{
+ if(!victim)
+ return;
+
+ WorldPacket data( SMSG_ATTACKSTOP, (4+16) ); // we guess size
+ data.append(GetPackGUID());
+ data.append(victim->GetPackGUID()); // can be 0x00...
+ data << uint32(0); // can be 0x1
+ SendMessageToSet(&data, true);
+ sLog.outDetail("%s %u stopped attacking %s %u", (GetTypeId()==TYPEID_PLAYER ? "player" : "creature"), GetGUIDLow(), (victim->GetTypeId()==TYPEID_PLAYER ? "player" : "creature"),victim->GetGUIDLow());
+
+ /*if(victim->GetTypeId() == TYPEID_UNIT)
+ ((Creature*)victim)->AI().EnterEvadeMode(this);*/
+}
+
+/*
+// Melee based spells can be miss, parry or dodge on this step
+// Crit or block - determined on damage calculation phase! (and can be both in some time)
+float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell)
+{
+ // Calculate hit chance (more correct for chance mod)
+ int32 HitChance;
+
+ // PvP - PvE melee chances
+ int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
+ int32 leveldif = pVictim->getLevelForTarget(this) - getLevelForTarget(pVictim);
+ if(leveldif < 3)
+ HitChance = 95 - leveldif;
+ else
+ HitChance = 93 - (leveldif - 2) * lchance;
+
+ // Hit chance depends from victim auras
+ if(attType == RANGED_ATTACK)
+ HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
+ else
+ HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
+
+ // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, HitChance);
+
+ // Miss = 100 - hit
+ float miss_chance= 100.0f - HitChance;
+
+ // Bonuses from attacker aura and ratings
+ if (attType == RANGED_ATTACK)
+ miss_chance -= m_modRangedHitChance;
+ else
+ miss_chance -= m_modMeleeHitChance;
+
+ // bonus from skills is 0.04%
+ miss_chance -= skillDiff * 0.04f;
+
+ // Limit miss chance from 0 to 60%
+ if (miss_chance < 0.0f)
+ return 0.0f;
+ if (miss_chance > 60.0f)
+ return 60.0f;
+ return miss_chance;
+}
+
+// Melee based spells hit result calculations
+SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
+{
+ WeaponAttackType attType = BASE_ATTACK;
+
+ if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
+ attType = RANGED_ATTACK;
+
+ // bonus from skills is 0.04% per skill Diff
+ int32 attackerWeaponSkill = int32(GetWeaponSkillValue(attType,pVictim));
+ int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetMaxSkillValueForLevel(this));
+ int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this));
+
+ uint32 roll = urand (0, 10000);
+ uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell)*100.0f);
+
+ // Roll miss
+ uint32 tmp = missChance;
+ if (roll < tmp)
+ return SPELL_MISS_MISS;
+
+ // Same spells cannot be parry/dodge
+ if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
+ return SPELL_MISS_NONE;
+
+ // Ranged attack can`t miss too
+ if (attType == RANGED_ATTACK)
+ return SPELL_MISS_NONE;
+
+ bool attackFromBehind = !pVictim->HasInArc(M_PI,this);
+
+ // Roll dodge
+ int32 dodgeChance = int32(pVictim->GetUnitDodgeChance()*100.0f) - skillDiff * 4;
+ // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
+ dodgeChance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
+
+ // Reduce dodge chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
+ if (dodgeChance < 0)
+ dodgeChance = 0;
+
+ // Can`t dodge from behind in PvP (but its possible in PvE)
+ if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER && attackFromBehind)
+ dodgeChance = 0;
+
+ // Rogue talent`s cant be dodged
+ AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
+ for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE) // can't be dodged rogue finishing move
+ {
+ if(spell->SpellFamilyName==SPELLFAMILY_ROGUE && (spell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
+ {
+ dodgeChance = 0;
+ break;
+ }
+ }
+ }
+
+ tmp += dodgeChance;
+ if (roll < tmp)
+ return SPELL_MISS_DODGE;
+
+ // Roll parry
+ int32 parryChance = int32(pVictim->GetUnitParryChance()*100.0f) - skillDiff * 4;
+ // Reduce parry chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
+ // Can`t parry from behind
+ if (parryChance < 0 || attackFromBehind)
+ parryChance = 0;
+
+ tmp += parryChance;
+ if (roll < tmp)
+ return SPELL_MISS_PARRY;
+
+ return SPELL_MISS_NONE;
+}*/
+
+// TODO need use unit spell resistances in calculations
+SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
+{
+ // Can`t miss on dead target (on skinning for example)
+ if (!pVictim->isAlive())
+ return SPELL_MISS_NONE;
+
+ SpellSchoolMask schoolMask = GetSpellSchoolMask(spell);
+ // PvP - PvE spell misschances per leveldif > 2
+ int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 7 : 11;
+ int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
+
+ // Base hit chance from attacker and victim levels
+ int32 modHitChance;
+ if(leveldif < 3)
+ modHitChance = 96 - leveldif;
+ else
+ modHitChance = 94 - (leveldif - 2) * lchance;
+
+ // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance);
+ // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras
+ modHitChance+=GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask);
+ // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
+ modHitChance+= pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask);
+ // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura
+ if (IsAreaOfEffectSpell(spell))
+ modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE);
+ // Reduce spell hit chance for dispel mechanic spells from victim SPELL_AURA_MOD_DISPEL_RESIST
+ if (IsDispelSpell(spell))
+ modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_DISPEL_RESIST);
+ // Chance resist mechanic (select max value from every mechanic spell effect)
+ int32 resist_mech = 0;
+ // Get effects mechanic and chance
+ for(int eff = 0; eff < 3; ++eff)
+ {
+ int32 effect_mech = GetEffectMechanic(spell, eff);
+ if (effect_mech)
+ {
+ int32 temp = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech);
+ if (resist_mech < temp)
+ resist_mech = temp;
+ }
+ }
+ // Apply mod
+ modHitChance-=resist_mech;
+
+ // Chance resist debuff
+ modHitChance-=pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel));
+
+ int32 HitChance = modHitChance * 100;
+ // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
+ HitChance += int32(m_modSpellHitChance*100.0f);
+
+ // Decrease hit chance from victim rating bonus
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ HitChance -= int32(((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)*100.0f);
+
+ if (HitChance < 100) HitChance = 100;
+ if (HitChance > 9900) HitChance = 9900;
+
+ uint32 rand = urand(0,10000);
+ if (rand > HitChance)
+ return SPELL_MISS_RESIST;
+ return SPELL_MISS_NONE;
+}
+
+SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool CanReflect)
+{
+ // Return evade for units in evade mode
+ if (pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return SPELL_MISS_EVADE;
+
+ // Check for immune (use charges)
+ if (pVictim->IsImmunedToSpell(spell,true))
+ return SPELL_MISS_IMMUNE;
+
+ // All positive spells can`t miss
+ // TODO: client not show miss log for this spells - so need find info for this in dbc and use it!
+ if (IsPositiveSpell(spell->Id))
+ return SPELL_MISS_NONE;
+
+ // Check for immune (use charges)
+ if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell),true))
+ return SPELL_MISS_IMMUNE;
+
+ // Try victim reflect spell
+ if (CanReflect)
+ {
+ // specialized first
+ Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL);
+ for(Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spell))
+ {
+ int32 reflectchance = (*i)->GetModifier()->m_amount;
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
+ {
+ if((*i)->m_procCharges > 0)
+ {
+ --(*i)->m_procCharges;
+ if((*i)->m_procCharges==0)
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ }
+ return SPELL_MISS_REFLECT;
+ }
+ }
+ }
+
+ // generic reflection
+ Unit::AuraList const& mReflectSpells = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS);
+ for(Unit::AuraList::const_iterator i = mReflectSpells.begin(); i != mReflectSpells.end(); ++i)
+ {
+ int32 reflectchance = (*i)->GetModifier()->m_amount;
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
+ {
+ if((*i)->m_procCharges > 0)
+ {
+ --(*i)->m_procCharges;
+ if((*i)->m_procCharges==0)
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ }
+ return SPELL_MISS_REFLECT;
+ }
+ }
+ }
+
+ // Temporary solution for melee based spells and spells vs SPELL_SCHOOL_NORMAL (hit result calculated after)
+ for (int i=0;i<3;i++)
+ {
+ if (spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE ||
+ spell->Effect[i] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE ||
+ spell->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG ||
+ spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL)
+ return SPELL_MISS_NONE;
+ }
+
+ // TODO need use this code for spell hit result calculation
+ // now code commented for compotability
+ switch (spell->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+// return MeleeSpellHitResult(pVictim, spell);
+ return SPELL_MISS_NONE;
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ return MagicSpellHitResult(pVictim, spell);
+ }
+ return SPELL_MISS_NONE;
+}
+
+float Unit::MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const
+{
+ if(!pVictim)
+ return 0.0f;
+
+ // Base misschance 5%
+ float misschance = 5.0f;
+
+ // DualWield - Melee spells and physical dmg spells - 5% , white damage 24%
+ if (haveOffhandWeapon() && attType != RANGED_ATTACK)
+ {
+ bool isNormal = false;
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if( m_currentSpells[i] && (GetSpellSchoolMask(m_currentSpells[i]->m_spellInfo) & SPELL_SCHOOL_MASK_NORMAL) )
+ {
+ isNormal = true;
+ break;
+ }
+ }
+ if (isNormal || m_currentSpells[CURRENT_MELEE_SPELL])
+ {
+ misschance = 5.0f;
+ }
+ else
+ {
+ misschance = 24.0f;
+ }
+ }
+
+ // PvP : PvE melee misschances per leveldif > 2
+ int32 chance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
+
+ int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
+ if(leveldif < 0)
+ leveldif = 0;
+
+ // Hit chance from attacker based on ratings and auras
+ float m_modHitChance;
+ if (attType == RANGED_ATTACK)
+ m_modHitChance = m_modRangedHitChance;
+ else
+ m_modHitChance = m_modMeleeHitChance;
+
+ if(leveldif < 3)
+ misschance += (leveldif - m_modHitChance);
+ else
+ misschance += ((leveldif - 2) * chance - m_modHitChance);
+
+ // Hit chance for victim based on ratings
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if (attType == RANGED_ATTACK)
+ misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_RANGED);
+ else
+ misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_MELEE);
+ }
+
+ // Modify miss chance by victim auras
+ if(attType == RANGED_ATTACK)
+ misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
+ else
+ misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
+
+ // Modify miss chance from skill difference ( bonus from skills is 0.04% )
+ int32 skillBonus = int32(GetWeaponSkillValue(attType,pVictim)) - int32(pVictim->GetDefenseSkillValue(this));
+ misschance -= skillBonus * 0.04f;
+
+ // Limit miss chance from 0 to 60%
+ if ( misschance < 0.0f)
+ return 0.0f;
+ if ( misschance > 60.0f)
+ return 60.0f;
+
+ return misschance;
+}
+
+uint32 Unit::GetDefenseSkillValue(Unit const* target) const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ // in PvP use full skill instead current skill value
+ uint32 value = (target && target->GetTypeId() == TYPEID_PLAYER)
+ ? ((Player*)this)->GetMaxSkillValue(SKILL_DEFENSE)
+ : ((Player*)this)->GetSkillValue(SKILL_DEFENSE);
+ value += uint32(((Player*)this)->GetRatingBonusValue(CR_DEFENSE_SKILL));
+ return value;
+ }
+ else
+ return GetUnitMeleeSkill(target);
+}
+
+float Unit::GetUnitDodgeChance() const
+{
+ if(hasUnitState(UNIT_STAT_STUNDED))
+ return 0.0f;
+ if( GetTypeId() == TYPEID_PLAYER )
+ return GetFloatValue(PLAYER_DODGE_PERCENTAGE);
+ else
+ {
+ if(((Creature const*)this)->isTotem())
+ return 0.0f;
+ else
+ {
+ float dodge = 5.0f;
+ dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
+ return dodge > 0.0f ? dodge : 0.0f;
+ }
+ }
+}
+
+float Unit::GetUnitParryChance() const
+{
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNDED))
+ return 0.0f;
+
+ float chance = 0.0f;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Player const* player = (Player const*)this;
+ if(player->CanParry() )
+ {
+ Item *tmpitem = ((Player*)this)->GetWeaponForAttack(BASE_ATTACK,true);
+ if(!tmpitem)
+ tmpitem = ((Player*)this)->GetWeaponForAttack(OFF_ATTACK,true);
+
+ if(tmpitem)
+ chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE);
+ }
+ }
+ else if(GetTypeId() == TYPEID_UNIT)
+ {
+ if(GetCreatureType() == CREATURE_TYPE_HUMANOID)
+ {
+ chance = 5.0f;
+ chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
+ }
+ }
+
+ return chance > 0.0f ? chance : 0.0f;
+}
+
+float Unit::GetUnitBlockChance() const
+{
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNDED))
+ return 0.0f;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *tmpitem = ((Player const*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ if(tmpitem && !tmpitem->IsBroken() && tmpitem->GetProto()->Block)
+ return GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
+ else
+ return 0.0f;
+ }
+ else
+ {
+ if(((Creature const*)this)->isTotem())
+ return 0.0f;
+ else
+ {
+ float block = 5.0f;
+ block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
+ return block > 0.0f ? block : 0.0f;
+ }
+ }
+}
+
+float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const
+{
+ float crit;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ switch(attackType)
+ {
+ case BASE_ATTACK:
+ crit = GetFloatValue( PLAYER_CRIT_PERCENTAGE );
+ break;
+ case OFF_ATTACK:
+ crit = GetFloatValue( PLAYER_OFFHAND_CRIT_PERCENTAGE );
+ break;
+ case RANGED_ATTACK:
+ crit = GetFloatValue( PLAYER_RANGED_CRIT_PERCENTAGE );
+ break;
+ // Just for good manner
+ default:
+ crit = 0.0f;
+ break;
+ }
+ }
+ else
+ {
+ crit = 5.0f;
+ crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PERCENT);
+ }
+
+ // flat aura mods
+ if(attackType == RANGED_ATTACK)
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
+ else
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
+
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
+
+ // reduce crit chance from Rating for players
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if (attackType==RANGED_ATTACK)
+ crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_RANGED);
+ else
+ crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
+ }
+
+ if (crit < 0.0f)
+ crit = 0.0f;
+ return crit;
+}
+
+uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) const
+{
+ uint32 value = 0;
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Item* item = ((Player*)this)->GetWeaponForAttack(attType,true);
+
+ // feral or unarmed skill only for base attack
+ if(attType != BASE_ATTACK && !item )
+ return 0;
+
+ if(((Player*)this)->IsInFeralForm())
+ return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
+
+ // weaon skill or (unarmed for base attack)
+ uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
+
+ // in PvP use full skill instead current skill value
+ value = (target && target->GetTypeId() == TYPEID_PLAYER)
+ ? ((Player*)this)->GetMaxSkillValue(skill)
+ : ((Player*)this)->GetSkillValue(skill);
+ // Modify value from ratings
+ value += uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL));
+ switch (attType)
+ {
+ case BASE_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND));break;
+ case OFF_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND));break;
+ case RANGED_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED));break;
+ }
+ }
+ else
+ value = GetUnitMeleeSkill(target);
+ return value;
+}
+
+void Unit::_UpdateSpells( uint32 time )
+{
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
+ _UpdateAutoRepeatSpell();
+
+ // remove finished spells from current pointers
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED)
+ {
+ m_currentSpells[i]->SetDeletable(true); // spell may be safely deleted now
+ m_currentSpells[i] = NULL; // remove pointer
+ }
+ }
+
+ // TODO: Find a better way to prevent crash when multiple auras are removed.
+ m_removedAuras = 0;
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ if ((*i).second)
+ (*i).second->SetUpdated(false);
+
+ for (AuraMap::iterator i = m_Auras.begin(), next; i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ if ((*i).second)
+ {
+ // prevent double update
+ if ((*i).second->IsUpdated())
+ continue;
+ (*i).second->SetUpdated(true);
+ (*i).second->Update( time );
+ // several auras can be deleted due to update
+ if (m_removedAuras)
+ {
+ if (m_Auras.empty()) break;
+ next = m_Auras.begin();
+ m_removedAuras = 0;
+ }
+ }
+ }
+
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();)
+ {
+ if ((*i).second)
+ {
+ if ( !(*i).second->GetAuraDuration() && !((*i).second->IsPermanent() || ((*i).second->IsPassive())) )
+ {
+ RemoveAura(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ if(!m_gameObj.empty())
+ {
+ std::list<GameObject*>::iterator ite1, dnext1;
+ for (ite1 = m_gameObj.begin(); ite1 != m_gameObj.end(); ite1 = dnext1)
+ {
+ dnext1 = ite1;
+ //(*i)->Update( difftime );
+ if( !(*ite1)->isSpawned() )
+ {
+ (*ite1)->SetOwnerGUID(0);
+ (*ite1)->SetRespawnTime(0);
+ (*ite1)->Delete();
+ dnext1 = m_gameObj.erase(ite1);
+ }
+ else
+ ++dnext1;
+ }
+ }
+}
+
+void Unit::_UpdateAutoRepeatSpell()
+{
+ //check "realtime" interrupts
+ if ( (GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving()) || IsNonMeleeSpellCasted(false,false,true) )
+ {
+ // cancel wand shoot
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_AutoRepeatFirstCast = true;
+ return;
+ }
+
+ //apply delay
+ if ( m_AutoRepeatFirstCast && getAttackTimer(RANGED_ATTACK) < 500 )
+ setAttackTimer(RANGED_ATTACK,500);
+ m_AutoRepeatFirstCast = false;
+
+ //castroutine
+ if (isAttackReady(RANGED_ATTACK))
+ {
+ // Check if able to cast
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true))
+ {
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ return;
+ }
+
+ // we want to shoot
+ Spell* spell = new Spell(this, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo, true, 0);
+ spell->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets));
+
+ // all went good, reset attack
+ resetAttackTimer(RANGED_ATTACK);
+ }
+}
+
+void Unit::SetCurrentCastedSpell( Spell * pSpell )
+{
+ assert(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
+
+ uint32 CSpellType = pSpell->GetCurrentContainer();
+
+ pSpell->SetDeletable(false); // spell will not be deleted until gone from current pointers
+ if (pSpell == m_currentSpells[CSpellType]) return; // avoid breaking self
+
+ // break same type spell if it is not delayed
+ InterruptSpell(CSpellType,false);
+
+ // special breakage effects:
+ switch (CSpellType)
+ {
+ case CURRENT_GENERIC_SPELL:
+ {
+ // generic spells always break channeled not delayed spells
+ InterruptSpell(CURRENT_CHANNELED_SPELL,false);
+
+ // autorepeat breaking
+ if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
+ {
+ // break autorepeat if not Auto Shot
+ if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_AutoRepeatFirstCast = true;
+ }
+ } break;
+
+ case CURRENT_CHANNELED_SPELL:
+ {
+ // channel spells always break generic non-delayed and any channeled spells
+ InterruptSpell(CURRENT_GENERIC_SPELL,false);
+ InterruptSpell(CURRENT_CHANNELED_SPELL);
+
+ // it also does break autorepeat if not Auto Shot
+ if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] &&
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351 )
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ } break;
+
+ case CURRENT_AUTOREPEAT_SPELL:
+ {
+ // only Auto Shoot does not break anything
+ if (pSpell->m_spellInfo->Category == 351)
+ {
+ // generic autorepeats break generic non-delayed and channeled non-delayed spells
+ InterruptSpell(CURRENT_GENERIC_SPELL,false);
+ InterruptSpell(CURRENT_CHANNELED_SPELL,false);
+ }
+ // special action: set first cast flag
+ m_AutoRepeatFirstCast = true;
+ } break;
+
+ default:
+ {
+ // other spell types don't break anything now
+ } break;
+ }
+
+ // current spell (if it is still here) may be safely deleted now
+ if (m_currentSpells[CSpellType])
+ m_currentSpells[CSpellType]->SetDeletable(true);
+
+ // set new current spell
+ m_currentSpells[CSpellType] = pSpell;
+}
+
+void Unit::InterruptSpell(uint32 spellType, bool withDelayed)
+{
+ assert(spellType < CURRENT_MAX_SPELL);
+
+ if(m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED) )
+ {
+ // send autorepeat cancel message for autorepeat spells
+ if (spellType == CURRENT_AUTOREPEAT_SPELL)
+ {
+ if(GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->SendAutoRepeatCancel();
+ }
+
+ if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED)
+ m_currentSpells[spellType]->cancel();
+ m_currentSpells[spellType]->SetDeletable(true);
+ m_currentSpells[spellType] = NULL;
+ }
+}
+
+bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const
+{
+ // We don't do loop here to explicitly show that melee spell is excluded.
+ // Maybe later some special spells will be excluded too.
+
+ // generic spells are casted when they are not finished and not delayed
+ 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) )
+ 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) )
+ 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);
+
+ return(false);
+}
+
+void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id)
+{
+ // generic spells are interrupted if they are not finished or delayed
+ if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ if ( (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+ (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) )
+ m_currentSpells[CURRENT_GENERIC_SPELL]->cancel();
+ m_currentSpells[CURRENT_GENERIC_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_GENERIC_SPELL] = NULL;
+ }
+
+ // autorepeat spells are interrupted if they are not finished or delayed
+ if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ // send disable autorepeat packet in any case
+ if(GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->SendAutoRepeatCancel();
+
+ if ( (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+ (withDelayed || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_DELAYED) )
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->cancel();
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL] = NULL;
+ }
+
+ // channeled spells are interrupted if they are not finished, even if they are delayed
+ if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ if (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)
+ m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel();
+ m_currentSpells[CURRENT_CHANNELED_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL;
+ }
+}
+
+Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
+{
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ if(m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id==spell_id)
+ return m_currentSpells[i];
+ return NULL;
+}
+
+bool Unit::isInFront(Unit const* target, float distance, float arc) const
+{
+ return IsWithinDistInMap(target, distance) && HasInArc( arc, target );
+}
+
+void Unit::SetInFront(Unit const* target)
+{
+ SetOrientation(GetAngle(target));
+}
+
+bool Unit::isInBack(Unit const* target, float distance, float arc) const
+{
+ return IsWithinDistInMap(target, distance) && !HasInArc( 2 * M_PI - arc, target );
+}
+
+bool Unit::isInAccessablePlaceFor(Creature const* c) const
+{
+ if(IsInWater())
+ return c->canSwim();
+ else
+ return c->canWalk();
+}
+
+bool Unit::IsInWater() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId())->IsInWater(GetPositionX(),GetPositionY(), GetPositionZ());
+}
+
+bool Unit::IsUnderWater() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId())->IsUnderWater(GetPositionX(),GetPositionY(),GetPositionZ());
+}
+
+void Unit::DeMorph()
+{
+ SetDisplayId(GetNativeDisplayId());
+}
+
+int32 Unit::GetTotalAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ modifier += (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplier(AuraType auratype) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ multipler *= (100.0f + (*i)->GetModifier()->m_amount)/100.0f;
+
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ if ((*i)->GetModifier()->m_amount > modifier)
+ modifier = (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ if ((*i)->GetModifier()->m_amount < modifier)
+ modifier = (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask)
+ modifier += mod->m_amount;
+ }
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask)
+ multipler *= (100.0f + mod->m_amount)/100.0f;
+ }
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask && mod->m_amount > modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask && mod->m_amount < modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value)
+ modifier += mod->m_amount;
+ }
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value)
+ multipler *= (100.0f + mod->m_amount)/100.0f;
+ }
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value && mod->m_amount > modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value && mod->m_amount < modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+bool Unit::AddAura(Aura *Aur)
+{
+ // 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 &&
+ (GetTypeId()!=TYPEID_PLAYER || !((Player*)this)->GetSession()->PlayerLoading()) )
+ {
+ delete Aur;
+ return false;
+ }
+
+ if(Aur->GetTarget() != this)
+ {
+ sLog.outError("Aura (spell %u eff %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)",
+ Aur->GetId(),Aur->GetEffIndex(),(GetTypeId()==TYPEID_PLAYER?"player":"creature"),GetGUIDLow(),
+ (Aur->GetTarget()->GetTypeId()==TYPEID_PLAYER?"player":"creature"),Aur->GetTarget()->GetGUIDLow());
+ delete Aur;
+ return false;
+ }
+
+ SpellEntry const* aurSpellInfo = Aur->GetSpellProto();
+
+ spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex());
+ AuraMap::iterator i = m_Auras.find( spair );
+
+ // take out same spell
+ if (i != m_Auras.end())
+ {
+ // passive and persistent auras can stack with themselves any number of times
+ if (!Aur->IsPassive() && !Aur->IsPersistent())
+ {
+ // replace aura if next will > spell StackAmount
+ if(aurSpellInfo->StackAmount)
+ {
+ if(m_Auras.count(spair) >= aurSpellInfo->StackAmount)
+ RemoveAura(i,AURA_REMOVE_BY_STACK);
+ }
+ // if StackAmount==0 not allow auras from same caster
+ else
+ {
+ for(AuraMap::iterator i2 = m_Auras.lower_bound(spair); i2 != m_Auras.upper_bound(spair); ++i2)
+ {
+ if(i2->second->GetCasterGUID()==Aur->GetCasterGUID())
+ {
+ // can be only single (this check done at _each_ aura add
+ RemoveAura(i2,AURA_REMOVE_BY_STACK);
+ break;
+ }
+
+ bool stop = false;
+ switch(aurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()])
+ {
+ // DoT/HoT/etc
+ case SPELL_AURA_PERIODIC_DAMAGE: // allow stack
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ case SPELL_AURA_PERIODIC_LEECH:
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ case SPELL_AURA_PERIODIC_ENERGIZE:
+ case SPELL_AURA_OBS_MOD_MANA:
+ case SPELL_AURA_POWER_BURN_MANA:
+ break;
+ default: // not allow
+ // can be only single (this check done at _each_ aura add
+ RemoveAura(i2,AURA_REMOVE_BY_STACK);
+ stop = true;
+ break;
+ }
+
+ if(stop)
+ break;
+ }
+ }
+ }
+ }
+
+ // passive auras stack with all (except passive spell proc auras)
+ if ((!Aur->IsPassive() || !IsPassiveStackableSpell(Aur->GetId())) &&
+ !(Aur->GetId() == 20584 || Aur->GetId() == 8326))
+ {
+ if (!RemoveNoStackAurasDueToAura(Aur))
+ {
+ delete Aur;
+ return false; // couldnt remove conflicting aura with higher rank
+ }
+ }
+
+ // update single target auras list (before aura add to aura list, to prevent unexpected remove recently added aura)
+ if (IsSingleTargetSpell(aurSpellInfo) && Aur->GetTarget())
+ {
+ // caster pointer can be deleted in time aura remove, find it by guid at each iteration
+ for(;;)
+ {
+ Unit* caster = Aur->GetCaster();
+ if(!caster) // caster deleted and not required adding scAura
+ break;
+
+ bool restart = false;
+ AuraList& scAuras = caster->GetSingleCastAuras();
+ for(AuraList::iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
+ {
+ if( (*itr)->GetTarget() != Aur->GetTarget() &&
+ IsSingleTargetSpells((*itr)->GetSpellProto(),aurSpellInfo) )
+ {
+ if ((*itr)->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for IsSingleTargetSpell", (*itr)->GetId(), (*itr)->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ (*itr)->GetTarget()->RemoveAura((*itr)->GetId(), (*itr)->GetEffIndex());
+ restart = true;
+ break;
+ }
+ }
+
+ if(!restart)
+ {
+ // done
+ scAuras.push_back(Aur);
+ break;
+ }
+ }
+ }
+
+ // add aura, register in lists and arrays
+ Aur->_AddAura();
+ m_Auras.insert(AuraMap::value_type(spellEffectPair(Aur->GetId(), Aur->GetEffIndex()), Aur));
+ if (Aur->GetModifier()->m_auraname < TOTAL_AURAS)
+ {
+ m_modAuras[Aur->GetModifier()->m_auraname].push_back(Aur);
+ }
+
+ Aur->ApplyModifier(true,true);
+ sLog.outDebug("Aura %u now is in use", Aur->GetModifier()->m_auraname);
+ return true;
+}
+
+void Unit::RemoveRankAurasDueToSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return;
+ AuraMap::iterator i,next;
+ for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ uint32 i_spellId = (*i).second->GetId();
+ if((*i).second && i_spellId && i_spellId != spellId)
+ {
+ if(spellmgr.IsRankSpellDueToSpell(spellInfo,i_spellId))
+ {
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ }
+ }
+}
+
+bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
+{
+ if (!Aur)
+ return false;
+
+ SpellEntry const* spellProto = Aur->GetSpellProto();
+ if (!spellProto)
+ return false;
+
+ uint32 spellId = Aur->GetId();
+ uint32 effIndex = Aur->GetEffIndex();
+
+ SpellSpecific spellId_spec = GetSpellSpecific(spellId);
+
+ AuraMap::iterator i,next;
+ for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ if (!(*i).second) continue;
+
+ SpellEntry const* i_spellProto = (*i).second->GetSpellProto();
+
+ if (!i_spellProto)
+ continue;
+
+ uint32 i_spellId = i_spellProto->Id;
+
+ if(IsPassiveSpell(i_spellId))
+ {
+ if(IsPassiveStackableSpell(i_spellId))
+ continue;
+
+ // passive non-stackable spells not stackable only with another rank of same spell
+ if (!spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
+ continue;
+ }
+
+ uint32 i_effIndex = (*i).second->GetEffIndex();
+
+ if(i_spellId == spellId) continue;
+
+ bool is_triggered_by_spell = false;
+ // prevent triggered aura of removing aura that triggered it
+ for(int j = 0; j < 3; ++j)
+ if (i_spellProto->EffectTriggerSpell[j] == spellProto->Id)
+ is_triggered_by_spell = true;
+ if (is_triggered_by_spell) continue;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ // prevent remove dummy triggered spells at next effect aura add
+ switch(spellProto->Effect[j]) // main spell auras added added after triggred spell
+ {
+ case SPELL_EFFECT_DUMMY:
+ switch(spellId)
+ {
+ case 5420: if(i_spellId==34123) is_triggered_by_spell = true; break;
+ }
+ break;
+ }
+
+ if(is_triggered_by_spell)
+ break;
+
+ // prevent remove form main spell by triggred passive spells
+ switch(i_spellProto->EffectApplyAuraName[j]) // main aura added before triggered spell
+ {
+ case SPELL_AURA_MOD_SHAPESHIFT:
+ switch(i_spellId)
+ {
+ case 24858: if(spellId==24905) is_triggered_by_spell = true; break;
+ case 33891: if(spellId==5420 || spellId==34123) is_triggered_by_spell = true; break;
+ case 34551: if(spellId==22688) is_triggered_by_spell = true; break;
+ }
+ break;
+ }
+ }
+
+ if(!is_triggered_by_spell)
+ {
+ SpellSpecific i_spellId_spec = GetSpellSpecific(i_spellId);
+
+ bool is_sspc = IsSingleFromSpellSpecificPerCaster(spellId_spec,i_spellId_spec);
+
+ if( is_sspc && Aur->GetCasterGUID() == (*i).second->GetCasterGUID() )
+ {
+ // cannot remove higher rank
+ if (spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
+ if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
+ return false;
+
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ else if( !is_sspc && spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId) )
+ {
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ // Potions stack aura by aura (elixirs/flask already checked)
+ else if( spellProto->SpellFamilyName == SPELLFAMILY_POTION && i_spellProto->SpellFamilyName == SPELLFAMILY_POTION )
+ {
+ if (IsNoStackAuraDueToAura(spellId, effIndex, i_spellId, i_effIndex))
+ {
+ if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
+ return false; // cannot remove higher rank
+
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAura(i);
+ next = i;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void Unit::RemoveAura(uint32 spellId, uint32 effindex, Aura* except)
+{
+ spellEffectPair spair = spellEffectPair(spellId, effindex);
+ for(AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
+ {
+ if(iter->second!=except)
+ {
+ RemoveAura(iter);
+ iter = m_Auras.lower_bound(spair);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ Aura *aur = iter->second;
+ if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
+ {
+ // Custom dispel case
+ // Unstable Affliction
+ if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags & 0x010000000000LL))
+ {
+ int32 damage = aur->GetModifier()->m_amount*9;
+ uint64 caster_guid = aur->GetCasterGUID();
+
+ // Remove aura
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+
+ // backfire damage and silence
+ dispeler->CastCustomSpell(dispeler, 31117, &damage, NULL, NULL, true, NULL, NULL,caster_guid);
+
+ iter = m_Auras.begin(); // iterator can be invalidate at cast if self-dispel
+ }
+ else
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ Aura *aur = iter->second;
+ if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
+ {
+ int32 basePoints = aur->GetBasePoints();
+ // construct the new aura for the attacker
+ Aura * new_aur = CreateAura(aur->GetSpellProto(), aur->GetEffIndex(), &basePoints, stealer);
+ if(!new_aur)
+ continue;
+
+ // set its duration and maximum duration
+ // max duration 2 minutes (in msecs)
+ int32 dur = aur->GetAuraDuration();
+ const int32 max_dur = 2*MINUTE*1000;
+ new_aur->SetAuraMaxDuration( max_dur > dur ? dur : max_dur );
+ new_aur->SetAuraDuration( max_dur > dur ? dur : max_dur );
+
+ // add the new aura to stealer
+ stealer->AddAura(new_aur);
+
+ // Remove aura as dispel
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellByCancel(uint32 spellId)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ if (iter->second->GetId() == spellId)
+ RemoveAura(iter, AURA_REMOVE_BY_CANCEL);
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasWithDispelType( DispelType type )
+{
+ // Create dispel mask by dispel type
+ uint32 dispelMask = GetDispellMask(type);
+ // Dispel all existing auras vs current dispell type
+ AuraMap& auras = GetAuras();
+ for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
+ {
+ SpellEntry const* spell = itr->second->GetSpellProto();
+ if( (1<<spell->Dispel) & dispelMask )
+ {
+ // Dispel aura
+ RemoveAurasDueToSpell(spell->Id);
+ itr = auras.begin();
+ }
+ else
+ ++itr;
+ }
+}
+
+void Unit::RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if(iter != m_Auras.end())
+ RemoveAura(iter);
+}
+
+void Unit::RemoveAurasDueToSpell(uint32 spellId, Aura* except)
+{
+ for (int i = 0; i < 3; ++i)
+ RemoveAura(spellId,i,except);
+}
+
+void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId)
+{
+ for (int k=0; k < 3; ++k)
+ {
+ spellEffectPair spair = spellEffectPair(spellId, k);
+ for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
+ {
+ if (iter->second->GetCastItemGUID() == castItem->GetGUID())
+ {
+ RemoveAura(iter);
+ iter = m_Auras.upper_bound(spair); // overwrite by more appropriate
+ }
+ else
+ ++iter;
+ }
+ }
+}
+
+void Unit::RemoveAurasWithInterruptFlags(uint32 flags)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ if (iter->second->GetSpellProto()->AuraInterruptFlags & flags)
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveNotOwnSingleTargetAuras()
+{
+ // 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);
+ else
+ ++iter;
+ }
+
+ // single target auras at other targets
+ AuraList& scAuras = GetSingleCastAuras();
+ for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end(); )
+ {
+ Aura* aura = *iter;
+ if (aura->GetTarget()!=this)
+ {
+ scAuras.erase(iter); // explicitly remove, instead waiting remove in RemoveAura
+ aura->GetTarget()->RemoveAura(aura->GetId(),aura->GetEffIndex());
+ iter = scAuras.begin();
+ }
+ else
+ ++iter;
+ }
+
+}
+
+void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
+{
+ if (IsSingleTargetSpell((*i).second->GetSpellProto()))
+ {
+ if(Unit* caster = (*i).second->GetCaster())
+ {
+ AuraList& scAuras = caster->GetSingleCastAuras();
+ scAuras.remove((*i).second);
+ }
+ else
+ {
+ sLog.outError("Couldn't find the caster of the single target aura, may crash later!");
+ assert(false);
+ }
+ }
+
+ if ((*i).second->GetModifier()->m_auraname < TOTAL_AURAS)
+ {
+ m_modAuras[(*i).second->GetModifier()->m_auraname].remove((*i).second);
+ }
+
+ // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
+ Aura* Aur = i->second;
+ // Set remove mode
+ Aur->SetRemoveMode(mode);
+ // some ShapeshiftBoosts at remove trigger removing other auras including parent Shapeshift aura
+ // remove aura from list before to prevent deleting it before
+ m_Auras.erase(i);
+ ++m_removedAuras; // internal count used by unit update
+
+ // Status unsummoned at aura remove
+ Totem* statue = NULL;
+ if(IsChanneledSpell(Aur->GetSpellProto()))
+ if(Unit* caster = Aur->GetCaster())
+ if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE)
+ statue = ((Totem*)caster);
+
+ sLog.outDebug("Aura %u now is remove mode %d",Aur->GetModifier()->m_auraname, mode);
+ Aur->ApplyModifier(false,true);
+ Aur->_RemoveAura();
+ delete Aur;
+
+ if(statue)
+ statue->UnSummon();
+
+ // only way correctly remove all auras from list
+ if( m_Auras.empty() )
+ i = m_Auras.end();
+ else
+ i = m_Auras.begin();
+}
+
+void Unit::RemoveAllAuras()
+{
+ while (!m_Auras.empty())
+ {
+ AuraMap::iterator iter = m_Auras.begin();
+ RemoveAura(iter);
+ }
+}
+
+void Unit::RemoveAllAurasOnDeath()
+{
+ // used just after dieing to remove all visible auras
+ // and disable the mods for the passive ones
+ for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
+ {
+ if (!iter->second->IsPassive() && !iter->second->IsDeathPersistent())
+ RemoveAura(iter, AURA_REMOVE_BY_DEATH);
+ else
+ ++iter;
+ }
+}
+
+void Unit::DelayAura(uint32 spellId, uint32 effindex, int32 delaytime)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if (iter != m_Auras.end())
+ {
+ if (iter->second->GetAuraDuration() < delaytime)
+ iter->second->SetAuraDuration(0);
+ else
+ iter->second->SetAuraDuration(iter->second->GetAuraDuration() - delaytime);
+ iter->second->UpdateAuraDuration();
+ sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",iter->second->GetModifier()->m_auraname, GetGUIDLow(), iter->second->GetAuraDuration());
+ }
+}
+
+void Unit::_RemoveAllAuraMods()
+{
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ {
+ (*i).second->ApplyModifier(false);
+ }
+}
+
+void Unit::_ApplyAllAuraMods()
+{
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ {
+ (*i).second->ApplyModifier(true);
+ }
+}
+
+Aura* Unit::GetAura(uint32 spellId, uint32 effindex)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if (iter != m_Auras.end())
+ return iter->second;
+ return NULL;
+}
+
+void Unit::AddDynObject(DynamicObject* dynObj)
+{
+ m_dynObjGUIDs.push_back(dynObj->GetGUID());
+}
+
+void Unit::RemoveDynObject(uint32 spellid)
+{
+ if(m_dynObjGUIDs.empty())
+ return;
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ }
+ else if(spellid == 0 || dynObj->GetSpellId() == spellid)
+ {
+ dynObj->Delete();
+ i = m_dynObjGUIDs.erase(i);
+ }
+ else
+ ++i;
+ }
+}
+
+void Unit::RemoveAllDynObjects()
+{
+ while(!m_dynObjGUIDs.empty())
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(dynObj)
+ dynObj->Delete();
+ m_dynObjGUIDs.erase(m_dynObjGUIDs.begin());
+ }
+}
+
+DynamicObject * Unit::GetDynObject(uint32 spellId, uint32 effIndex)
+{
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ continue;
+ }
+
+ if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effIndex)
+ return dynObj;
+ ++i;
+ }
+ return NULL;
+}
+
+DynamicObject * Unit::GetDynObject(uint32 spellId)
+{
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ continue;
+ }
+
+ if (dynObj->GetSpellId() == spellId)
+ return dynObj;
+ ++i;
+ }
+ return NULL;
+}
+
+void Unit::AddGameObject(GameObject* gameObj)
+{
+ assert(gameObj && gameObj->GetOwnerGUID()==0);
+ m_gameObj.push_back(gameObj);
+ gameObj->SetOwnerGUID(GetGUID());
+}
+
+void Unit::RemoveGameObject(GameObject* gameObj, bool del)
+{
+ assert(gameObj && gameObj->GetOwnerGUID()==GetGUID());
+
+ // GO created by some spell
+ if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() )
+ {
+ SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId());
+ // Need activate spell use for owner
+ if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ ((Player*)this)->SendCooldownEvent(createBySpell);
+ }
+ gameObj->SetOwnerGUID(0);
+ m_gameObj.remove(gameObj);
+ if(del)
+ {
+ gameObj->SetRespawnTime(0);
+ gameObj->Delete();
+ }
+}
+
+void Unit::RemoveGameObject(uint32 spellid, bool del)
+{
+ if(m_gameObj.empty())
+ return;
+ std::list<GameObject*>::iterator i, next;
+ for (i = m_gameObj.begin(); i != m_gameObj.end(); i = next)
+ {
+ next = i;
+ if(spellid == 0 || (*i)->GetSpellId() == spellid)
+ {
+ (*i)->SetOwnerGUID(0);
+ if(del)
+ {
+ (*i)->SetRespawnTime(0);
+ (*i)->Delete();
+ }
+
+ next = m_gameObj.erase(i);
+ }
+ else
+ ++next;
+ }
+}
+
+void Unit::RemoveAllGameObjects()
+{
+ // remove references to unit
+ for(std::list<GameObject*>::iterator i = m_gameObj.begin(); i != m_gameObj.end();)
+ {
+ (*i)->SetOwnerGUID(0);
+ (*i)->SetRespawnTime(0);
+ (*i)->Delete();
+ i = m_gameObj.erase(i);
+ }
+}
+
+void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit)
+{
+ sLog.outDebug("Sending: SMSG_SPELLNONMELEEDAMAGELOG");
+ WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+4+4+1+4+4+1+1+4+4+1)); // we guess size
+ data.append(target->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(Damage-AbsorbedDamage-Resist-Blocked);
+ data << uint8(damageSchoolMask); // spell school
+ data << uint32(AbsorbedDamage); // AbsorbedDamage
+ data << uint32(Resist); // resist
+ data << uint8(PhysicalDamage); // if 1, then client show spell name (example: %s's ranged shot hit %s for %u school or %s suffers %u school damage from %s's spell_name
+ data << uint8(0); // unk isFromAura
+ data << uint32(Blocked); // blocked
+ data << uint32(CriticalHit ? 0x27 : 0x25); // hitType, flags: 0x2 - SPELL_HIT_TYPE_CRIT, 0x10 - replace caster?
+ data << uint8(0); // isDebug?
+ SendMessageToSet( &data, true );
+}
+
+void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
+{
+ WorldPacket data(SMSG_SPELLLOGMISS, (4+8+1+4+8+1));
+ data << uint32(spellID);
+ data << uint64(GetGUID());
+ data << uint8(0); // can be 0 or 1
+ data << uint32(1); // target count
+ // for(i = 0; i < target count; ++i)
+ data << uint64(target->GetGUID()); // target GUID
+ data << uint8(missInfo);
+ // end loop
+ SendMessageToSet(&data, true);
+}
+
+void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
+{
+ sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
+
+ WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size
+ data << (uint32)HitInfo;
+ data.append(GetPackGUID());
+ data.append(target->GetPackGUID());
+ data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
+
+ data << (uint8)SwingType; // count?
+
+ // for(i = 0; i < SwingType; ++i)
+ data << (uint32)damageSchoolMask;
+ data << (float)(Damage-AbsorbDamage-Resist-BlockedAmount);
+ // still need to double check damage
+ data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
+ data << (uint32)AbsorbDamage;
+ data << (uint32)Resist;
+ // end loop
+
+ data << (uint32)TargetState;
+
+ if( AbsorbDamage == 0 ) //also 0x3E8 = 0x3E8, check when that happens
+ data << (uint32)0;
+ else
+ data << (uint32)-1;
+
+ data << (uint32)0;
+ data << (uint32)BlockedAmount;
+
+ SendMessageToSet( &data, true );
+}
+
+void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellSchoolMask damageSchoolMask, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType)
+{
+ sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim);
+ if(procSpell)
+ sLog.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell->Id, (isTriggeredSpell?"(triggered)":""));
+
+ // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities
+ // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect
+ // That is the question though if it's fully correct
+ if(procSpell && !isTriggeredSpell)
+ {
+ if(procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE)
+ {
+ if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_MELEE;
+ if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_MELEE;
+ if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_MELEE;
+ if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_MELEE;
+ attType = BASE_ATTACK; // Melee abilities are assumed to be dealt with mainhand weapon
+ }
+ else if (procSpell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
+ {
+ if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_RANGED;
+ if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_RANGED;
+ if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_RANGED;
+ if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_RANGED;
+ attType = RANGED_ATTACK;
+ }
+ }
+ if(damage && (procVictim & (PROC_FLAG_STRUCK_MELEE|PROC_FLAG_STRUCK_RANGED|PROC_FLAG_STRUCK_SPELL)))
+ procVictim |= (PROC_FLAG_TAKE_DAMAGE|PROC_FLAG_TOUCH);
+
+ // Not much to do if no flags are set.
+ if (procAttacker)
+ {
+ // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
+ ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcEffectAuraTypes,attType, procSpell, damage, damageSchoolMask);
+ ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcCastAuraTypes,attType, procSpell, damage, damageSchoolMask);
+ }
+
+ // Now go on with a victim's events'n'auras
+ // Not much to do if no flags are set or there is no victim
+ if(pVictim && pVictim->isAlive() && procVictim)
+ {
+ // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
+ pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcEffectAuraTypes,attType,procSpell, damage, damageSchoolMask);
+ pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcCastAuraTypes,attType,procSpell, damage, damageSchoolMask);
+ }
+}
+
+void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted, bool isTriggeredSpell)
+{
+ if(!pVictim)
+ return;
+
+ uint32 procAttacker = PROC_FLAG_NONE;
+ uint32 procVictim = PROC_FLAG_NONE;
+
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ return;
+ case MELEE_HIT_MISS:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_MISS;
+ }
+ break;
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ if(spellCasted && attType == BASE_ATTACK)
+ {
+ procAttacker |= PROC_FLAG_CRIT_SPELL;
+ procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ procVictim |= PROC_FLAG_BLOCK;
+ procAttacker |= PROC_FLAG_TARGET_BLOCK;
+ }
+ }
+ else if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
+ }
+ break;
+ case MELEE_HIT_PARRY:
+ procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
+ procVictim = PROC_FLAG_PARRY;
+ break;
+ case MELEE_HIT_BLOCK:
+ procAttacker = PROC_FLAG_TARGET_BLOCK;
+ procVictim = PROC_FLAG_BLOCK;
+ break;
+ case MELEE_HIT_DODGE:
+ procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
+ procVictim = PROC_FLAG_DODGE;
+ break;
+ case MELEE_HIT_CRUSHING:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
+ }
+ break;
+ default:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED;
+ }
+ break;
+ }
+
+ if(damage > 0)
+ procVictim |= PROC_FLAG_TAKE_DAMAGE;
+
+ if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE)
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType);
+}
+
+bool Unit::HandleHasteAuraProc(Unit *pVictim, SpellEntry const *hasteSpell, uint32 /*effIndex*/, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 cooldown)
+{
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+ Unit* target = pVictim;
+ int32 basepoints0 = 0;
+
+ switch(hasteSpell->SpellFamilyName)
+ {
+ case SPELLFAMILY_ROGUE:
+ {
+ switch(hasteSpell->Id)
+ {
+ // Blade Flurry
+ case 13877:
+ case 33735:
+ {
+ target = SelectNearbyTarget();
+ if(!target)
+ return false;
+ basepoints0 = damage;
+ triggered_spell_id = 22482;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // processed charge only counting case
+ if(!triggered_spell_id)
+ return true;
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleHasteAuraProc: Spell %u have not existed triggered spell %u",hasteSpell->Id,triggered_spell_id);
+ return false;
+ }
+
+ // default case
+ if(!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
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
+{
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+ Unit* target = pVictim;
+ int32 basepoints0 = 0;
+
+ switch(dummySpell->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch (dummySpell->Id)
+ {
+ // Eye of Eye
+ case 9799:
+ case 25988:
+ {
+ // prevent damage back from weapon special attacks
+ if (!procSpell || procSpell->DmgClass != SPELL_DAMAGE_CLASS_MAGIC )
+ return false;
+
+ // return damage % to attacker but < 50% own total health
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*int32(damage)/100;
+ if(basepoints0 > GetMaxHealth()/2)
+ basepoints0 = GetMaxHealth()/2;
+
+ triggered_spell_id = 25997;
+ break;
+ }
+ // Sweeping Strikes
+ case 12328:
+ case 18765:
+ case 35429:
+ {
+ // prevent chain of triggred spell from same triggred spell
+ if(procSpell && procSpell->Id==26654)
+ return false;
+
+ target = SelectNearbyTarget();
+ if(!target)
+ return false;
+
+ triggered_spell_id = 26654;
+ break;
+ }
+ // Unstable Power
+ case 24658:
+ {
+ if (!procSpell || procSpell->Id == 24659)
+ return false;
+ // Need remove one 24659 aura
+ RemoveSingleAuraFromStack(24659, 0);
+ RemoveSingleAuraFromStack(24659, 1);
+ return true;
+ }
+ // Restless Strength
+ case 24661:
+ {
+ // Need remove one 24662 aura
+ RemoveSingleAuraFromStack(24662, 0);
+ return true;
+ }
+ // Adaptive Warding (Frostfire Regalia set)
+ case 28764:
+ {
+ if(!procSpell)
+ return false;
+
+ // find Mage Armor
+ bool found = false;
+ AuraList const& mRegenInterupt = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
+ for(AuraList::const_iterator iter = mRegenInterupt.begin(); iter != mRegenInterupt.end(); ++iter)
+ {
+ if(SpellEntry const* iterSpellProto = (*iter)->GetSpellProto())
+ {
+ if(iterSpellProto->SpellFamilyName==SPELLFAMILY_MAGE && (iterSpellProto->SpellFamilyFlags & 0x10000000))
+ {
+ found=true;
+ break;
+ }
+ }
+ }
+ if(!found)
+ return false;
+
+ switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
+ {
+ case SPELL_SCHOOL_NORMAL:
+ case SPELL_SCHOOL_HOLY:
+ return false; // ignored
+ case SPELL_SCHOOL_FIRE: triggered_spell_id = 28765; break;
+ case SPELL_SCHOOL_NATURE: triggered_spell_id = 28768; break;
+ case SPELL_SCHOOL_FROST: triggered_spell_id = 28766; break;
+ case SPELL_SCHOOL_SHADOW: triggered_spell_id = 28769; break;
+ case SPELL_SCHOOL_ARCANE: triggered_spell_id = 28770; break;
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ // Obsidian Armor (Justice Bearer`s Pauldrons shoulder)
+ case 27539:
+ {
+ if(!procSpell)
+ return false;
+
+ // not from DoT
+ bool found = false;
+ for(int j = 0; j < 3; ++j)
+ {
+ if(procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE||procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ return false;
+
+ switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
+ {
+ case SPELL_SCHOOL_NORMAL:
+ return false; // ignore
+ case SPELL_SCHOOL_HOLY: triggered_spell_id = 27536; break;
+ case SPELL_SCHOOL_FIRE: triggered_spell_id = 27533; break;
+ case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break;
+ case SPELL_SCHOOL_FROST: triggered_spell_id = 27534; break;
+ case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break;
+ case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break;
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ // Mana Leech (Passive) (Priest Pet Aura)
+ case 28305:
+ {
+ // Cast on owner
+ target = GetOwner();
+ if(!target)
+ return false;
+
+ basepoints0 = int32(damage * 2.5f); // manaregen
+ triggered_spell_id = 34650;
+ break;
+ }
+ // Mark of Malice
+ case 33493:
+ {
+ // Cast finish spell at last charge
+ if (triggeredByAura->m_procCharges > 1)
+ return false;
+
+ target = this;
+ triggered_spell_id = 33494;
+ break;
+ }
+ // Twisted Reflection (boss spell)
+ case 21063:
+ triggered_spell_id = 21064;
+ break;
+ // Vampiric Aura (boss spell)
+ case 38196:
+ {
+ basepoints0 = 3 * damage; // 300%
+ if (basepoints0 < 0)
+ return false;
+
+ triggered_spell_id = 31285;
+ target = this;
+ break;
+ }
+ // Aura of Madness (Darkmoon Card: Madness trinket)
+ //=====================================================
+ // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior)
+ // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid)
+ // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid)
+ // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes)
+ // 41005 Manic: +35 haste (spell, melee and ranged) (All classes)
+ // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter)
+ // 41011 Martyr Complex: +35 stamina (All classes)
+ // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ case 39446:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Select class defined buff
+ switch (getClass())
+ {
+ case CLASS_PALADIN: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
+ case CLASS_DRUID: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
+ {
+ uint32 RandomSpell[]={39511,40997,40998,40999,41002,41005,41009,41011,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_ROGUE: // 39511,40997,40998,41002,41005,41011
+ case CLASS_WARRIOR: // 39511,40997,40998,41002,41005,41011
+ {
+ uint32 RandomSpell[]={39511,40997,40998,41002,41005,41011};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_PRIEST: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_SHAMAN: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_MAGE: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_WARLOCK: // 40999,41002,41005,41009,41011,41406,41409
+ {
+ uint32 RandomSpell[]={40999,41002,41005,41009,41011,41406,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_HUNTER: // 40997,40999,41002,41005,41009,41011,41406,41409
+ {
+ uint32 RandomSpell[]={40997,40999,41002,41005,41009,41011,41406,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ default:
+ return false;
+ }
+
+ target = this;
+ if (roll_chance_i(10))
+ ((Player*)this)->Say("This is Madness!", LANG_UNIVERSAL);
+ break;
+ }
+ /*
+ // TODO: need find item for aura and triggered spells
+ // Sunwell Exalted Caster Neck (??? neck)
+ // cast ??? Light's Wrath if Exalted by Aldor
+ // cast ??? Arcane Bolt if Exalted by Scryers*/
+ case 46569:
+ return false; // disable for while
+ /*
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = ???
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = ???
+ break;
+ }
+ return false;
+ }/**/
+ // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck)
+ // cast 45479 Light's Wrath if Exalted by Aldor
+ // cast 45429 Arcane Bolt if Exalted by Scryers
+ case 45481:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45479;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45429;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck)
+ // cast 45480 Light's Strength if Exalted by Aldor
+ // cast 45428 Arcane Strike if Exalted by Scryers
+ case 45482:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45480;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45428;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck)
+ // cast 45431 Arcane Insight if Exalted by Aldor
+ // cast 45432 Light's Ward if Exalted by Scryers
+ case 45483:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45432;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45431;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck)
+ // cast 45478 Light's Salvation if Exalted by Aldor
+ // cast 45430 Arcane Surge if Exalted by Scryers
+ case 45484:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45478;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45430;
+ break;
+ }
+ return false;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Magic Absorption
+ if (dummySpell->SpellIconID == 459) // only this spell have SpellIconID == 459 and dummy aura
+ {
+ if (getPowerType() != POWER_MANA)
+ return false;
+
+ // mana reward
+ basepoints0 = (triggeredByAura->GetModifier()->m_amount * GetMaxPower(POWER_MANA) / 100);
+ target = this;
+ triggered_spell_id = 29442;
+ break;
+ }
+ // Master of Elements
+ if (dummySpell->SpellIconID == 1920)
+ {
+ if(!procSpell)
+ return false;
+
+ // mana cost save
+ basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
+ if( basepoints0 <=0 )
+ return false;
+
+ target = this;
+ triggered_spell_id = 29077;
+ break;
+ }
+ switch(dummySpell->Id)
+ {
+ // Ignite
+ case 11119:
+ case 11120:
+ case 12846:
+ case 12847:
+ case 12848:
+ {
+ switch (dummySpell->Id)
+ {
+ case 11119: basepoints0 = int32(0.04f*damage); break;
+ case 11120: basepoints0 = int32(0.08f*damage); break;
+ case 12846: basepoints0 = int32(0.12f*damage); break;
+ case 12847: basepoints0 = int32(0.16f*damage); break;
+ case 12848: basepoints0 = int32(0.20f*damage); break;
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (IG)",dummySpell->Id);
+ return false;
+ }
+
+ triggered_spell_id = 12654;
+ break;
+ }
+ // Combustion
+ case 11129:
+ {
+ //last charge and crit
+ if( triggeredByAura->m_procCharges <= 1 && (procFlag & PROC_FLAG_CRIT_SPELL) )
+ {
+ RemoveAurasDueToSpell(28682); //-> remove Combustion auras
+ return true; // charge counting (will removed)
+ }
+
+ CastSpell(this, 28682, true, castItem, triggeredByAura);
+ return(procFlag & PROC_FLAG_CRIT_SPELL);// charge update only at crit hits, no hidden cooldowns
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Retaliation
+ if(dummySpell->SpellFamilyFlags==0x0000000800000000LL)
+ {
+ // check attack comes not from behind
+ if (!HasInArc(M_PI, pVictim))
+ return false;
+
+ triggered_spell_id = 22858;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Seed of Corruption
+ if (dummySpell->SpellFamilyFlags & 0x0000001000000000LL)
+ {
+ Modifier* mod = triggeredByAura->GetModifier();
+ // if damage is more than need or target die from damage deal finish spell
+ // FIX ME: not triggered currently at death
+ if( mod->m_amount <= damage || GetHealth() <= damage )
+ {
+ // remember guid before aura delete
+ uint64 casterGuid = triggeredByAura->GetCasterGUID();
+
+ // Remove aura (before cast for prevent infinite loop handlers)
+ RemoveAurasDueToSpell(triggeredByAura->GetId());
+
+ // Cast finish spell (triggeredByAura already not exist!)
+ CastSpell(this, 27285, true, castItem, NULL, casterGuid);
+ return true; // no hidden cooldown
+ }
+
+ // Damage counting
+ mod->m_amount-=damage;
+ return true;
+ }
+ // Seed of Corruption (Mobs cast) - no die req
+ if (dummySpell->SpellFamilyFlags == 0x00LL && dummySpell->SpellIconID == 1932)
+ {
+ Modifier* mod = triggeredByAura->GetModifier();
+ // if damage is more than need deal finish spell
+ if( mod->m_amount <= damage )
+ {
+ // remember guid before aura delete
+ uint64 casterGuid = triggeredByAura->GetCasterGUID();
+
+ // Remove aura (before cast for prevent infinite loop handlers)
+ RemoveAurasDueToSpell(triggeredByAura->GetId());
+
+ // Cast finish spell (triggeredByAura already not exist!)
+ CastSpell(this, 32865, true, castItem, NULL, casterGuid);
+ return true; // no hidden cooldown
+ }
+ // Damage counting
+ mod->m_amount-=damage;
+ return true;
+ }
+ switch(dummySpell->Id)
+ {
+ // Nightfall
+ case 18094:
+ case 18095:
+ {
+ target = this;
+ triggered_spell_id = 17941;
+ break;
+ }
+ //Soul Leech
+ case 30293:
+ case 30295:
+ case 30296:
+ {
+ // health
+ basepoints0 = int32(damage*triggeredByAura->GetModifier()->m_amount/100);
+ target = this;
+ triggered_spell_id = 30294;
+ break;
+ }
+ // Shadowflame (Voidheart Raiment set bonus)
+ case 37377:
+ {
+ triggered_spell_id = 37379;
+ break;
+ }
+ // Pet Healing (Corruptor Raiment or Rift Stalker Armor)
+ case 37381:
+ {
+ target = GetPet();
+ if(!target)
+ return false;
+
+ // heal amount
+ basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100;
+ triggered_spell_id = 37382;
+ break;
+ }
+ // Shadowflame Hellfire (Voidheart Raiment set bonus)
+ case 39437:
+ {
+ triggered_spell_id = 37378;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Vampiric Touch
+ if( dummySpell->SpellFamilyFlags & 0x0000040000000000LL )
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // pVictim is caster of aura
+ if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
+ return false;
+
+ // energize amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ pVictim->CastCustomSpell(pVictim,34919,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ switch(dummySpell->Id)
+ {
+ // Vampiric Embrace
+ case 15286:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // pVictim is caster of aura
+ if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
+ return false;
+
+ // heal amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ pVictim->CastCustomSpell(pVictim,15290,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen)
+ case 40438:
+ {
+ // Shadow Word: Pain
+ if( procSpell->SpellFamilyFlags & 0x0000000000008000LL )
+ triggered_spell_id = 40441;
+ // Renew
+ else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
+ triggered_spell_id = 40440;
+ else
+ return false;
+
+ target = this;
+ break;
+ }
+ // Oracle Healing Bonus ("Garments of the Oracle" set)
+ case 26169:
+ {
+ // heal amount
+ basepoints0 = int32(damage * 10/100);
+ target = this;
+ triggered_spell_id = 26170;
+ break;
+ }
+ // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set
+ case 39372:
+ {
+ if(!procSpell || (GetSpellSchoolMask(procSpell) & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW))==0 )
+ return false;
+
+ // heal amount
+ basepoints0 = int32(damage * 2 / 100);
+ target = this;
+ triggered_spell_id = 39373;
+ break;
+ }
+ // Vestments of Faith (Priest Tier 3) - 4 pieces bonus
+ case 28809:
+ {
+ triggered_spell_id = 28810;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ switch(dummySpell->Id)
+ {
+ // Healing Touch (Dreamwalker Raiment set)
+ case 28719:
+ {
+ // mana back
+ basepoints0 = int32(procSpell->manaCost * 30 / 100);
+ target = this;
+ triggered_spell_id = 28742;
+ break;
+ }
+ // Healing Touch Refund (Idol of Longevity trinket)
+ case 28847:
+ {
+ target = this;
+ triggered_spell_id = 28848;
+ break;
+ }
+ // Mana Restore (Malorne Raiment set / Malorne Regalia set)
+ case 37288:
+ case 37295:
+ {
+ target = this;
+ triggered_spell_id = 37238;
+ break;
+ }
+ // Druid Tier 6 Trinket
+ case 40442:
+ {
+ float chance;
+
+ // Starfire
+ if( procSpell->SpellFamilyFlags & 0x0000000000000004LL )
+ {
+ triggered_spell_id = 40445;
+ chance = 25.f;
+ }
+ // Rejuvenation
+ else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
+ {
+ triggered_spell_id = 40446;
+ chance = 25.f;
+ }
+ // Mangle (cat/bear)
+ else if( procSpell->SpellFamilyFlags & 0x0000044000000000LL )
+ {
+ triggered_spell_id = 40452;
+ chance = 40.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ target = this;
+ break;
+ }
+ // Maim Interrupt
+ case 44835:
+ {
+ // Deadly Interrupt Effect
+ triggered_spell_id = 32747;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ switch(dummySpell->Id)
+ {
+ // Deadly Throw Interrupt
+ case 32748:
+ {
+ // Prevent cast Deadly Throw Interrupt on self from last effect (apply dummy) of Deadly Throw
+ if(this == pVictim)
+ return false;
+
+ triggered_spell_id = 32747;
+ break;
+ }
+ }
+ // Quick Recovery
+ if( dummySpell->SpellIconID == 2116 )
+ {
+ if(!procSpell)
+ return false;
+
+ // only rogue's finishing moves (maybe need additional checks)
+ if( procSpell->SpellFamilyName!=SPELLFAMILY_ROGUE ||
+ (procSpell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE) == 0)
+ return false;
+
+ // energy cost save
+ basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
+ if(basepoints0 <= 0)
+ return false;
+
+ target = this;
+ triggered_spell_id = 31663;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Thrill of the Hunt
+ if ( dummySpell->SpellIconID == 2236 )
+ {
+ if(!procSpell)
+ return false;
+
+ // mana cost save
+ basepoints0 = procSpell->manaCost * 40/100;
+ if(basepoints0 <= 0)
+ return false;
+
+ target = this;
+ triggered_spell_id = 34720;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Seal of Righteousness - melee proc dummy
+ if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggeredByAura->GetEffIndex()==0)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ uint32 spellId;
+ switch (triggeredByAura->GetId())
+ {
+ case 21084: spellId = 25742; break; // Rank 1
+ case 20287: spellId = 25740; break; // Rank 2
+ case 20288: spellId = 25739; break; // Rank 3
+ case 20289: spellId = 25738; break; // Rank 4
+ case 20290: spellId = 25737; break; // Rank 5
+ case 20291: spellId = 25736; break; // Rank 6
+ case 20292: spellId = 25735; break; // Rank 7
+ case 20293: spellId = 25713; break; // Rank 8
+ case 27155: spellId = 27156; break; // Rank 9
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled possibly SoR (Id = %u)", triggeredByAura->GetId());
+ return false;
+ }
+ Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+ float speed = (item ? item->GetProto()->Delay : BASE_ATTACK_TIME)/1000.0f;
+
+ float damageBasePoints;
+ if(item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ // two hand weapon
+ damageBasePoints=1.20f*triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f + 1;
+ else
+ // one hand weapon/no weapon
+ damageBasePoints=0.85f*ceil(triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) - 1;
+
+ int32 damagePoint = int32(damageBasePoints + 0.03f * (GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE)+GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE))/2.0f) + 1;
+
+ // apply damage bonuses manually
+ if(damagePoint >= 0)
+ damagePoint = SpellDamageBonus(pVictim, dummySpell, damagePoint, SPELL_DIRECT_DAMAGE);
+
+ CastCustomSpell(pVictim,spellId,&damagePoint,NULL,NULL,true,NULL, triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ // Seal of Blood do damage trigger
+ if(dummySpell->SpellFamilyFlags & 0x0000040000000000LL)
+ {
+ switch(triggeredByAura->GetEffIndex())
+ {
+ case 0:
+ // prevent chain triggering
+ if(procSpell && procSpell->Id==31893 )
+ return false;
+
+ triggered_spell_id = 31893;
+ break;
+ case 1:
+ {
+ // damage
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100;
+ target = this;
+ triggered_spell_id = 32221;
+ break;
+ }
+ }
+ }
+
+ switch(dummySpell->Id)
+ {
+ // Holy Power (Redemption Armor set)
+ case 28789:
+ {
+ if(!pVictim)
+ return false;
+
+ // Set class defined buff
+ switch (pVictim->getClass())
+ {
+ case CLASS_PALADIN:
+ case CLASS_PRIEST:
+ case CLASS_SHAMAN:
+ case CLASS_DRUID:
+ triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
+ break;
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
+ break;
+ case CLASS_HUNTER:
+ case CLASS_ROGUE:
+ triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d.
+ break;
+ case CLASS_WARRIOR:
+ triggered_spell_id = 28790; // Increases the friendly target's armor
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ //Seal of Vengeance
+ case 31801:
+ {
+ if(effIndex != 0) // effect 1,2 used by seal unleashing code
+ return false;
+
+ triggered_spell_id = 31803;
+ break;
+ }
+ // Spiritual Att.
+ case 31785:
+ case 33776:
+ {
+ // if healed by another unit (pVictim)
+ if(this == pVictim)
+ return false;
+
+ // heal amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ target = this;
+ triggered_spell_id = 31786;
+ break;
+ }
+ // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal)
+ case 40470:
+ {
+ if( !procSpell )
+ return false;
+
+ float chance;
+
+ // Flash of light/Holy light
+ if( procSpell->SpellFamilyFlags & 0x00000000C0000000LL)
+ {
+ triggered_spell_id = 40471;
+ chance = 15.f;
+ }
+ // Judgement
+ else if( procSpell->SpellFamilyFlags & 0x0000000000800000LL )
+ {
+ triggered_spell_id = 40472;
+ chance = 50.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ switch(dummySpell->Id)
+ {
+ // Totemic Power (The Earthshatterer set)
+ case 28823:
+ {
+ if( !pVictim )
+ return false;
+
+ // Set class defined buff
+ switch (pVictim->getClass())
+ {
+ case CLASS_PALADIN:
+ case CLASS_PRIEST:
+ case CLASS_SHAMAN:
+ case CLASS_DRUID:
+ triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
+ break;
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
+ break;
+ case CLASS_HUNTER:
+ case CLASS_ROGUE:
+ triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d.
+ break;
+ case CLASS_WARRIOR:
+ triggered_spell_id = 28827; // Increases the friendly target's armor
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ // Lesser Healing Wave (Totem of Flowing Water Relic)
+ case 28849:
+ {
+ target = this;
+ triggered_spell_id = 28850;
+ break;
+ }
+ // Windfury Weapon (Passive) 1-5 Ranks
+ case 33757:
+ {
+ if(GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ if(!castItem || !castItem->IsEquipped())
+ return false;
+
+ // custom cooldown processing case
+ if( cooldown && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
+ return false;
+
+ uint32 spellId;
+ switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)))
+ {
+ case 283: spellId = 33757; break; //1 Rank
+ case 284: spellId = 33756; break; //2 Rank
+ case 525: spellId = 33755; break; //3 Rank
+ case 1669:spellId = 33754; break; //4 Rank
+ case 2636:spellId = 33727; break; //5 Rank
+ default:
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)",
+ castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)),dummySpell->Id);
+ return false;
+ }
+ }
+
+ SpellEntry const* windfurySpellEntry = sSpellStore.LookupEntry(spellId);
+ if(!windfurySpellEntry)
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: non existed spell id: %u (Windfury)",spellId);
+ return false;
+ }
+
+ int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry,0,windfurySpellEntry->EffectBasePoints[0],pVictim);
+
+ // Off-Hand case
+ if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
+ {
+ // 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
+ {
+ // Value gained from additional AP
+ basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(BASE_ATTACK)/1000);
+ triggered_spell_id = 25504;
+ }
+
+ // apply cooldown before cast to prevent processing itself
+ if( cooldown )
+ ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
+
+ // Attack Twice
+ for ( uint32 i = 0; i<2; ++i )
+ CastCustomSpell(pVictim,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+
+ return true;
+ }
+ // Shaman Tier 6 Trinket
+ case 40463:
+ {
+ if( !procSpell )
+ return false;
+
+ float chance;
+ if (procSpell->SpellFamilyFlags & 0x0000000000000001LL)
+ {
+ triggered_spell_id = 40465; // Lightning Bolt
+ chance = 15.f;
+ }
+ else if (procSpell->SpellFamilyFlags & 0x0000000000000080LL)
+ {
+ triggered_spell_id = 40465; // Lesser Healing Wave
+ chance = 10.f;
+ }
+ else if (procSpell->SpellFamilyFlags & 0x0000001000000000LL)
+ {
+ triggered_spell_id = 40466; // Stormstrike
+ chance = 50.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ target = this;
+ break;
+ }
+ }
+
+ // Earth Shield
+ if(dummySpell->SpellFamilyFlags==0x40000000000LL)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // heal
+ basepoints0 = triggeredByAura->GetModifier()->m_amount;
+ target = this;
+ triggered_spell_id = 379;
+ break;
+ }
+ // Lightning Overload
+ if (dummySpell->SpellIconID == 2018) // only this spell have SpellFamily Shaman SpellIconID == 2018 and dummy aura
+ {
+ if(!procSpell || GetTypeId() != TYPEID_PLAYER || !pVictim )
+ return false;
+
+ // custom cooldown processing case
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
+ return false;
+
+ uint32 spellId = 0;
+ // Every Lightning Bolt and Chain Lightning spell have dublicate vs half damage and zero cost
+ switch (procSpell->Id)
+ {
+ // Lightning Bolt
+ case 403: spellId = 45284; break; // Rank 1
+ case 529: spellId = 45286; break; // Rank 2
+ case 548: spellId = 45287; break; // Rank 3
+ case 915: spellId = 45288; break; // Rank 4
+ case 943: spellId = 45289; break; // Rank 5
+ case 6041: spellId = 45290; break; // Rank 6
+ case 10391: spellId = 45291; break; // Rank 7
+ case 10392: spellId = 45292; break; // Rank 8
+ case 15207: spellId = 45293; break; // Rank 9
+ case 15208: spellId = 45294; break; // Rank 10
+ case 25448: spellId = 45295; break; // Rank 11
+ case 25449: spellId = 45296; break; // Rank 12
+ // Chain Lightning
+ case 421: spellId = 45297; break; // Rank 1
+ case 930: spellId = 45298; break; // Rank 2
+ case 2860: spellId = 45299; break; // Rank 3
+ case 10605: spellId = 45300; break; // Rank 4
+ case 25439: spellId = 45301; break; // Rank 5
+ case 25442: spellId = 45302; break; // Rank 6
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id);
+ return false;
+ }
+ // No thread generated mod
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_THREAT;
+ mod->value = -100;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = dummySpell->Id;
+ mod->effectId = 0;
+ mod->lastAffected = NULL;
+ mod->mask = 0x0000000000000003LL;
+ mod->charges = 0;
+ ((Player*)this)->AddSpellMod(mod, true);
+
+ // Remove cooldown (Chain Lightning - have Category Recovery time)
+ if (procSpell->SpellFamilyFlags & 0x0000000000000002LL)
+ ((Player*)this)->RemoveSpellCooldown(spellId);
+
+ // Hmmm.. in most case spells alredy set half basepoints but...
+ // Lightning Bolt (2-10 rank) have full basepoint and half bonus from level
+ // As on wiki:
+ // BUG: Rank 2 to 10 (and maybe 11) of Lightning Bolt will proc another Bolt with FULL damage (not halved). This bug is known and will probably be fixed soon.
+ // So - no add changes :)
+ CastSpell(pVictim, spellId, true, castItem, triggeredByAura);
+
+ ((Player*)this)->AddSpellMod(mod, false);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
+
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // processed charge only counting case
+ if(!triggered_spell_id)
+ return true;
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id);
+ return false;
+ }
+
+ // default case
+ if(!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
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attackType, uint32 cooldown)
+{
+ SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
+
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()];
+ Unit* target = !(procFlags & PROC_FLAG_HEAL) && IsPositiveSpell(triggered_spell_id) ? this : pVictim;
+ int32 basepoints0 = 0;
+
+ switch(auraSpellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Aegis of Preservation
+ case 23780:
+ //Aegis Heal (instead non-existed triggered spell)
+ triggered_spell_id = 23781;
+ target = this;
+ break;
+ // Elune's Touch (moonkin mana restore)
+ case 24905:
+ {
+ // Elune's Touch (instead non-existed triggered spell)
+ triggered_spell_id = 33926;
+ basepoints0 = int32(0.3f * GetTotalAttackPowerValue(BASE_ATTACK));
+ target = this;
+ break;
+ }
+ // Enlightenment
+ case 29601:
+ {
+ // only for cast with mana price
+ if(!procSpell || procSpell->powerType!=POWER_MANA || procSpell->manaCost==0 && procSpell->ManaCostPercentage==0 && procSpell->manaCostPerlevel==0)
+ return false;
+ break; // fall through to normal cast
+ }
+ // Health Restore
+ case 33510:
+ {
+ // at melee hit call std triggered spell
+ if(procFlags & PROC_FLAG_HIT_MELEE)
+ break; // fall through to normal cast
+
+ // Mark of Conquest - else (at range hit) called custom case
+ triggered_spell_id = 39557;
+ target = this;
+ break;
+ }
+ // Shaleskin
+ case 36576:
+ return true; // nothing to do
+ // Forgotten Knowledge (Blade of Wizardry)
+ case 38319:
+ // only for harmful enemy targeted spell
+ if(!pVictim || pVictim==this || !procSpell || IsPositiveSpell(procSpell->Id))
+ return false;
+ break; // fall through to normal cast
+ // Aura of Wrath (Darkmoon Card: Wrath trinket bonus)
+ case 39442:
+ {
+ // proc only at non-crit hits
+ if(procFlags & (PROC_FLAG_CRIT_MELEE|PROC_FLAG_CRIT_RANGED|PROC_FLAG_CRIT_SPELL))
+ return false;
+ break; // fall through to normal cast
+ }
+ // Augment Pain (Timbal's Focusing Crystal trinket bonus)
+ case 45054:
+ {
+ if(!procSpell)
+ return false;
+
+ //only periodic damage can trigger spell
+ bool found = false;
+ for(int j = 0; j < 3; ++j)
+ {
+ if( procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE ||
+ procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT ||
+ procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_LEECH )
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return false;
+
+ break; // fall through to normal cast
+ }
+ // Evasive Maneuvers (Commendation of Kael'thas)
+ case 45057:
+ {
+ // damage taken that reduces below 35% health
+ // does NOT mean you must have been >= 35% before
+ if (int32(GetHealth())-int32(damage) >= int32(GetMaxHealth()*0.35f))
+ return false;
+ break; // fall through to normal cast
+ }
+ }
+
+ switch(triggered_spell_id)
+ {
+ // Setup
+ case 15250:
+ {
+ // applied only for main target
+ if(!pVictim || pVictim != getVictim())
+ return false;
+
+ // continue normal case
+ break;
+ }
+ // Shamanistic Rage triggered spell
+ case 30824:
+ basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK)*triggeredByAura->GetModifier()->m_amount/100);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ // Blazing Speed
+ case 2127:
+ //Blazing Speed (instead non-existed triggered spell)
+ triggered_spell_id = 31643;
+ target = this;
+ break;
+ }
+ switch(auraSpellInfo->Id)
+ {
+ // Persistent Shield (Scarab Brooch)
+ case 26467:
+ basepoints0 = int32(damage * 0.15f);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ //Rampage
+ if((auraSpellInfo->SpellFamilyFlags & 0x100000) && auraSpellInfo->SpellIconID==2006)
+ {
+ //all ranks have effect[0]==AURA (Proc Trigger Spell, non-existed)
+ //and effect[1]==TriggerSpell
+ if(auraSpellInfo->Effect[1]!=SPELL_EFFECT_TRIGGER_SPELL)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have wrong effect in RM",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ triggered_spell_id = auraSpellInfo->EffectTriggerSpell[1];
+ break; // fall through to normal cast
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Pyroclasm
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000 && auraSpellInfo->SpellIconID==1137)
+ {
+ // last case for Hellfire that damage caster also but don't must stun caster
+ if( pVictim == this )
+ return false;
+
+ // custom chnace
+ float chance = 0;
+ switch (triggeredByAura->GetId())
+ {
+ case 18096: chance = 13.0f; break;
+ case 18073: chance = 26.0f; break;
+ }
+ if (!roll_chance_f(chance))
+ return false;
+
+ // Pyroclasm (instead non-existed triggered spell)
+ triggered_spell_id = 18093;
+ target = pVictim;
+ break;
+ }
+ // Drain Soul
+ if(auraSpellInfo->SpellFamilyFlags & 0x0000000000004000)
+ {
+ bool found = false;
+ Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
+ for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i)
+ {
+ //Improved Drain Soul
+ if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113)
+ {
+ int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this);
+ basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100;
+
+ // Drain Soul
+ triggered_spell_id = 18371;
+ target = this;
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return false;
+ break; // fall through to normal cast
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ //Blessed Recovery
+ if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL && auraSpellInfo->SpellIconID==1875)
+ {
+ switch (triggeredByAura->GetSpellProto()->Id)
+ {
+ case 27811: triggered_spell_id = 27813; break;
+ case 27815: triggered_spell_id = 27817; break;
+ case 27816: triggered_spell_id = 27818; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ int32 heal_amount = damage * triggeredByAura->GetModifier()->m_amount / 100;
+ basepoints0 = heal_amount/3;
+ target = this;
+ break;
+ }
+ // Shadowguard
+ if((auraSpellInfo->SpellFamilyFlags & 0x80000000LL) && auraSpellInfo->SpellVisual==7958)
+ {
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 18137:
+ triggered_spell_id = 28377; break; // Rank 1
+ case 19308:
+ triggered_spell_id = 28378; break; // Rank 2
+ case 19309:
+ triggered_spell_id = 28379; break; // Rank 3
+ case 19310:
+ triggered_spell_id = 28380; break; // Rank 4
+ case 19311:
+ triggered_spell_id = 28381; break; // Rank 5
+ case 19312:
+ triggered_spell_id = 28382; break; // Rank 6
+ case 25477:
+ triggered_spell_id = 28385; break; // Rank 7
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ target = pVictim;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Leader of the Pack (triggering Improved Leader of the Pack heal)
+ case 24932:
+ {
+ if (triggeredByAura->GetModifier()->m_amount == 0)
+ return false;
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
+ triggered_spell_id = 34299;
+ break;
+ };
+ // Druid Forms Trinket (Druid Tier5 Trinket, triggers different spells per Form)
+ case 37336:
+ {
+ switch(m_form)
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ triggered_spell_id=37340; break;// Ursine Blessing
+ case FORM_CAT:
+ triggered_spell_id=37341; break;// Feline Blessing
+ case FORM_TREE:
+ triggered_spell_id=37342; break;// Slyvan Blessing
+ case FORM_MOONKIN:
+ triggered_spell_id=37343; break;// Lunar Blessing
+ case FORM_NONE:
+ triggered_spell_id=37344; break;// Cenarion Blessing (for caster form, except FORM_MOONKIN)
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000LL)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ // Combat Potency
+ case 2260:
+ {
+ // skip non offhand attacks
+ if(attackType!=OFF_ATTACK)
+ return false;
+ break; // fall through to normal cast
+ }
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL)
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Lightning Capacitor
+ case 37657:
+ {
+ // trinket ProcTriggerSpell but for safe checks for player
+ if(!castItem || !pVictim || !pVictim->isAlive() || GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ if(((Player*)this)->HasSpellCooldown(37657))
+ return false;
+
+ // stacking
+ CastSpell(this, 37658, true, castItem, triggeredByAura);
+ // 2.5s cooldown before it can stack again, current system allow 1 sec step in cooldown
+ ((Player*)this)->AddSpellCooldown(37657,0,time(NULL)+(roll_chance_i(50) ? 2 : 3));
+
+ // counting
+ uint32 count = 0;
+ AuraList const& dummyAura = GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = dummyAura.begin(); itr != dummyAura.end(); ++itr)
+ if((*itr)->GetId()==37658)
+ ++count;
+
+ // release at 3 aura in stack
+ if(count <= 2)
+ return true; // main triggered spell casted anyway
+
+ RemoveAurasDueToSpell(37658);
+ CastSpell(pVictim, 37661, true, castItem, triggeredByAura);
+ return true;
+ }
+ // Healing Discount
+ case 37705:
+ // Healing Trance (instead non-existed triggered spell)
+ triggered_spell_id = 37706;
+ target = this;
+ break;
+ // HoTs on Heals (Fel Reaver's Piston trinket)
+ case 38299:
+ {
+ // at direct heal effect
+ if(!procSpell || !IsSpellHaveEffect(procSpell,SPELL_EFFECT_HEAL))
+ return false;
+
+ // single proc at time
+ AuraList const& scAuras = GetSingleCastAuras();
+ for(AuraList::const_iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
+ if((*itr)->GetId()==triggered_spell_id)
+ return false;
+
+ // positive cast at victim instead self
+ target = pVictim;
+ break;
+ }
+ }
+ switch(auraSpellInfo->SpellIconID)
+ {
+ case 241:
+ {
+ switch(auraSpellInfo->EffectTriggerSpell[0])
+ {
+ //Illumination
+ case 18350:
+ {
+ if(!procSpell)
+ return false;
+
+ // procspell is triggered spell but we need mana cost of original casted spell
+ uint32 originalSpellId = procSpell->Id;
+
+ // Holy Shock
+ if(procSpell->SpellFamilyName == SPELLFAMILY_PALADIN)
+ {
+ if(procSpell->SpellFamilyFlags & 0x0001000000000000LL)
+ {
+ switch(procSpell->Id)
+ {
+ case 25914: originalSpellId = 20473; break;
+ case 25913: originalSpellId = 20929; break;
+ case 25903: originalSpellId = 20930; break;
+ case 27175: originalSpellId = 27174; break;
+ case 33074: originalSpellId = 33072; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id);
+ return false;
+ }
+ }
+ }
+
+ SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId);
+ if(!originalSpell)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId);
+ return false;
+ }
+
+ // percent stored in effect 1 (class scripts) base points
+ int32 percent = auraSpellInfo->EffectBasePoints[1]+1;
+
+ basepoints0 = originalSpell->manaCost*percent/100;
+ triggered_spell_id = 20272;
+ target = this;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if(auraSpellInfo->SpellFamilyFlags & 0x00080000)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ //Judgement of Wisdom (overwrite non existing triggered spell call in spell.dbc
+ case 206:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ uint32 spell = 0;
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 20186:
+ triggered_spell_id = 20268; // Rank 1
+ break;
+ case 20354:
+ triggered_spell_id = 20352; // Rank 2
+ break;
+ case 20355:
+ triggered_spell_id = 20353; // Rank 3
+ break;
+ case 27164:
+ triggered_spell_id = 27165; // Rank 4
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoW",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
+ return true; // no hidden cooldown
+ }
+ //Judgement of Light
+ case 299:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // overwrite non existing triggered spell call in spell.dbc
+ uint32 spell = 0;
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 20185:
+ triggered_spell_id = 20267; // Rank 1
+ break;
+ case 20344:
+ triggered_spell_id = 20341; // Rank 2
+ break;
+ case 20345:
+ triggered_spell_id = 20342; // Rank 3
+ break;
+ case 20346:
+ triggered_spell_id = 20343; // Rank 4
+ break;
+ case 27162:
+ triggered_spell_id = 27163; // Rank 5
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoL",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
+ return true; // no hidden cooldown
+ }
+ }
+ }
+ // custom check for proc spell
+ switch(auraSpellInfo->Id)
+ {
+ // Bonus Healing (item spell)
+ case 40971:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // bonus if health < 50%
+ if(pVictim->GetHealth() >= pVictim->GetMaxHealth()*triggeredByAura->GetModifier()->m_amount/100)
+ return false;
+
+ // cast at target positive spell
+ target = pVictim;
+ break;
+ }
+ }
+ switch(triggered_spell_id)
+ {
+ // Seal of Command
+ case 20424:
+ // prevent chain of triggered spell from same triggered spell
+ if(procSpell && procSpell->Id==20424)
+ return false;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ case 19:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ case 23551: // Lightning Shield - Tier2: 8 pieces proc shield
+ {
+ // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
+ triggered_spell_id = 23552;
+ target = pVictim;
+ break;
+ }
+ case 23552: // Lightning Shield - trigger shield damage
+ {
+ // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
+ triggered_spell_id = 27635;
+ target = pVictim;
+ break;
+ }
+ }
+ break;
+ }
+ // Mana Surge (Shaman T1 bonus)
+ case 87:
+ {
+ if(!procSpell)
+ return false;
+
+ basepoints0 = procSpell->manaCost * 35/100;
+ triggered_spell_id = 23571;
+ target = this;
+ break;
+ }
+ //Nature's Guardian
+ case 2013:
+ {
+ if(GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ // damage taken that reduces below 30% health
+ // does NOT mean you must have been >= 30% before
+ if (10*(int32(GetHealth())-int32(damage)) >= 3*GetMaxHealth())
+ return false;
+
+ triggered_spell_id = 31616;
+
+ // need check cooldown now
+ if( cooldown && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
+ target = this;
+ if(pVictim && pVictim->isAlive())
+ pVictim->getThreatManager().modifyThreatPercent(this,-10);
+ break;
+ }
+ }
+ }
+
+ // Water Shield (we can't set cooldown for main spell - it's player casted spell
+ if((auraSpellInfo->SpellFamilyFlags & 0x0000002000000000LL) && auraSpellInfo->SpellVisual==7358)
+ {
+ target = this;
+ break;
+ }
+
+ // Lightning Shield
+ if((auraSpellInfo->SpellFamilyFlags & 0x00000400) && auraSpellInfo->SpellVisual==37)
+ {
+ // overwrite non existing triggered spell call in spell.dbc
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 324:
+ triggered_spell_id = 26364; break; // Rank 1
+ case 325:
+ triggered_spell_id = 26365; break; // Rank 2
+ case 905:
+ triggered_spell_id = 26366; break; // Rank 3
+ case 945:
+ triggered_spell_id = 26367; break; // Rank 4
+ case 8134:
+ triggered_spell_id = 26369; break; // Rank 5
+ case 10431:
+ triggered_spell_id = 26370; break; // Rank 6
+ case 10432:
+ triggered_spell_id = 26363; break; // Rank 7
+ case 25469:
+ triggered_spell_id = 26371; break; // Rank 8
+ case 25472:
+ triggered_spell_id = 26372; break; // Rank 9
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ target = pVictim;
+ break;
+ }
+ break;
+ }
+ }
+
+ // standard non-dummy case
+ if(!triggered_spell_id)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex());
+ return false;
+ }
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have not existed EffectTriggered[%d]=%u, not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex(),triggered_spell_id);
+ return false;
+ }
+
+ // not allow proc extra attack spell at extra attack
+ if( m_extraAttacks && IsSpellHaveEffect(triggerEntry,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
+ return false;
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ // default case
+ if(!target || target!=this && !target->isAlive())
+ return false;
+
+ if(basepoints0)
+ CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ else
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
+{
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+
+ switch(scriptId)
+ {
+ case 836: // Improved Blizzard (Rank 1)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12484;
+ break;
+ }
+ case 988: // Improved Blizzard (Rank 2)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12485;
+ break;
+ }
+ case 989: // Improved Blizzard (Rank 3)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12486;
+ break;
+ }
+ case 4086: // Improved Mend Pet (Rank 1)
+ case 4087: // Improved Mend Pet (Rank 2)
+ {
+ int32 chance = triggeredByAura->GetSpellProto()->EffectBasePoints[triggeredByAura->GetEffIndex()];
+ if(!roll_chance_i(chance))
+ return false;
+
+ triggered_spell_id = 24406;
+ break;
+ }
+ case 4533: // Dreamwalker Raiment 2 pieces bonus
+ {
+ // Chance 50%
+ if (!roll_chance_i(50))
+ return false;
+
+ switch (pVictim->getPowerType())
+ {
+ case POWER_MANA: triggered_spell_id = 28722; break;
+ case POWER_RAGE: triggered_spell_id = 28723; break;
+ case POWER_ENERGY: triggered_spell_id = 28724; break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case 4537: // Dreamwalker Raiment 6 pieces bonus
+ triggered_spell_id = 28750; // Blessing of the Claw
+ break;
+ case 5497: // Improved Mana Gems (Serpent-Coil Braid)
+ triggered_spell_id = 37445; // Mana Surge
+ break;
+ }
+
+ // not processed
+ if(!triggered_spell_id)
+ return false;
+
+ // standard non-dummy case
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u",triggered_spell_id,scriptId);
+ return false;
+ }
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ CastSpell(pVictim, triggered_spell_id, true, castItem, triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+void Unit::setPowerType(Powers new_powertype)
+{
+ SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POWER_TYPE);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_POWER_TYPE);
+ }
+ }
+
+ switch(new_powertype)
+ {
+ default:
+ case POWER_MANA:
+ break;
+ case POWER_RAGE:
+ SetMaxPower(POWER_RAGE,GetCreatePowers(POWER_RAGE));
+ SetPower( POWER_RAGE,0);
+ break;
+ case POWER_FOCUS:
+ SetMaxPower(POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
+ SetPower( POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
+ break;
+ case POWER_ENERGY:
+ SetMaxPower(POWER_ENERGY,GetCreatePowers(POWER_ENERGY));
+ SetPower( POWER_ENERGY,0);
+ break;
+ case POWER_HAPPINESS:
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ break;
+ }
+}
+
+FactionTemplateEntry const* Unit::getFactionTemplateEntry() const
+{
+ FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(getFaction());
+ if(!entry)
+ {
+ static uint64 guid = 0; // prevent repeating spam same faction problem
+
+ if(GetGUID() != guid)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ sLog.outError("Player %s have invalid faction (faction template id) #%u", ((Player*)this)->GetName(), getFaction());
+ else
+ sLog.outError("Creature (template id: %u) have invalid faction (faction template id) #%u", ((Creature*)this)->GetCreatureInfo()->Entry, getFaction());
+ guid = GetGUID();
+ }
+ }
+ return entry;
+}
+
+bool Unit::IsHostileTo(Unit const* unit) const
+{
+ // always non-hostile to self
+ if(unit==this)
+ return false;
+
+ // always non-hostile to GM in GM mode
+ if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
+ return false;
+
+ // always hostile to enemy
+ if(getVictim()==unit || unit->getVictim()==this)
+ return true;
+
+ // test pet/charm masters instead pers/charmeds
+ Unit const* testerOwner = GetCharmerOrOwner();
+ Unit const* targetOwner = unit->GetCharmerOrOwner();
+
+ // always hostile to owner's enemy
+ if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
+ return true;
+
+ // always hostile to enemy owner
+ if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
+ return true;
+
+ // always hostile to owner of owner's enemy
+ if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
+ return true;
+
+ Unit const* tester = testerOwner ? testerOwner : this;
+ Unit const* target = targetOwner ? targetOwner : unit;
+
+ // always non-hostile to target with common owner, or to owner/pet
+ if(tester==target)
+ return false;
+
+ // special cases (Duel, etc)
+ if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player const* pTester = (Player const*)tester;
+ Player const* pTarget = (Player const*)target;
+
+ // Duel
+ if(pTester->duel && pTester->duel->opponent == pTarget && pTester->duel->startTime != 0)
+ return true;
+
+ // Group
+ if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
+ return false;
+
+ // Sanctuary
+ if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
+ return false;
+
+ // PvP FFA state
+ if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
+ return true;
+
+ //= PvP states
+ // Green/Blue (can't attack)
+ if(pTester->GetTeam()==pTarget->GetTeam())
+ return false;
+
+ // Red (can attack) if true, Blue/Yellow (can't attack) in another case
+ return pTester->IsPvP() && pTarget->IsPvP();
+ }
+
+ // faction base cases
+ FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
+ FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
+ if(!tester_faction || !target_faction)
+ return false;
+
+ if(target->isAttackingPlayer() && tester->IsContestedGuard())
+ return true;
+
+ // PvC forced reaction and reputation case
+ if(tester->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction);
+ if(forceItr!=((Player*)tester)->m_forcedReactions.end())
+ return forceItr->second <= REP_HOSTILE;
+
+ // if faction have reputation then hostile state for tester at 100% dependent from at_war state
+ if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
+ if(raw_target_faction->reputationListID >=0)
+ if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction))
+ return (factionState->Flags & FACTION_FLAG_AT_WAR);
+ }
+ // CvP forced reaction and reputation case
+ else if(target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
+ if(forceItr!=((Player const*)target)->m_forcedReactions.end())
+ return forceItr->second <= REP_HOSTILE;
+
+ // apply reputation state
+ FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction);
+ if(raw_tester_faction && raw_tester_faction->reputationListID >=0 )
+ return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE;
+ }
+
+ // common faction based case (CvC,PvC,CvP)
+ return tester_faction->IsHostileTo(*target_faction);
+}
+
+bool Unit::IsFriendlyTo(Unit const* unit) const
+{
+ // always friendly to self
+ if(unit==this)
+ return true;
+
+ // always friendly to GM in GM mode
+ if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
+ return true;
+
+ // always non-friendly to enemy
+ if(getVictim()==unit || unit->getVictim()==this)
+ return false;
+
+ // test pet/charm masters instead pers/charmeds
+ Unit const* testerOwner = GetCharmerOrOwner();
+ Unit const* targetOwner = unit->GetCharmerOrOwner();
+
+ // always non-friendly to owner's enemy
+ if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
+ return false;
+
+ // always non-friendly to enemy owner
+ if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
+ return false;
+
+ // always non-friendly to owner of owner's enemy
+ if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
+ return false;
+
+ Unit const* tester = testerOwner ? testerOwner : this;
+ Unit const* target = targetOwner ? targetOwner : unit;
+
+ // always friendly to target with common owner, or to owner/pet
+ if(tester==target)
+ return true;
+
+ // special cases (Duel)
+ if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player const* pTester = (Player const*)tester;
+ Player const* pTarget = (Player const*)target;
+
+ // Duel
+ if(pTester->duel && pTester->duel->opponent == target && pTester->duel->startTime != 0)
+ return false;
+
+ // Group
+ if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
+ return true;
+
+ // Sanctuary
+ if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
+ return true;
+
+ // PvP FFA state
+ if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
+ return false;
+
+ //= PvP states
+ // Green/Blue (non-attackable)
+ if(pTester->GetTeam()==pTarget->GetTeam())
+ return true;
+
+ // Blue (friendly/non-attackable) if not PVP, or Yellow/Red in another case (attackable)
+ return !pTarget->IsPvP();
+ }
+
+ // faction base cases
+ FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
+ FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
+ if(!tester_faction || !target_faction)
+ return false;
+
+ if(target->isAttackingPlayer() && tester->IsContestedGuard())
+ return false;
+
+ // PvC forced reaction and reputation case
+ if(tester->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction);
+ if(forceItr!=((Player const*)tester)->m_forcedReactions.end())
+ return forceItr->second >= REP_FRIENDLY;
+
+ // if faction have reputation then friendly state for tester at 100% dependent from at_war state
+ if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
+ if(raw_target_faction->reputationListID >=0)
+ if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction))
+ return !(FactionState->Flags & FACTION_FLAG_AT_WAR);
+ }
+ // CvP forced reaction and reputation case
+ else if(target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
+ if(forceItr!=((Player const*)target)->m_forcedReactions.end())
+ return forceItr->second >= REP_FRIENDLY;
+
+ // apply reputation state
+ if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction))
+ if(raw_tester_faction->reputationListID >=0 )
+ return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY;
+ }
+
+ // common faction based case (CvC,PvC,CvP)
+ return tester_faction->IsFriendlyTo(*target_faction);
+}
+
+bool Unit::IsHostileToPlayers() const
+{
+ FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
+ if(!my_faction)
+ return false;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
+ if(raw_faction && raw_faction->reputationListID >=0 )
+ return false;
+
+ return my_faction->IsHostileToPlayers();
+}
+
+bool Unit::IsNeutralToAll() const
+{
+ FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
+ if(!my_faction)
+ return true;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
+ if(raw_faction && raw_faction->reputationListID >=0 )
+ return false;
+
+ return my_faction->IsNeutralToAll();
+}
+
+bool Unit::Attack(Unit *victim, bool meleeAttack)
+{
+ if(!victim || victim == this)
+ return false;
+
+ // dead units can neither attack nor be attacked
+ if(!isAlive() || !victim->isAlive())
+ return false;
+
+ // player cannot attack in mount state
+ if(GetTypeId()==TYPEID_PLAYER && IsMounted())
+ return false;
+
+ // nobody can attack GM in GM-mode
+ if(victim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)victim)->isGameMaster())
+ return false;
+ }
+ else
+ {
+ if(((Creature*)victim)->IsInEvadeMode())
+ return false;
+ }
+
+ // remove SPELL_AURA_MOD_UNATTACKABLE at attack (in case non-interruptible spells stun aura applied also that not let attack)
+ if(HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_UNATTACKABLE);
+
+ if (m_attacking)
+ {
+ if (m_attacking == victim)
+ {
+ // switch to melee attack from ranged/magic
+ if( meleeAttack && !hasUnitState(UNIT_STAT_MELEE_ATTACKING) )
+ {
+ addUnitState(UNIT_STAT_MELEE_ATTACKING);
+ SendAttackStart(victim);
+ return true;
+ }
+ return false;
+ }
+ AttackStop();
+ }
+
+ //Set our target
+ SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID());
+
+ if(meleeAttack)
+ addUnitState(UNIT_STAT_MELEE_ATTACKING);
+ m_attacking = victim;
+ m_attacking->_addAttacker(this);
+
+ if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->AI())
+ ((Creature*)m_attacking)->AI()->AttackedBy(this);
+
+ if(GetTypeId()==TYPEID_UNIT)
+ {
+ WorldPacket data(SMSG_AI_REACTION, 12);
+ data << GetGUID();
+ data << uint32(AI_REACTION_AGGRO); // Aggro sound
+ ((WorldObject*)this)->SendMessageToSet(&data, true);
+
+ ((Creature*)this)->CallAssistence();
+ ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
+ }
+
+ // delay offhand weapon attack to next attack time
+ if(haveOffhandWeapon())
+ resetAttackTimer(OFF_ATTACK);
+
+ if(meleeAttack)
+ SendAttackStart(victim);
+
+ return true;
+}
+
+bool Unit::AttackStop()
+{
+ if (!m_attacking)
+ return false;
+
+ Unit* victim = m_attacking;
+
+ m_attacking->_removeAttacker(this);
+ m_attacking = NULL;
+
+ //Clear our target
+ SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
+ clearUnitState(UNIT_STAT_MELEE_ATTACKING);
+
+ InterruptSpell(CURRENT_MELEE_SPELL);
+
+ if( GetTypeId()==TYPEID_UNIT )
+ {
+ // reset call assistance
+ ((Creature*)this)->SetNoCallAssistence(false);
+ }
+
+ SendAttackStop(victim);
+
+ return true;
+}
+
+void Unit::CombatStop(bool cast)
+{
+ if(cast& IsNonMeleeSpellCasted(false))
+ InterruptNonMeleeSpells(false);
+
+ AttackStop();
+ RemoveAllAttackers();
+ if( GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
+ ClearInCombat();
+}
+
+void Unit::CombatStopWithPets(bool cast)
+{
+ CombatStop(cast);
+ if(Pet* pet = GetPet())
+ pet->CombatStop(cast);
+ if(Unit* charm = GetCharm())
+ charm->CombatStop(cast);
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ GuardianPetList const& guardians = ((Player*)this)->GetGuardians();
+ for(GuardianPetList::const_iterator itr = guardians.begin(); itr != guardians.end(); ++itr)
+ if(Unit* guardian = Unit::GetUnit(*this,*itr))
+ guardian->CombatStop(cast);
+ }
+}
+
+bool Unit::isAttackingPlayer() const
+{
+ if(hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ return true;
+
+ Pet* pet = GetPet();
+ if(pet && pet->isAttackingPlayer())
+ return true;
+
+ Unit* charmed = GetCharm();
+ if(charmed && charmed->isAttackingPlayer())
+ return true;
+
+ for (int8 i = 0; i < MAX_TOTEM; i++)
+ {
+ if(m_TotemSlot[i])
+ {
+ Creature *totem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
+ if(totem && totem->isAttackingPlayer())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Unit::RemoveAllAttackers()
+{
+ while (!m_attackers.empty())
+ {
+ AttackerSet::iterator iter = m_attackers.begin();
+ if(!(*iter)->AttackStop())
+ {
+ sLog.outError("WORLD: Unit has an attacker that isnt attacking it!");
+ m_attackers.erase(iter);
+ }
+ }
+}
+
+void Unit::ModifyAuraState(AuraState flag, bool apply)
+{
+ if (apply)
+ {
+ if (!HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)))
+ {
+ SetFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ const PlayerSpellMap& sp_list = ((Player*)this)->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 || !IsPassiveSpell(itr->first)) continue;
+ if (spellInfo->CasterAuraState == flag)
+ CastSpell(this, itr->first, true, NULL);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (HasFlag(UNIT_FIELD_AURASTATE,1<<(flag-1)))
+ {
+ RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
+ Unit::AuraMap& tAuras = GetAuras();
+ for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
+ {
+ 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==0x100000)
+ {
+ ++itr;
+ continue;
+ }
+
+ RemoveAura(itr);
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+}
+
+Unit *Unit::GetOwner() const
+{
+ uint64 ownerid = GetOwnerGUID();
+ if(!ownerid)
+ return NULL;
+ return ObjectAccessor::GetUnit(*this, ownerid);
+}
+
+Unit *Unit::GetCharmer() const
+{
+ if(uint64 charmerid = GetCharmerGUID())
+ return ObjectAccessor::GetUnit(*this, charmerid);
+ return NULL;
+}
+
+Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself()
+{
+ uint64 guid = GetCharmerOrOwnerGUID();
+ if(IS_PLAYER_GUID(guid))
+ return ObjectAccessor::GetPlayer(*this, guid);
+
+ return GetTypeId()==TYPEID_PLAYER ? (Player*)this : NULL;
+}
+
+Pet* Unit::GetPet() const
+{
+ if(uint64 pet_guid = GetPetGUID())
+ {
+ if(Pet* pet = ObjectAccessor::GetPet(pet_guid))
+ return pet;
+
+ sLog.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid));
+ const_cast<Unit*>(this)->SetPet(0);
+ }
+
+ return NULL;
+}
+
+Unit* Unit::GetCharm() const
+{
+ if(uint64 charm_guid = GetCharmGUID())
+ {
+ if(Unit* pet = ObjectAccessor::GetUnit(*this, charm_guid))
+ return pet;
+
+ sLog.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid));
+ const_cast<Unit*>(this)->SetCharm(0);
+ }
+
+ return NULL;
+}
+
+void Unit::SetPet(Pet* pet)
+{
+ SetUInt64Value(UNIT_FIELD_SUMMON,pet ? pet->GetGUID() : 0);
+
+ // FIXME: hack, speed must be set only at follow
+ if(pet)
+ for(int i = 0; i < MAX_MOVE_TYPE; ++i)
+ pet->SetSpeed(UnitMoveType(i),m_speed_rate[i],true);
+}
+
+void Unit::SetCharm(Unit* charmed)
+{
+ SetUInt64Value(UNIT_FIELD_CHARM,charmed ? charmed->GetGUID() : 0);
+}
+
+void Unit::UnsummonAllTotems()
+{
+ for (int8 i = 0; i < MAX_TOTEM; ++i)
+ {
+ if(!m_TotemSlot[i])
+ continue;
+
+ Creature *OldTotem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
+ if (OldTotem && OldTotem->isTotem())
+ ((Totem*)OldTotem)->UnSummon();
+ }
+}
+
+void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical)
+{
+ // we guess size
+ WorldPacket data(SMSG_SPELLHEALLOG, (8+8+4+4+1));
+ data.append(pVictim->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(Damage);
+ data << uint8(critical ? 1 : 0);
+ data << uint8(0); // unused in client?
+ SendMessageToSet(&data, true);
+}
+
+void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype, bool critical)
+{
+ WorldPacket data(SMSG_SPELLENERGIZELOG, (8+8+4+4+4+1));
+ data.append(pVictim->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(powertype);
+ data << uint32(Damage);
+ //data << uint8(critical ? 1 : 0); // removed in 2.4.0
+ SendMessageToSet(&data, true);
+}
+
+uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype)
+{
+ if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE )
+ return pdamage;
+
+ int32 BonusDamage = 0;
+ if( GetTypeId()==TYPEID_UNIT )
+ {
+ // Pets just add their bonus damage to their spell damage
+ // note that their spell damage is just gain of their own auras
+ if (((Creature*)this)->isPet())
+ {
+ BonusDamage = ((Pet*)this)->GetBonusDamage();
+ }
+ // For totems get damage bonus from owner (statue isn't totem in fact)
+ else if (((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
+ {
+ if(Unit* owner = GetOwner())
+ return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype);
+ }
+ }
+
+ // Damage Done
+ uint32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto);
+
+ // Taken/Done fixed damage bonus auras
+ int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto))+BonusDamage;
+ int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
+
+ // Damage over Time spells bonus calculation
+ float DotFactor = 1.0f;
+ if(damagetype == DOT)
+ {
+ int32 DotDuration = GetSpellDuration(spellProto);
+ // 200% limit
+ if(DotDuration > 0)
+ {
+ if(DotDuration > 30000) DotDuration = 30000;
+ if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
+ int x = 0;
+ for(int j = 0; j < 3; j++)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE ||
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
+ {
+ x = j;
+ break;
+ }
+ }
+ int DotTicks = 6;
+ if(spellProto->EffectAmplitude[x] != 0)
+ DotTicks = DotDuration / spellProto->EffectAmplitude[x];
+ if(DotTicks)
+ {
+ DoneAdvertisedBenefit /= DotTicks;
+ TakenAdvertisedBenefit /= DotTicks;
+ }
+ }
+ }
+
+ // Taken/Done total percent damage auras
+ float DoneTotalMod = 1.0f;
+ float TakenTotalMod = 1.0f;
+
+ // ..done
+ AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
+ for(AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
+ {
+ if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) &&
+ (*i)->GetSpellProto()->EquippedItemClass == -1 &&
+ // -1 == any item class (not wand then)
+ (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
+ // 0 == any inventory type (not wand then)
+ {
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ }
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // ..taken
+ AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
+ if( (*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto) )
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // .. taken pct: scripted (increases damage of * against targets *)
+ AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ //Molten Fury
+ case 4920: case 4919:
+ if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT))
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; break;
+ }
+ }
+ // .. taken pct: dummy auras
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ {
+ switch((*i)->GetSpellProto()->SpellIconID)
+ {
+ //Cheat Death
+ case 2109:
+ if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) )
+ {
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ continue;
+ float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4;
+ if (mod < (*i)->GetModifier()->m_amount)
+ mod = (*i)->GetModifier()->m_amount;
+ TakenTotalMod *= (mod+100.0f)/100.0f;
+ }
+ break;
+ //Mangle
+ case 2312:
+ for(int j=0;j<3;j++)
+ {
+ if(GetEffectMechanic(spellProto, j)==MECHANIC_BLEED)
+ {
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // Distribute Damage over multiple effects, reduce by AoE
+ CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
+
+ // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing
+ for(int j = 0; j < 3; ++j)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
+ spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
+ {
+ CastingTime /= 2;
+ break;
+ }
+ }
+
+ switch(spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ // Ignite - do not modify, it is (8*Rank)% damage of procing Spell
+ if(spellProto->Id==12654)
+ {
+ return pdamage;
+ }
+ // Ice Lance
+ else if((spellProto->SpellFamilyFlags & 0x20000LL) && spellProto->SpellIconID == 186)
+ {
+ CastingTime /= 3; // applied 1/3 bonuses in case generic target
+ if(pVictim->isFrozen()) // and compensate this for frozen target.
+ TakenTotalMod *= 3.0f;
+ }
+ // Pyroblast - 115% of Fire Damage, DoT - 20% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 184 )
+ {
+ DotFactor = damagetype == DOT ? 0.2f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 4025;
+ }
+ // Fireball - 100% of Fire Damage, DoT - 0% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x1LL) && spellProto->SpellIconID == 185)
+ {
+ CastingTime = 3500;
+ DotFactor = damagetype == DOT ? 0.0f : 1.0f;
+ }
+ // Molten armor
+ else if (spellProto->SpellFamilyFlags & 0x0000000800000000LL)
+ {
+ CastingTime = 0;
+ }
+ // Arcane Missiles triggered spell
+ else if ((spellProto->SpellFamilyFlags & 0x200000LL) && spellProto->SpellIconID == 225)
+ {
+ CastingTime = 1000;
+ }
+ // Blizzard triggered spell
+ else if ((spellProto->SpellFamilyFlags & 0x80080LL) && spellProto->SpellIconID == 285)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ // Life Tap
+ if((spellProto->SpellFamilyFlags & 0x40000LL) && spellProto->SpellIconID == 208)
+ {
+ CastingTime = 2800; // 80% from +shadow damage
+ DoneTotalMod = 1.0f;
+ TakenTotalMod = 1.0f;
+ }
+ // Dark Pact
+ else if((spellProto->SpellFamilyFlags & 0x80000000LL) && spellProto->SpellIconID == 154 && GetPetGUID())
+ {
+ CastingTime = 3360; // 96% from +shadow damage
+ DoneTotalMod = 1.0f;
+ TakenTotalMod = 1.0f;
+ }
+ // Soul Fire - 115% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x8000000000LL) && spellProto->SpellIconID == 184)
+ {
+ CastingTime = 4025;
+ }
+ // Curse of Agony - 120% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x0000000400LL) && spellProto->SpellIconID == 544)
+ {
+ DotFactor = 1.2f;
+ }
+ // Drain Mana - 0% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 548)
+ {
+ CastingTime = 0;
+ }
+ // Drain Soul 214.3%
+ else if ((spellProto->SpellFamilyFlags & 0x4000LL) && spellProto->SpellIconID == 113 )
+ {
+ CastingTime = 7500;
+ }
+ // Hellfire
+ else if ((spellProto->SpellFamilyFlags & 0x40LL) && spellProto->SpellIconID == 937)
+ {
+ CastingTime = damagetype == DOT ? 5000 : 500; // self damage seems to be so
+ }
+ // Unstable Affliction - 180%
+ else if (spellProto->Id == 31117 && spellProto->SpellIconID == 232)
+ {
+ CastingTime = 6300;
+ }
+ // Corruption 93%
+ else if ((spellProto->SpellFamilyFlags & 0x2LL) && spellProto->SpellIconID == 313)
+ {
+ DotFactor = 0.93f;
+ }
+ break;
+ case SPELLFAMILY_PALADIN:
+ // Consecration - 95% of Holy Damage
+ if((spellProto->SpellFamilyFlags & 0x20LL) && spellProto->SpellIconID == 51)
+ {
+ DotFactor = 0.95f;
+ CastingTime = 3500;
+ }
+ // Seal of Righteousness - 10.2%/9.8% ( based on weapon type ) of Holy Damage, multiplied by weapon speed
+ else if((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 25)
+ {
+ Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+ float wspeed = GetAttackTime(BASE_ATTACK)/1000.0f;
+
+ if( item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ CastingTime = uint32(wspeed*3500*0.102f);
+ else
+ CastingTime = uint32(wspeed*3500*0.098f);
+ }
+ // Judgement of Righteousness - 73%
+ else if ((spellProto->SpellFamilyFlags & 1024) && spellProto->SpellIconID == 25)
+ {
+ CastingTime = 2555;
+ }
+ // Seal of Vengeance - 17% per Fully Stacked Tick - 5 Applications
+ else if ((spellProto->SpellFamilyFlags & 0x80000000000LL) && spellProto->SpellIconID == 2292)
+ {
+ DotFactor = 0.17f;
+ CastingTime = 3500;
+ }
+ // Holy shield - 5% of Holy Damage
+ else if ((spellProto->SpellFamilyFlags & 0x4000000000LL) && spellProto->SpellIconID == 453)
+ {
+ CastingTime = 175;
+ }
+ // Blessing of Sanctuary - 0%
+ else if ((spellProto->SpellFamilyFlags & 0x10000000LL) && spellProto->SpellIconID == 29)
+ {
+ CastingTime = 0;
+ }
+ // Seal of Righteousness trigger - already computed for parent spell
+ else if ( spellProto->SpellFamilyName==SPELLFAMILY_PALADIN && spellProto->SpellIconID==25 && spellProto->AttributesEx4 & 0x00800000LL )
+ {
+ return pdamage;
+ }
+ break;
+ case SPELLFAMILY_SHAMAN:
+ // totem attack
+ if (spellProto->SpellFamilyFlags & 0x000040000000LL)
+ {
+ if (spellProto->SpellIconID == 33) // Fire Nova totem attack must be 21.4%(untested)
+ CastingTime = 749; // ignore CastingTime and use as modifier
+ else if (spellProto->SpellIconID == 680) // Searing Totem attack 8%
+ CastingTime = 280; // ignore CastingTime and use as modifier
+ else if (spellProto->SpellIconID == 37) // Magma totem attack must be 6.67%(untested)
+ CastingTime = 234; // ignore CastingTimePenalty and use as modifier
+ }
+ // Lightning Shield (and proc shield from T2 8 pieces bonus ) 33% per charge
+ else if( (spellProto->SpellFamilyFlags & 0x00000000400LL) || spellProto->Id == 23552)
+ CastingTime = 1155; // ignore CastingTimePenalty and use as modifier
+ break;
+ case SPELLFAMILY_PRIEST:
+ // Mana Burn - 0% of Shadow Damage
+ if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 212)
+ {
+ CastingTime = 0;
+ }
+ // Mind Flay - 59% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x800000LL) && spellProto->SpellIconID == 548)
+ {
+ CastingTime = 2065;
+ }
+ // Holy Fire - 86.71%, DoT - 16.5%
+ else if ((spellProto->SpellFamilyFlags & 0x100000LL) && spellProto->SpellIconID == 156)
+ {
+ DotFactor = damagetype == DOT ? 0.165f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 3035;
+ }
+ // Shadowguard - 28% per charge
+ else if ((spellProto->SpellFamilyFlags & 0x2000000LL) && spellProto->SpellIconID == 19)
+ {
+ CastingTime = 980;
+ }
+ // Touch of Weakeness - 10%
+ else if ((spellProto->SpellFamilyFlags & 0x80000LL) && spellProto->SpellIconID == 1591)
+ {
+ CastingTime = 350;
+ }
+ // Reflective Shield (back damage) - 0% (other spells fit to check not have damage effects/auras)
+ else if (spellProto->SpellFamilyFlags == 0 && spellProto->SpellIconID == 566)
+ {
+ CastingTime = 0;
+ }
+ // Holy Nova - 14%
+ else if ((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 1874)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ // Hurricane triggered spell
+ if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 220)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_WARRIOR:
+ case SPELLFAMILY_HUNTER:
+ case SPELLFAMILY_ROGUE:
+ CastingTime = 0;
+ break;
+ default:
+ break;
+ }
+
+ float LvlPenalty = CalculateLevelPenalty(spellProto);
+
+ // Spellmod SpellDamage
+ float SpellModSpellDamage = 100.0f;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
+
+ SpellModSpellDamage /= 100.0f;
+
+ float DoneActualBenefit = DoneAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
+ float TakenActualBenefit = TakenAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * LvlPenalty;
+
+ float tmpDamage = (float(pdamage)+DoneActualBenefit)*DoneTotalMod;
+
+ // Add flat bonus from spell damage versus
+ tmpDamage += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask);
+
+ // apply spellmod to Done damage
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage);
+
+ tmpDamage = (tmpDamage+TakenActualBenefit)*TakenTotalMod;
+
+ if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() )
+ tmpDamage *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank);
+
+ return tmpDamage > 0 ? uint32(tmpDamage) : 0;
+}
+
+int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
+{
+ int32 DoneAdvertisedBenefit = 0;
+
+ // ..done
+ AuraList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
+ for(AuraList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 &&
+ (*i)->GetSpellProto()->EquippedItemClass == -1 &&
+ // -1 == any item class (not wand then)
+ (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
+ // 0 == any inventory type (not wand then)
+ DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Damage bonus from stats
+ AuraList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue & schoolMask)
+ {
+ SpellEntry const* iSpellProto = (*i)->GetSpellProto();
+ uint8 eff = (*i)->GetEffIndex();
+
+ // stat used dependent from next effect aura SPELL_AURA_MOD_SPELL_HEALING presence and misc value (stat index)
+ Stats usedStat = STAT_INTELLECT;
+ if(eff < 2 && iSpellProto->EffectApplyAuraName[eff+1]==SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT)
+ usedStat = Stats(iSpellProto->EffectMiscValue[eff+1]);
+
+ DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+ }
+ // ... and attack power
+ AuraList const& mDamageDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER);
+ for(AuraList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue & schoolMask)
+ DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
+
+ }
+ return DoneAdvertisedBenefit;
+}
+
+int32 Unit::SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
+{
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+
+ int32 TakenAdvertisedBenefit = 0;
+ // ..done (for creature type by mask) in taken
+ AuraList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
+ for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ // ..taken
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ return TakenAdvertisedBenefit;
+}
+
+bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType)
+{
+ // not criting spell
+ if((spellProto->AttributesEx2 & SPELL_ATTR_EX2_CANT_CRIT))
+ return false;
+
+ float crit_chance = 0.0f;
+ switch(spellProto->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_NONE:
+ return false;
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ {
+ if (schoolMask & SPELL_SCHOOL_MASK_NORMAL)
+ crit_chance = 0.0f;
+ // For other schools
+ else if (GetTypeId() == TYPEID_PLAYER)
+ crit_chance = GetFloatValue( PLAYER_SPELL_CRIT_PERCENTAGE1 + GetFirstSchoolInMask(schoolMask));
+ else
+ {
+ crit_chance = m_baseSpellCritChance;
+ crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
+ }
+ // taken
+ if (pVictim && !IsPositiveSpell(spellProto->Id))
+ {
+ // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE
+ crit_chance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask);
+ // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
+ crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
+ // Modify by player victim resilience
+ if (pVictim->GetTypeId() == TYPEID_PLAYER)
+ crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
+ // scripted (increase crit chance ... against ... target by x%
+ if(pVictim->isFrozen()) // Shatter
+ {
+ AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case 849: crit_chance+= 10.0f; break; //Shatter Rank 1
+ case 910: crit_chance+= 20.0f; break; //Shatter Rank 2
+ case 911: crit_chance+= 30.0f; break; //Shatter Rank 3
+ case 912: crit_chance+= 40.0f; break; //Shatter Rank 4
+ case 913: crit_chance+= 50.0f; break; //Shatter Rank 5
+ }
+ }
+ }
+ }
+ break;
+ }
+ case SPELL_DAMAGE_CLASS_MELEE:
+ case SPELL_DAMAGE_CLASS_RANGED:
+ {
+ if (pVictim)
+ {
+ crit_chance = GetUnitCriticalChance(attackType, pVictim);
+ crit_chance+= (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f;
+ crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ // percent done
+ // only players use intelligence for critical chance computations
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
+
+ crit_chance = crit_chance > 0.0f ? crit_chance : 0.0f;
+ if (roll_chance_f(crit_chance))
+ return true;
+ return false;
+}
+
+uint32 Unit::SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim)
+{
+ // Calculate critical bonus
+ int32 crit_bonus;
+ switch(spellProto->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100%
+ case SPELL_DAMAGE_CLASS_RANGED:
+ // TODO: write here full calculation for melee/ranged spells
+ crit_bonus = damage;
+ break;
+ default:
+ crit_bonus = damage / 2; // for spells is 50%
+ break;
+ }
+
+ // adds additional damage to crit_bonus (from talents)
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
+
+ if(pVictim)
+ {
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask));
+ }
+
+ if(crit_bonus > 0)
+ damage += crit_bonus;
+
+ return damage;
+}
+
+uint32 Unit::SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim)
+{
+ // For totems get healing bonus from owner (statue isn't totem in fact)
+ if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
+ if(Unit* owner = GetOwner())
+ return owner->SpellHealingBonus(spellProto, healamount, damagetype, pVictim);
+
+ // Healing Done
+
+ // These Spells are doing fixed amount of healing (TODO found less hack-like check)
+ if(spellProto->Id == 15290 || spellProto->Id == 39373 || spellProto->Id == 33778 || spellProto->Id == 379 || spellProto->Id == 38395)
+ return healamount;
+
+
+ int32 AdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto));
+ uint32 CastingTime = GetSpellCastTime(spellProto);
+
+ // Healing Taken
+ AdvertisedBenefit += SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
+
+ // Blessing of Light dummy effects healing taken from Holy Light and Flash of Light
+ if (spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && (spellProto->SpellFamilyFlags & 0x00000000C0000000LL))
+ {
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if((*i)->GetSpellProto()->SpellVisual == 9180)
+ {
+ // Flash of Light
+ if ((spellProto->SpellFamilyFlags & 0x0000000040000000LL) && (*i)->GetEffIndex() == 1)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ // Holy Light
+ else if ((spellProto->SpellFamilyFlags & 0x0000000080000000LL) && (*i)->GetEffIndex() == 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ }
+ }
+ }
+
+ float ActualBenefit = 0.0f;
+
+ if (AdvertisedBenefit != 0)
+ {
+ // Healing over Time spells
+ float DotFactor = 1.0f;
+ if(damagetype == DOT)
+ {
+ int32 DotDuration = GetSpellDuration(spellProto);
+ if(DotDuration > 0)
+ {
+ // 200% limit
+ if(DotDuration > 30000) DotDuration = 30000;
+ if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
+ int x = 0;
+ for(int j = 0; j < 3; j++)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL ||
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
+ {
+ x = j;
+ break;
+ }
+ }
+ int DotTicks = 6;
+ if(spellProto->EffectAmplitude[x] != 0)
+ DotTicks = DotDuration / spellProto->EffectAmplitude[x];
+ if(DotTicks)
+ AdvertisedBenefit /= DotTicks;
+ }
+ }
+
+ // distribute healing to all effects, reduce AoE damage
+ CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
+
+ // 0% bonus for damage and healing spells for leech spells from healing bonus
+ for(int j = 0; j < 3; ++j)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
+ spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
+ {
+ CastingTime = 0;
+ break;
+ }
+ }
+
+ // Exception
+ switch (spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_SHAMAN:
+ // Healing stream from totem (add 6% per tick from hill bonus owner)
+ if (spellProto->SpellFamilyFlags & 0x000000002000LL)
+ CastingTime = 210;
+ // Earth Shield 30% per charge
+ else if (spellProto->SpellFamilyFlags & 0x40000000000LL)
+ CastingTime = 1050;
+ break;
+ case SPELLFAMILY_DRUID:
+ // Lifebloom
+ if (spellProto->SpellFamilyFlags & 0x1000000000LL)
+ {
+ CastingTime = damagetype == DOT ? 3500 : 1200;
+ DotFactor = damagetype == DOT ? 0.519f : 1.0f;
+ }
+ // Tranquility triggered spell
+ else if (spellProto->SpellFamilyFlags & 0x80LL)
+ CastingTime = 667;
+ // Rejuvenation
+ else if (spellProto->SpellFamilyFlags & 0x10LL)
+ DotFactor = 0.845f;
+ // Regrowth
+ else if (spellProto->SpellFamilyFlags & 0x40LL)
+ {
+ DotFactor = damagetype == DOT ? 0.705f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 1010;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ // Holy Nova - 14%
+ if ((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 1874)
+ CastingTime = 500;
+ break;
+ case SPELLFAMILY_PALADIN:
+ // Seal and Judgement of Light
+ if ( spellProto->SpellFamilyFlags & 0x100040000LL )
+ CastingTime = 0;
+ break;
+ case SPELLFAMILY_WARRIOR:
+ case SPELLFAMILY_ROGUE:
+ case SPELLFAMILY_HUNTER:
+ CastingTime = 0;
+ break;
+ }
+
+ float LvlPenalty = CalculateLevelPenalty(spellProto);
+
+ // Spellmod SpellDamage
+ float SpellModSpellDamage = 100.0f;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
+
+ SpellModSpellDamage /= 100.0f;
+
+ ActualBenefit = (float)AdvertisedBenefit * ((float)CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
+ }
+
+ // use float as more appropriate for negative values and percent applying
+ float heal = healamount + ActualBenefit;
+
+ // TODO: check for ALL/SPELLS type
+ // Healing done percent
+ AuraList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
+ for(AuraList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i)
+ heal *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
+
+ // apply spellmod to Done amount
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal);
+
+ // Healing Wave cast
+ if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags & 0x0000000000000040LL)
+ {
+ // Search for Healing Way on Victim (stack up to 3 time)
+ int32 pctMod = 0;
+ Unit::AuraList const& auraDummy = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr!=auraDummy.end(); ++itr)
+ if((*itr)->GetId() == 29203)
+ pctMod += (*itr)->GetModifier()->m_amount;
+ // Apply bonus
+ if (pctMod)
+ heal = heal * (100 + pctMod) / 100;
+ }
+
+ // Healing taken percent
+ float minval = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
+ if(minval)
+ heal *= (100.0f + minval) / 100.0f;
+
+ float maxval = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
+ if(maxval)
+ heal *= (100.0f + maxval) / 100.0f;
+
+ if (heal < 0) heal = 0;
+
+ return uint32(heal);
+}
+
+int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
+{
+ int32 AdvertisedBenefit = 0;
+
+ AuraList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE);
+ for(AuraList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ // Healing bonus of spirit, intellect and strength
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Healing bonus from stats
+ AuraList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i)
+ {
+ // stat used dependent from misc value (stat index)
+ Stats usedStat = Stats((*i)->GetSpellProto()->EffectMiscValue[(*i)->GetEffIndex()]);
+ AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+
+ // ... and attack power
+ AuraList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER);
+ for(AuraList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue & schoolMask)
+ AdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+ return AdvertisedBenefit;
+}
+
+int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
+{
+ int32 AdvertisedBenefit = 0;
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ return AdvertisedBenefit;
+}
+
+bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges)
+{
+ // no charges dependent checks
+ SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
+ for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
+ if(itr->type & shoolMask)
+ return true;
+
+ // charges dependent checks
+ SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
+ for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
+ {
+ if(itr->type & shoolMask)
+ {
+ if(useCharges)
+ {
+ AuraList const& auraDamageImmunity = GetAurasByType(SPELL_AURA_DAMAGE_IMMUNITY);
+ for(AuraList::const_iterator auraItr = auraDamageImmunity.begin(); auraItr != auraDamageImmunity.end(); ++auraItr)
+ {
+ if((*auraItr)->GetId()==itr->spellId)
+ {
+ if((*auraItr)->m_procCharges > 0)
+ {
+ --(*auraItr)->m_procCharges;
+ if((*auraItr)->m_procCharges==0)
+ RemoveAurasDueToSpell(itr->spellId);
+ }
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
+{
+ if (!spellInfo)
+ return false;
+
+ // no charges first
+
+ //FIX ME this hack: don't get feared if stunned
+ if (spellInfo->Mechanic == MECHANIC_FEAR )
+ {
+ if ( hasUnitState(UNIT_STAT_STUNDED) )
+ return true;
+ }
+
+ // not have spells with charges currently
+ SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
+ for(SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
+ if(itr->type == spellInfo->Dispel)
+ return true;
+
+ if( !(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE)) // unaffected by school immunity
+ {
+ // not have spells with charges currently
+ SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
+ for(SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
+ if( !(IsPositiveSpell(itr->spellId) && IsPositiveSpell(spellInfo->Id)) &&
+ (itr->type & GetSpellSchoolMask(spellInfo)) )
+ return true;
+ }
+
+ // charges dependent checks
+
+ SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
+ for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
+ {
+ if(itr->type == spellInfo->Mechanic)
+ {
+ if(useCharges)
+ {
+ AuraList const& auraMechImmunity = GetAurasByType(SPELL_AURA_MECHANIC_IMMUNITY);
+ for(AuraList::const_iterator auraItr = auraMechImmunity.begin(); auraItr != auraMechImmunity.end(); ++auraItr)
+ {
+ if((*auraItr)->GetId()==itr->spellId)
+ {
+ if((*auraItr)->m_procCharges > 0)
+ {
+ --(*auraItr)->m_procCharges;
+ if((*auraItr)->m_procCharges==0)
+ RemoveAurasDueToSpell(itr->spellId);
+ }
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Unit::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
+{
+ //If m_immuneToEffect type contain this effect type, IMMUNE effect.
+ SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT];
+ for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
+ if(itr->type == effect)
+ return true;
+
+ SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
+ for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
+ if(itr->type == mechanic)
+ return true;
+
+ return false;
+}
+
+bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
+{
+ if(!spellInfo)
+ return false;
+
+ uint32 family = spellInfo->SpellFamilyName;
+ uint64 flags = spellInfo->SpellFamilyFlags;
+
+ if((family == 5 && flags == 256) || //Searing Pain
+ (family == 6 && flags == 8192) || //Mind Blast
+ (family == 11 && flags == 1048576)) //Earth Shock
+ return true;
+
+ return false;
+}
+
+void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attType, SpellEntry const *spellProto)
+{
+ if(!pVictim)
+ return;
+
+ if(*pdamage == 0)
+ return;
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+
+ // Taken/Done fixed damage bonus auras
+ int32 DoneFlatBenefit = 0;
+ int32 TakenFlatBenefit = 0;
+
+ // ..done (for creature type by mask) in taken
+ AuraList const& mDamageDoneCreature = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
+ for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneFlatBenefit += (*i)->GetModifier()->m_amount;
+
+ // ..done
+ // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage
+
+ // ..done (base at attack power for marked target and base at attack power for creature type)
+ int32 APbonus = 0;
+ if(attType == RANGED_ATTACK)
+ {
+ APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS);
+
+ // ..done (base at attack power and creature type)
+ AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS);
+ for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ APbonus += (*i)->GetModifier()->m_amount;
+ }
+ else
+ {
+ APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS);
+
+ // ..done (base at attack power and creature type)
+ AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS);
+ for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ APbonus += (*i)->GetModifier()->m_amount;
+ }
+
+ if (APbonus!=0) // Can be negative
+ {
+ bool normalized = false;
+ if(spellProto)
+ {
+ for (uint8 i = 0; i<3;i++)
+ {
+ if (spellProto->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG)
+ {
+ normalized = true;
+ break;
+ }
+ }
+ }
+
+ DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType,normalized));
+ }
+
+ // ..taken
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ TakenFlatBenefit += (*i)->GetModifier()->m_amount;
+
+ if(attType!=RANGED_ATTACK)
+ TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN);
+ else
+ TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN);
+
+ // Done/Taken total percent damage auras
+ float DoneTotalMod = 1;
+ float TakenTotalMod = 1;
+
+ // ..done
+ // SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage
+ // SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage
+
+ AuraList const& mDamageDoneVersus = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // ..taken
+ AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // .. taken pct: dummy auras
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ {
+ switch((*i)->GetSpellProto()->SpellIconID)
+ {
+ //Cheat Death
+ case 2109:
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ continue;
+ float mod = ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*(-8.0f);
+ if (mod < (*i)->GetModifier()->m_amount)
+ mod = (*i)->GetModifier()->m_amount;
+ 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==0x00008000LL))
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
+ break;
+ }
+ }
+
+ // .. taken pct: class scripts
+ AuraList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i)
+ {
+ switch((*i)->GetMiscValue())
+ {
+ case 6427: case 6428: // Dirty Deeds
+ if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT))
+ {
+ Aura* eff0 = GetAura((*i)->GetId(),0);
+ if(!eff0 || (*i)->GetEffIndex()!=1)
+ {
+ sLog.outError("Spell structure of DD (%u) changed.",(*i)->GetId());
+ continue;
+ }
+
+ // effect 0 have expected value but in negative state
+ TakenTotalMod *= (-eff0->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ break;
+ }
+ }
+
+ if(attType != RANGED_ATTACK)
+ {
+ AuraList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT);
+ for(AuraList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ else
+ {
+ AuraList const& mModRangedDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT);
+ for(AuraList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+
+ float tmpDamage = float(int32(*pdamage) + DoneFlatBenefit) * DoneTotalMod;
+
+ // apply spellmod to Done damage
+ if(spellProto)
+ {
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage);
+ }
+
+ tmpDamage = (tmpDamage + TakenFlatBenefit)*TakenTotalMod;
+
+ // bonus result can be negative
+ *pdamage = tmpDamage > 0 ? uint32(tmpDamage) : 0;
+}
+
+void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply)
+{
+ if (apply)
+ {
+ for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(), next; itr != m_spellImmune[op].end(); itr = next)
+ {
+ next = itr; ++next;
+ if(itr->type == type)
+ {
+ m_spellImmune[op].erase(itr);
+ next = m_spellImmune[op].begin();
+ }
+ }
+ SpellImmune Immune;
+ Immune.spellId = spellId;
+ Immune.type = type;
+ m_spellImmune[op].push_back(Immune);
+ }
+ else
+ {
+ for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
+ {
+ if(itr->spellId == spellId)
+ {
+ m_spellImmune[op].erase(itr);
+ break;
+ }
+ }
+ }
+
+}
+
+void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply)
+{
+ ApplySpellImmune(spellProto->Id,IMMUNITY_DISPEL, type, apply);
+
+ if (apply && spellProto->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ RemoveAurasWithDispelType(type);
+}
+
+float Unit::GetWeaponProcChance() const
+{
+ // normalized proc chance for weapon attack speed
+ // (odd formulae...)
+ if(isAttackReady(BASE_ATTACK))
+ return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
+ else if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
+ return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
+ return 0;
+}
+
+float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM) const
+{
+ // proc per minute chance calculation
+ if (PPM <= 0) return 0.0f;
+ uint32 result = uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
+ return result;
+}
+
+void Unit::Mount(uint32 mount)
+{
+ if(!mount)
+ return;
+
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNTING);
+
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, mount);
+
+ SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
+
+ // unsummon pet
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Pet* pet = GetPet();
+ if(pet)
+ {
+ if(pet->isControlled())
+ {
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber());
+ ((Player*)this)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
+ }
+
+ ((Player*)this)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT);
+ }
+ else
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
+ }
+}
+
+void Unit::Unmount()
+{
+ if(!IsMounted())
+ return;
+
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_MOUNTED);
+
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
+ RemoveFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
+
+ // only resummon old pet if the player is already added to a map
+ // this prevents adding a pet to a not created map which would otherwise cause a crash
+ // (it could probably happen when logging in after a previous crash)
+ if(GetTypeId() == TYPEID_PLAYER && IsInWorld() && ((Player*)this)->GetTemporaryUnsummonedPetNumber() && isAlive())
+ {
+ Pet* NewPet = new Pet;
+ if(!NewPet->LoadPetFromDB(this, 0, ((Player*)this)->GetTemporaryUnsummonedPetNumber(), true))
+ delete NewPet;
+
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
+ }
+}
+
+void Unit::SetInCombatWith(Unit* enemy)
+{
+ Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
+ if(eOwner->IsPvP())
+ {
+ SetInCombatState(true);
+ return;
+ }
+
+ //check for duel
+ if(eOwner->GetTypeId() == TYPEID_PLAYER && ((Player*)eOwner)->duel)
+ {
+ Unit const* myOwner = GetCharmerOrOwnerOrSelf();
+ if(((Player const*)eOwner)->duel->opponent == myOwner)
+ {
+ SetInCombatState(true);
+ return;
+ }
+ }
+ SetInCombatState(false);
+}
+
+void Unit::SetInCombatState(bool PvP)
+{
+ // only alive units can be in combat
+ if(!isAlive())
+ return;
+
+ if(PvP)
+ m_CombatTimer = 5000;
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
+ if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
+}
+
+void Unit::ClearInCombat()
+{
+ m_CombatTimer = 0;
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
+ if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
+
+ // Player's state will be cleared in Player::UpdateContestedPvP
+ if(GetTypeId()!=TYPEID_PLAYER)
+ clearUnitState(UNIT_STAT_ATTACK_PLAYER);
+}
+
+bool Unit::isTargetableForAttack() const
+{
+ if (GetTypeId()==TYPEID_PLAYER && ((Player *)this)->isGameMaster())
+ return false;
+
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
+ return false;
+
+ return isAlive() && !hasUnitState(UNIT_STAT_DIED)&& !isInFlight() /*&& !isStealth()*/;
+}
+
+int32 Unit::ModifyHealth(int32 dVal)
+{
+ int32 gain = 0;
+
+ if(dVal==0)
+ return 0;
+
+ int32 curHealth = (int32)GetHealth();
+
+ int32 val = dVal + curHealth;
+ if(val <= 0)
+ {
+ SetHealth(0);
+ return -curHealth;
+ }
+
+ int32 maxHealth = (int32)GetMaxHealth();
+
+ if(val < maxHealth)
+ {
+ SetHealth(val);
+ gain = val - curHealth;
+ }
+ else if(curHealth != maxHealth)
+ {
+ SetHealth(maxHealth);
+ gain = maxHealth - curHealth;
+ }
+
+ return gain;
+}
+
+int32 Unit::ModifyPower(Powers power, int32 dVal)
+{
+ int32 gain = 0;
+
+ if(dVal==0)
+ return 0;
+
+ int32 curPower = (int32)GetPower(power);
+
+ int32 val = dVal + curPower;
+ if(val <= 0)
+ {
+ SetPower(power,0);
+ return -curPower;
+ }
+
+ int32 maxPower = (int32)GetMaxPower(power);
+
+ if(val < maxPower)
+ {
+ SetPower(power,val);
+ gain = val - curPower;
+ }
+ else if(curPower != maxPower)
+ {
+ SetPower(power,maxPower);
+ gain = maxPower - curPower;
+ }
+
+ return gain;
+}
+
+bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) const
+{
+ if(!u)
+ return false;
+
+ // Always can see self
+ if (u==this)
+ return true;
+
+ // player visible for other player if not logout and at same transport
+ // including case when player is out of world
+ bool at_same_transport =
+ GetTypeId() == TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER &&
+ !((Player*)this)->GetSession()->PlayerLogout() && !((Player*)u)->GetSession()->PlayerLogout() &&
+ !((Player*)this)->GetSession()->PlayerLoading() && !((Player*)u)->GetSession()->PlayerLoading() &&
+ ((Player*)this)->GetTransport() && ((Player*)this)->GetTransport() == ((Player*)u)->GetTransport();
+
+ // not in world
+ if(!at_same_transport && (!IsInWorld() || !u->IsInWorld()))
+ return false;
+
+ // forbidden to seen (at GM respawn command)
+ if(m_Visibility==VISIBILITY_RESPAWN)
+ return false;
+
+ // always seen by owner
+ if(GetCharmerOrOwnerGUID()==u->GetGUID())
+ return true;
+
+ // Grid dead/alive checks
+ if( u->GetTypeId()==TYPEID_PLAYER)
+ {
+ // non visible at grid for any stealth state
+ if(!IsVisibleInGridForPlayer((Player *)u))
+ return false;
+
+ // if player is dead then he can't detect anyone in anycases
+ if(!u->isAlive())
+ detect = false;
+ }
+ else
+ {
+ // all dead creatures/players not visible for any creatures
+ if(!u->isAlive() || !isAlive())
+ return false;
+ }
+
+ // different visible distance checks
+ if(u->isInFlight()) // what see player in flight
+ {
+ // use object grey distance for all (only see objects any way)
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
+ return false;
+ }
+ else if(!isAlive()) // distance for show body
+ {
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
+ return false;
+ }
+ else if(GetTypeId()==TYPEID_PLAYER) // distance for show player
+ {
+ if(u->GetTypeId()==TYPEID_PLAYER)
+ {
+ // Players far than max visible distance for player or not in our map are not visible too
+ if (!at_same_transport && !IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ else
+ {
+ // Units far than max visible distance for creature or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ }
+ else if(GetCharmerOrOwnerGUID()) // distance for show pet/charmed
+ {
+ // Pet/charmed far than max visible distance for player or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ else // distance for show creature
+ {
+ // Units far than max visible distance for creature or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+
+ // Visible units, always are visible for all units, except for units under invisibility
+ if (m_Visibility == VISIBILITY_ON && u->m_invisibilityMask==0)
+ return true;
+
+ // GMs see any players, not higher GMs and all units
+ if (u->GetTypeId() == TYPEID_PLAYER && ((Player *)u)->isGameMaster())
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ return ((Player *)this)->GetSession()->GetSecurity() <= ((Player *)u)->GetSession()->GetSecurity();
+ else
+ return true;
+ }
+
+ // non faction visibility non-breakable for non-GMs
+ if (m_Visibility == VISIBILITY_OFF)
+ return false;
+
+ // raw invisibility
+ bool invisible = (m_invisibilityMask != 0 || u->m_invisibilityMask !=0);
+
+ // detectable invisibility case
+ if( invisible && (
+ // Invisible units, always are visible for units under same invisibility type
+ (m_invisibilityMask & u->m_invisibilityMask)!=0 ||
+ // Invisible units, always are visible for unit that can detect this invisibility (have appropriate level for detect)
+ u->canDetectInvisibilityOf(this) ||
+ // Units that can detect invisibility always are visible for units that can be detected
+ canDetectInvisibilityOf(u) ))
+ {
+ invisible = false;
+ }
+
+ // special cases for always overwrite invisibility/stealth
+ if(invisible || m_Visibility == VISIBILITY_GROUP_STEALTH)
+ {
+ // non-hostile case
+ if (!u->IsHostileTo(this))
+ {
+ // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting)
+ if(GetTypeId()==TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)this)->IsGroupVisibleFor(((Player*)u)))
+ return true;
+
+ // else apply same rules as for hostile case (detecting check for stealth)
+ }
+ }
+ // hostile case
+ else
+ {
+ // Hunter mark functionality
+ AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED);
+ for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
+ if((*iter)->GetCasterGUID()==u->GetGUID())
+ return true;
+
+ // else apply detecting check for stealth
+ }
+
+ // none other cases for detect invisibility, so invisible
+ if(invisible)
+ return false;
+
+ // else apply stealth detecting check
+ }
+
+ // unit got in stealth in this moment and must ignore old detected state
+ if (m_Visibility == VISIBILITY_GROUP_NO_DETECT)
+ return false;
+
+ // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible
+ if (m_Visibility != VISIBILITY_GROUP_STEALTH)
+ return true;
+
+ // NOW ONLY STEALTH CASE
+
+ // stealth and detected and visible for some seconds
+ if (u->GetTypeId() == TYPEID_PLAYER && ((Player*)u)->m_DetectInvTimer > 300 && ((Player*)u)->HaveAtClient(this))
+ return true;
+
+ //if in non-detect mode then invisible for unit
+ if (!detect)
+ return false;
+
+ // Special cases
+
+ // If is attacked then stealth is lost, some creature can use stealth too
+ if( !getAttackers().empty() )
+ return true;
+
+ // If there is collision rogue is seen regardless of level difference
+ // TODO: check sizes in DB
+ float distance = GetDistance(u);
+ if (distance < 0.24f)
+ return true;
+
+ //If a mob or player is stunned he will not be able to detect stealth
+ if (u->hasUnitState(UNIT_STAT_STUNDED) && (u != this))
+ return false;
+
+ // Creature can detect target only in aggro radius
+ if(u->GetTypeId() != TYPEID_PLAYER)
+ {
+ //Always invisible from back and out of aggro range
+ bool isInFront = u->isInFront(this,((Creature const*)u)->GetAttackDistance(this));
+ if(!isInFront)
+ return false;
+ }
+ else
+ {
+ //Always invisible from back
+ bool isInFront = u->isInFront(this,(GetTypeId()==TYPEID_PLAYER || GetCharmerOrOwnerGUID()) ? World::GetMaxVisibleDistanceForPlayer() : World::GetMaxVisibleDistanceForCreature());
+ if(!isInFront)
+ return false;
+ }
+
+ // if doesn't have stealth detection (Shadow Sight), then check how stealthy the unit is, otherwise just check los
+ if(!u->HasAuraType(SPELL_AURA_DETECT_STEALTH))
+ {
+ //Calculation if target is in front
+
+ //Visible distance based on stealth value (stealth rank 4 300MOD, 10.5 - 3 = 7.5)
+ float visibleDistance = 10.5f - (GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH)/100.0f);
+
+ //Visible distance is modified by
+ //-Level Diff (every level diff = 1.0f in visible distance)
+ visibleDistance += int32(u->getLevelForTarget(this)) - int32(this->getLevelForTarget(u));
+
+ //This allows to check talent tree and will add addition stealth dependent on used points)
+ int32 stealthMod = GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL);
+ if(stealthMod < 0)
+ stealthMod = 0;
+
+ //-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 += (int32(u->GetTotalAuraModifier(SPELL_AURA_MOD_DETECT)) - stealthMod)/5.0f;
+
+ if(distance > visibleDistance)
+ return false;
+ }
+
+ // Now check is target visible with LoS
+ float ox,oy,oz;
+ u->GetPosition(ox,oy,oz);
+ return IsWithinLOS(ox,oy,oz);
+}
+
+void Unit::SetVisibility(UnitVisibility x)
+{
+ m_Visibility = x;
+
+ if(IsInWorld())
+ {
+ Map *m = MapManager::Instance().GetMap(GetMapId(), this);
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ m->PlayerRelocation((Player*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
+ else
+ m->CreatureRelocation((Creature*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
+ }
+}
+
+bool Unit::canDetectInvisibilityOf(Unit const* u) const
+{
+ if(uint32 mask = (m_detectInvisibilityMask & u->m_invisibilityMask))
+ {
+ for(uint32 i = 0; i < 10; ++i)
+ {
+ if(((1 << i) & mask)==0)
+ continue;
+
+ // find invisibility level
+ uint32 invLevel = 0;
+ Unit::AuraList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
+ for(Unit::AuraList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
+ if(((*itr)->GetModifier()->m_miscvalue)==i && invLevel < (*itr)->GetModifier()->m_amount)
+ invLevel = (*itr)->GetModifier()->m_amount;
+
+ // find invisibility detect level
+ uint32 detectLevel = 0;
+ Unit::AuraList const& dAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION);
+ for(Unit::AuraList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr)
+ if(((*itr)->GetModifier()->m_miscvalue)==i && detectLevel < (*itr)->GetModifier()->m_amount)
+ detectLevel = (*itr)->GetModifier()->m_amount;
+
+ if(i==6 && GetTypeId()==TYPEID_PLAYER) // special drunk detection case
+ {
+ detectLevel = ((Player*)this)->GetDrunkValue();
+ }
+
+ if(invLevel <= detectLevel)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
+{
+ int32 main_speed_mod = 0;
+ float stack_bonus = 1.0f;
+ float non_stack_bonus = 1.0f;
+
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ return;
+ case MOVE_RUN:
+ {
+ if (IsMounted()) // Use on mount auras
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS);
+ non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK))/100.0f;
+ }
+ else
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_SPEED_ALWAYS);
+ non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NOT_STACK))/100.0f;
+ }
+ break;
+ }
+ case MOVE_WALKBACK:
+ return;
+ case MOVE_SWIM:
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SWIM_SPEED);
+ break;
+ }
+ case MOVE_SWIMBACK:
+ return;
+ case MOVE_FLY:
+ {
+ if (IsMounted()) // Use on mount auras
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
+ else // Use not mount (shapeshift for example) auras (should stack)
+ main_speed_mod = GetTotalAuraModifier(SPELL_AURA_MOD_SPEED_FLIGHT);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS);
+ non_stack_bonus = (100.0 + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK))/100.0f;
+ break;
+ }
+ case MOVE_FLYBACK:
+ 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;
+
+ switch(mtype)
+ {
+ case MOVE_RUN:
+ case MOVE_SWIM:
+ case MOVE_FLY:
+ {
+ // 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))
+ {
+ // Use speed from aura
+ float max_speed = normalization / baseMoveSpeed[mtype];
+ if (speed > max_speed)
+ speed = max_speed;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Apply strongest slow aura mod to speed
+ int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED);
+ if (slow)
+ speed *=(100.0f + slow)/100.0f;
+ SetSpeed(mtype, speed, forced);
+}
+
+float Unit::GetSpeed( UnitMoveType mtype ) const
+{
+ return m_speed_rate[mtype]*baseMoveSpeed[mtype];
+}
+
+void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
+{
+ if (rate < 0)
+ rate = 0.0f;
+
+ // Update speed only on change
+ if (m_speed_rate[mtype] == rate)
+ return;
+
+ m_speed_rate[mtype] = rate;
+
+ propagateSpeedChange();
+
+ // Send speed change packet only for player
+ if (GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ WorldPacket data;
+ if(!forced)
+ {
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_RUN:
+ data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_WALKBACK:
+ data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_SWIM:
+ data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_SWIMBACK:
+ data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_TURN:
+ data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_FLY:
+ data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_FLYBACK:
+ data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ default:
+ sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
+ return;
+ }
+
+ data.append(GetPackGUID());
+ data << uint32(0); //movement flags
+ data << uint8(0); //unk
+ data << uint32(getMSTime());
+ data << float(GetPositionX());
+ data << float(GetPositionY());
+ data << float(GetPositionZ());
+ data << float(GetOrientation());
+ data << uint32(0); //flag unk
+ data << float(GetSpeed(mtype));
+ SendMessageToSet( &data, true );
+ }
+ else
+ {
+ // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
+ // and do it only for real sent packets and use run for run/mounted as client expected
+ ++((Player*)this)->m_forced_speed_changes[mtype];
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_RUN:
+ data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
+ break;
+ case MOVE_WALKBACK:
+ data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_SWIM:
+ data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
+ break;
+ case MOVE_SWIMBACK:
+ data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_TURN:
+ data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
+ break;
+ case MOVE_FLY:
+ data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
+ break;
+ case MOVE_FLYBACK:
+ data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
+ break;
+ default:
+ sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
+ return;
+ }
+ data.append(GetPackGUID());
+ data << (uint32)0;
+ if (mtype == MOVE_RUN)
+ data << uint8(0); // new 2.1.0
+ data << float(GetSpeed(mtype));
+ SendMessageToSet( &data, true );
+ }
+ if(Pet* pet = GetPet())
+ pet->SetSpeed(MOVE_RUN, m_speed_rate[mtype],forced);
+}
+
+void Unit::SetHover(bool on)
+{
+ if(on)
+ CastSpell(this,11010,true);
+ else
+ RemoveAurasDueToSpell(11010);
+}
+
+void Unit::setDeathState(DeathState s)
+{
+ if (s != ALIVE && s!= JUST_ALIVED)
+ {
+ CombatStop();
+ DeleteThreatList();
+ ClearComboPointHolders(); // any combo points pointed to unit lost at it death
+
+ if(IsNonMeleeSpellCasted(false))
+ InterruptNonMeleeSpells(false);
+ }
+
+ if (s == JUST_DIED)
+ {
+ RemoveAllAurasOnDeath();
+ UnsummonAllTotems();
+
+ ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
+ ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
+ // remove aurastates allowing special moves
+ ClearAllReactives();
+ ClearDiminishings();
+ }
+ else if(s == JUST_ALIVED)
+ {
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
+ }
+
+ if (m_deathState != ALIVE && s == ALIVE)
+ {
+ //_ApplyAllAuraMods();
+ }
+ m_deathState = s;
+}
+
+/*########################################
+######## ########
+######## AGGRO SYSTEM ########
+######## ########
+########################################*/
+bool Unit::CanHaveThreatList() const
+{
+ // only creatures can have threat list
+ if( GetTypeId() != TYPEID_UNIT )
+ return false;
+
+ // only alive units can have threat list
+ if( !isAlive() )
+ return false;
+
+ // pets and totems can not have threat list
+ if( ((Creature*)this)->isPet() || ((Creature*)this)->isTotem() )
+ return false;
+
+ return true;
+}
+
+//======================================================================
+
+float Unit::ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask)
+{
+ if(!HasAuraType(SPELL_AURA_MOD_THREAT))
+ return threat;
+
+ SpellSchools school = GetFirstSchoolInMask(schoolMask);
+
+ return threat * m_threatModifier[school];
+}
+
+//======================================================================
+
+void Unit::AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask, SpellEntry const *threatSpell)
+{
+ // Only mobs can manage threat lists
+ if(CanHaveThreatList())
+ m_ThreatManager.addThreat(pVictim, threat, schoolMask, threatSpell);
+}
+
+//======================================================================
+
+void Unit::DeleteThreatList()
+{
+ m_ThreatManager.clearReferences();
+}
+
+//======================================================================
+
+void Unit::TauntApply(Unit* taunter)
+{
+ assert(GetTypeId()== TYPEID_UNIT);
+
+ if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
+ return;
+
+ if(!CanHaveThreatList())
+ return;
+
+ Unit *target = getVictim();
+ if(target && target == taunter)
+ return;
+
+ SetInFront(taunter);
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(taunter);
+
+ m_ThreatManager.tauntApply(taunter);
+}
+
+//======================================================================
+
+void Unit::TauntFadeOut(Unit *taunter)
+{
+ assert(GetTypeId()== TYPEID_UNIT);
+
+ if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
+ return;
+
+ if(!CanHaveThreatList())
+ return;
+
+ Unit *target = getVictim();
+ if(!target || target != taunter)
+ return;
+
+ if(m_ThreatManager.isThreatListEmpty())
+ {
+ if(((Creature*)this)->AI())
+ ((Creature*)this)->AI()->EnterEvadeMode();
+ return;
+ }
+
+ m_ThreatManager.tauntFadeOut(taunter);
+ target = m_ThreatManager.getHostilTarget();
+
+ if (target && target != taunter)
+ {
+ SetInFront(target);
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(target);
+ }
+}
+
+//======================================================================
+
+bool Unit::SelectHostilTarget()
+{
+ //function provides main threat functionality
+ //next-victim-selection algorithm and evade mode are called
+ //threat list sorting etc.
+
+ assert(GetTypeId()== TYPEID_UNIT);
+ Unit* target = NULL;
+
+ //This function only useful once AI has been initilazied
+ if (!((Creature*)this)->AI())
+ return false;
+
+ if(!m_ThreatManager.isThreatListEmpty())
+ {
+ if(!HasAuraType(SPELL_AURA_MOD_TAUNT))
+ {
+ target = m_ThreatManager.getHostilTarget();
+ }
+ }
+
+ if(target)
+ {
+ if(!hasUnitState(UNIT_STAT_STUNDED))
+ SetInFront(target);
+ ((Creature*)this)->AI()->AttackStart(target);
+ return true;
+ }
+
+ // no target but something prevent go to evade mode
+ if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_TAUNT) )
+ return false;
+
+ // last case when creature don't must go to evade mode:
+ // 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( GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE )
+ {
+ for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
+ {
+ if( (*itr)->IsInMap(this) && (*itr)->isTargetableForAttack() && (*itr)->isInAccessablePlaceFor((Creature*)this) )
+ return false;
+ }
+ }
+
+ // enter in evade mode in other case
+ ((Creature*)this)->AI()->EnterEvadeMode();
+
+ return false;
+}
+
+//======================================================================
+//======================================================================
+//======================================================================
+
+int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 effBasePoints, Unit const* target)
+{
+ Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
+
+ uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
+
+ int32 level = int32(getLevel()) - int32(spellProto->spellLevel);
+ if (level > spellProto->maxLevel && spellProto->maxLevel > 0)
+ level = spellProto->maxLevel;
+
+ float basePointsPerLevel = spellProto->EffectRealPointsPerLevel[effect_index];
+ float randomPointsPerLevel = spellProto->EffectDicePerLevel[effect_index];
+ int32 basePoints = int32(effBasePoints + level * basePointsPerLevel);
+ int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel);
+ float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index];
+
+ // prevent random generator from getting confused by spells casted with Unit::CastCustomSpell
+ int32 randvalue = spellProto->EffectBaseDice[effect_index] >= randomPoints ? spellProto->EffectBaseDice[effect_index]:irand(spellProto->EffectBaseDice[effect_index], randomPoints);
+ int32 value = basePoints + randvalue;
+ //random damage
+ if(comboDamage != 0 && unitPlayer && target && (target->GetGUID() == unitPlayer->GetComboTarget()))
+ value += (int32)(comboDamage * comboPoints);
+
+ if(Player* modOwner = GetSpellModOwner())
+ {
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_ALL_EFFECTS, value);
+ switch(effect_index)
+ {
+ case 0:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT1, value);
+ break;
+ case 1:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT2, value);
+ break;
+ case 2:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT3, value);
+ break;
+ }
+ }
+
+ if(spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel &&
+ spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE &&
+ spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK)
+ value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f));
+
+ return value;
+}
+
+int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target)
+{
+ Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
+
+ uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
+
+ int32 minduration = GetSpellDuration(spellProto);
+ int32 maxduration = GetSpellMaxDuration(spellProto);
+
+ int32 duration;
+
+ if( minduration != -1 && minduration != maxduration )
+ duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
+ else
+ duration = minduration;
+
+ if (duration > 0)
+ {
+ int32 mechanic = GetEffectMechanic(spellProto, effect_index);
+ // Find total mod value (negative bonus)
+ int32 durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, mechanic);
+ // Find max mod (negative bonus)
+ int32 durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanic);
+
+ int32 durationMod = 0;
+ // Select strongest negative mod
+ if (durationMod_always > durationMod_not_stack)
+ durationMod = durationMod_not_stack;
+ else
+ durationMod = durationMod_always;
+
+ if (durationMod != 0)
+ duration = int32(int64(duration) * (100+durationMod) /100);
+
+ if (duration < 0) duration = 0;
+ }
+
+ return duration;
+}
+
+DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
+{
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ if(!i->hitCount)
+ return DIMINISHING_LEVEL_1;
+
+ if(!i->hitTime)
+ return DIMINISHING_LEVEL_1;
+
+ // If last spell was casted more than 15 seconds ago - reset the count.
+ if(i->stack==0 && getMSTimeDiff(i->hitTime,getMSTime()) > 15000)
+ {
+ i->hitCount = DIMINISHING_LEVEL_1;
+ return DIMINISHING_LEVEL_1;
+ }
+ // or else increase the count.
+ else
+ {
+ return DiminishingLevels(i->hitCount);
+ }
+ }
+ return DIMINISHING_LEVEL_1;
+}
+
+void Unit::IncrDiminishing(DiminishingGroup group)
+{
+ // Checking for existing in the table
+ bool IsExist = false;
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ IsExist = true;
+ if(i->hitCount < DIMINISHING_LEVEL_IMMUNE)
+ i->hitCount += 1;
+
+ break;
+ }
+
+ if(!IsExist)
+ m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2));
+}
+
+void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level)
+{
+ if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) )
+ return;
+
+ // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
+ if(duration > 10000 && IsDiminishingReturnsGroupDurationLimited(group))
+ {
+ // test pet/charm masters instead pets/charmeds
+ Unit const* targetOwner = GetCharmerOrOwner();
+ Unit const* casterOwner = caster->GetCharmerOrOwner();
+
+ Unit const* target = targetOwner ? targetOwner : this;
+ Unit const* source = casterOwner ? casterOwner : caster;
+
+ if(target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() == TYPEID_PLAYER)
+ duration = 10000;
+ }
+
+ float mod = 1.0f;
+
+ // Some diminishings applies to mobs too (for example, Stun)
+ if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL)
+ {
+ DiminishingLevels diminish = Level;
+ switch(diminish)
+ {
+ case DIMINISHING_LEVEL_1: break;
+ case DIMINISHING_LEVEL_2: mod = 0.5f; break;
+ case DIMINISHING_LEVEL_3: mod = 0.25f; break;
+ case DIMINISHING_LEVEL_IMMUNE: mod = 0.0f;break;
+ default: break;
+ }
+ }
+
+ duration = int32(duration * mod);
+}
+
+void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
+{
+ // Checking for existing in the table
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ i->hitTime = getMSTime();
+
+ if(apply)
+ i->stack += 1;
+ else if(i->stack)
+ i->stack -= 1;
+
+ break;
+ }
+}
+
+Unit* Unit::GetUnit(WorldObject& object, uint64 guid)
+{
+ return ObjectAccessor::GetUnit(object,guid);
+}
+
+bool Unit::isVisibleForInState( Player const* u, bool inVisibleList ) const
+{
+ return isVisibleForOrDetect(u,false,inVisibleList);
+}
+
+uint32 Unit::GetCreatureType() const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(((Player*)this)->m_form);
+ if(ssEntry && ssEntry->creatureType > 0)
+ return ssEntry->creatureType;
+ else
+ return CREATURE_TYPE_HUMANOID;
+ }
+ else
+ return ((Creature*)this)->GetCreatureInfo()->type;
+}
+
+/*#######################################
+######## ########
+######## STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply)
+{
+ if(unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
+ {
+ sLog.outError("ERROR in HandleStatModifier(): nonexisted UnitMods or wrong UnitModifierType!");
+ return false;
+ }
+
+ float val = 1.0f;
+
+ switch(modifierType)
+ {
+ case BASE_VALUE:
+ case TOTAL_VALUE:
+ m_auraModifiersGroup[unitMod][modifierType] += apply ? amount : -amount;
+ break;
+ case BASE_PCT:
+ case TOTAL_PCT:
+ if(amount <= -100.0f) //small hack-fix for -100% modifiers
+ amount = -200.0f;
+
+ val = (100.0f + amount) / 100.0f;
+ m_auraModifiersGroup[unitMod][modifierType] *= apply ? val : (1.0f/val);
+ break;
+
+ default:
+ break;
+ }
+
+ if(!CanModifyStats())
+ return false;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_STAT_STRENGTH:
+ case UNIT_MOD_STAT_AGILITY:
+ case UNIT_MOD_STAT_STAMINA:
+ case UNIT_MOD_STAT_INTELLECT:
+ case UNIT_MOD_STAT_SPIRIT: UpdateStats(GetStatByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_ARMOR: UpdateArmor(); break;
+ case UNIT_MOD_HEALTH: UpdateMaxHealth(); break;
+
+ case UNIT_MOD_MANA:
+ case UNIT_MOD_RAGE:
+ case UNIT_MOD_FOCUS:
+ case UNIT_MOD_ENERGY:
+ case UNIT_MOD_HAPPINESS: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_RESISTANCE_HOLY:
+ case UNIT_MOD_RESISTANCE_FIRE:
+ case UNIT_MOD_RESISTANCE_NATURE:
+ case UNIT_MOD_RESISTANCE_FROST:
+ case UNIT_MOD_RESISTANCE_SHADOW:
+ case UNIT_MOD_RESISTANCE_ARCANE: UpdateResistances(GetSpellSchoolByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_ATTACK_POWER: UpdateAttackPowerAndDamage(); break;
+ case UNIT_MOD_ATTACK_POWER_RANGED: UpdateAttackPowerAndDamage(true); break;
+
+ case UNIT_MOD_DAMAGE_MAINHAND: UpdateDamagePhysical(BASE_ATTACK); break;
+ case UNIT_MOD_DAMAGE_OFFHAND: UpdateDamagePhysical(OFF_ATTACK); break;
+ case UNIT_MOD_DAMAGE_RANGED: UpdateDamagePhysical(RANGED_ATTACK); break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const
+{
+ if( unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted modifier value from UnitMods!");
+ return 0.0f;
+ }
+
+ if(modifierType == TOTAL_PCT && m_auraModifiersGroup[unitMod][modifierType] <= 0.0f)
+ return 0.0f;
+
+ return m_auraModifiersGroup[unitMod][modifierType];
+}
+
+float Unit::GetTotalStatValue(Stats stat) const
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_STAT_START + stat);
+
+ if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
+ return 0.0f;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = m_auraModifiersGroup[unitMod][BASE_VALUE] + GetCreateStat(stat);
+ value *= m_auraModifiersGroup[unitMod][BASE_PCT];
+ value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
+ value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
+
+ return value;
+}
+
+float Unit::GetTotalAuraModValue(UnitMods unitMod) const
+{
+ if(unitMod >= UNIT_MOD_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted UnitMods in GetTotalAuraModValue()!");
+ return 0.0f;
+ }
+
+ if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
+ return 0.0f;
+
+ float value = m_auraModifiersGroup[unitMod][BASE_VALUE];
+ value *= m_auraModifiersGroup[unitMod][BASE_PCT];
+ value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
+ value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
+
+ return value;
+}
+
+SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
+{
+ SpellSchools school = SPELL_SCHOOL_NORMAL;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_RESISTANCE_HOLY: school = SPELL_SCHOOL_HOLY; break;
+ case UNIT_MOD_RESISTANCE_FIRE: school = SPELL_SCHOOL_FIRE; break;
+ case UNIT_MOD_RESISTANCE_NATURE: school = SPELL_SCHOOL_NATURE; break;
+ case UNIT_MOD_RESISTANCE_FROST: school = SPELL_SCHOOL_FROST; break;
+ case UNIT_MOD_RESISTANCE_SHADOW: school = SPELL_SCHOOL_SHADOW; break;
+ case UNIT_MOD_RESISTANCE_ARCANE: school = SPELL_SCHOOL_ARCANE; break;
+
+ default:
+ break;
+ }
+
+ return school;
+}
+
+Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const
+{
+ Stats stat = STAT_STRENGTH;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_STAT_STRENGTH: stat = STAT_STRENGTH; break;
+ case UNIT_MOD_STAT_AGILITY: stat = STAT_AGILITY; break;
+ case UNIT_MOD_STAT_STAMINA: stat = STAT_STAMINA; break;
+ case UNIT_MOD_STAT_INTELLECT: stat = STAT_INTELLECT; break;
+ case UNIT_MOD_STAT_SPIRIT: stat = STAT_SPIRIT; break;
+
+ default:
+ break;
+ }
+
+ return stat;
+}
+
+Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
+{
+ Powers power = POWER_MANA;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_MANA: power = POWER_MANA; break;
+ case UNIT_MOD_RAGE: power = POWER_RAGE; break;
+ case UNIT_MOD_FOCUS: power = POWER_FOCUS; break;
+ case UNIT_MOD_ENERGY: power = POWER_ENERGY; break;
+ case UNIT_MOD_HAPPINESS: power = POWER_HAPPINESS; break;
+
+ default:
+ break;
+ }
+
+ return power;
+}
+
+float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
+{
+ UnitMods unitMod = (attType == RANGED_ATTACK) ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
+
+ float val = GetTotalAuraModValue(unitMod);
+ if(val < 0.0f)
+ val = 0.0f;
+
+ return val;
+}
+
+float Unit::GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const
+{
+ if (attType == OFF_ATTACK && !haveOffhandWeapon())
+ return 0.0f;
+
+ return m_weaponDamage[attType][type];
+}
+
+void Unit::SetLevel(uint32 lvl)
+{
+ SetUInt32Value(UNIT_FIELD_LEVEL, lvl);
+
+ // group update
+ if ((GetTypeId() == TYPEID_PLAYER) && ((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_LEVEL);
+}
+
+void Unit::SetHealth(uint32 val)
+{
+ uint32 maxHealth = GetMaxHealth();
+ if(maxHealth < val)
+ val = maxHealth;
+
+ SetUInt32Value(UNIT_FIELD_HEALTH, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_HP);
+ }
+ }
+}
+
+void Unit::SetMaxHealth(uint32 val)
+{
+ uint32 health = GetHealth();
+ SetUInt32Value(UNIT_FIELD_MAXHEALTH, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP);
+ }
+ }
+
+ if(val < health)
+ SetHealth(val);
+}
+
+void Unit::SetPower(Powers power, uint32 val)
+{
+ uint32 maxPower = GetMaxPower(power);
+ if(maxPower < val)
+ val = maxPower;
+
+ SetStatInt32Value(UNIT_FIELD_POWER1 + power, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
+ }
+
+ // Update the pet's character sheet with happiness damage bonus
+ if(pet->getPetType() == HUNTER_PET && power == POWER_HAPPINESS)
+ {
+ pet->UpdateDamagePhysical(BASE_ATTACK);
+ }
+ }
+}
+
+void Unit::SetMaxPower(Powers power, uint32 val)
+{
+ uint32 cur_power = GetPower(power);
+ SetStatInt32Value(UNIT_FIELD_MAXPOWER1 + power, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
+ }
+ }
+
+ if(val < cur_power)
+ SetPower(power, val);
+}
+
+void Unit::ApplyPowerMod(Powers power, uint32 val, bool apply)
+{
+ ApplyModUInt32Value(UNIT_FIELD_POWER1+power, val, apply);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
+ }
+ }
+}
+
+void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply)
+{
+ ApplyModUInt32Value(UNIT_FIELD_MAXPOWER1+power, val, apply);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
+ }
+ }
+}
+
+void Unit::ApplyAuraProcTriggerDamage( Aura* aura, bool apply )
+{
+ AuraList& tAuraProcTriggerDamage = m_modAuras[SPELL_AURA_PROC_TRIGGER_DAMAGE];
+ if(apply)
+ tAuraProcTriggerDamage.push_back(aura);
+ else
+ tAuraProcTriggerDamage.remove(aura);
+}
+
+uint32 Unit::GetCreatePowers( Powers power ) const
+{
+ // POWER_FOCUS and POWER_HAPPINESS only have hunter pet
+ switch(power)
+ {
+ case POWER_MANA: return GetCreateMana();
+ case POWER_RAGE: return 1000;
+ case POWER_FOCUS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 100);
+ case POWER_ENERGY: return 100;
+ case POWER_HAPPINESS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 1050000);
+ }
+
+ return 0;
+}
+
+void Unit::AddToWorld()
+{
+ Object::AddToWorld();
+}
+
+void Unit::RemoveFromWorld()
+{
+ // cleanup
+ if(IsInWorld())
+ {
+ RemoveNotOwnSingleTargetAuras();
+ }
+
+ Object::RemoveFromWorld();
+}
+
+void Unit::CleanupsBeforeDelete()
+{
+ if(m_uint32Values) // only for fully created object
+ {
+ InterruptNonMeleeSpells(true);
+ m_Events.KillAllEvents();
+ CombatStop();
+ ClearComboPointHolders();
+ DeleteThreatList();
+ getHostilRefManager().setOnlineOfflineState(false);
+ RemoveAllAuras();
+ RemoveAllGameObjects();
+ RemoveAllDynObjects();
+ GetMotionMaster()->Clear(false); // remove different non-standard movement generators.
+ }
+ RemoveFromWorld();
+}
+
+CharmInfo* Unit::InitCharmInfo(Unit *charm)
+{
+ if(!m_charmInfo)
+ m_charmInfo = new CharmInfo(charm);
+ return m_charmInfo;
+}
+
+CharmInfo::CharmInfo(Unit* unit)
+: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_ReactSate(REACT_PASSIVE), m_petnumber(0)
+{
+ for(int i =0; i<4; ++i)
+ {
+ m_charmspells[i].spellId = 0;
+ m_charmspells[i].active = ACT_DISABLED;
+ }
+}
+
+void CharmInfo::InitPetActionBar()
+{
+ // the first 3 SpellOrActions are attack, follow and stay
+ for(uint32 i = 0; i < 3; i++)
+ {
+ PetActionBar[i].Type = ACT_COMMAND;
+ PetActionBar[i].SpellOrAction = COMMAND_ATTACK - i;
+
+ PetActionBar[i + 7].Type = ACT_REACTION;
+ PetActionBar[i + 7].SpellOrAction = COMMAND_ATTACK - i;
+ }
+ for(uint32 i=0; i < 4; i++)
+ {
+ PetActionBar[i + 3].Type = ACT_DISABLED;
+ PetActionBar[i + 3].SpellOrAction = 0;
+ }
+}
+
+void CharmInfo::InitEmptyActionBar()
+{
+ for(uint32 x = 1; x < 10; ++x)
+ {
+ PetActionBar[x].Type = ACT_CAST;
+ PetActionBar[x].SpellOrAction = 0;
+ }
+ PetActionBar[0].Type = ACT_COMMAND;
+ PetActionBar[0].SpellOrAction = COMMAND_ATTACK;
+}
+
+void CharmInfo::InitPossessCreateSpells()
+{
+ if(m_unit->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ InitEmptyActionBar(); //charm action bar
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x]))
+ m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true);
+ else
+ AddSpellToAB(0, ((Creature*)m_unit)->m_spells[x], ACT_CAST);
+ }
+}
+
+void CharmInfo::InitCharmCreateSpells()
+{
+ if(m_unit->GetTypeId() == TYPEID_PLAYER) //charmed players don't have spells
+ {
+ InitEmptyActionBar();
+ return;
+ }
+
+ InitPetActionBar();
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ uint32 spellId = ((Creature*)m_unit)->m_spells[x];
+ m_charmspells[x].spellId = spellId;
+
+ if(!spellId)
+ continue;
+
+ if (IsPassiveSpell(spellId))
+ {
+ m_unit->CastSpell(m_unit, spellId, true);
+ m_charmspells[x].active = ACT_PASSIVE;
+ }
+ else
+ {
+ ActiveStates newstate;
+ bool onlyselfcast = true;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+
+ if(!spellInfo) onlyselfcast = false;
+ for(uint32 i = 0;i<3 && onlyselfcast;++i) //non existent spell will not make any problems as onlyselfcast would be false -> break right away
+ {
+ if(spellInfo->EffectImplicitTargetA[i] != TARGET_SELF && spellInfo->EffectImplicitTargetA[i] != 0)
+ onlyselfcast = false;
+ }
+
+ if(onlyselfcast || !IsPositiveSpell(spellId)) //only self cast and spells versus enemies are autocastable
+ newstate = ACT_DISABLED;
+ else
+ newstate = ACT_CAST;
+
+ AddSpellToAB(0, spellId, newstate);
+ }
+ }
+}
+
+bool CharmInfo::AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate)
+{
+ for(uint8 i = 0; i < 10; i++)
+ {
+ if((PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_CAST) && PetActionBar[i].SpellOrAction == oldid)
+ {
+ PetActionBar[i].SpellOrAction = newid;
+ if(!oldid)
+ {
+ if(newstate == ACT_DECIDE)
+ PetActionBar[i].Type = ACT_DISABLED;
+ else
+ PetActionBar[i].Type = newstate;
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply)
+{
+ if(IsPassiveSpell(spellid))
+ return;
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ if(spellid == m_charmspells[x].spellId)
+ {
+ m_charmspells[x].active = apply ? ACT_ENABLED : ACT_DISABLED;
+ }
+ }
+}
+
+void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
+{
+ m_petnumber = petnumber;
+ if(statwindow)
+ m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, m_petnumber);
+ else
+ m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
+}
+
+bool Unit::isFrozen() const
+{
+ AuraList const& mRoot = GetAurasByType(SPELL_AURA_MOD_ROOT);
+ for(AuraList::const_iterator i = mRoot.begin(); i != mRoot.end(); ++i)
+ if( GetSpellSchoolMask((*i)->GetSpellProto()) & SPELL_SCHOOL_MASK_FROST)
+ return true;
+ return false;
+}
+
+struct ProcTriggeredData
+{
+ ProcTriggeredData(SpellEntry const * _spellInfo, uint32 _spellParam, Aura* _triggeredByAura, uint32 _cooldown)
+ : spellInfo(_spellInfo), spellParam(_spellParam), triggeredByAura(_triggeredByAura),
+ triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())),
+ cooldown(_cooldown)
+ {}
+
+ SpellEntry const * spellInfo;
+ uint32 spellParam;
+ Aura* triggeredByAura;
+ Unit::spellEffectPair triggeredByAura_SpellPair;
+ uint32 cooldown;
+};
+
+typedef std::list< ProcTriggeredData > ProcTriggeredList;
+
+void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask )
+{
+ for(AuraTypeSet::const_iterator aur = procAuraTypes.begin(); aur != procAuraTypes.end(); ++aur)
+ {
+ // List of spells (effects) that proceed. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE)
+ ProcTriggeredList procTriggered;
+
+ AuraList const& auras = GetAurasByType(*aur);
+ for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
+ {
+ next = i; ++next;
+
+ SpellEntry const *spellProto = (*i)->GetSpellProto();
+ if(!spellProto)
+ continue;
+
+ SpellProcEventEntry const *spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
+ if(!spellProcEvent)
+ {
+ // used to prevent spam in log about same non-handled spells
+ static std::set<uint32> nonHandledSpellProcSet;
+
+ if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
+ {
+ sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
+ nonHandledSpellProcSet.insert(spellProto->Id);
+ }
+
+ // spell.dbc use totally different flags, that only can create problems if used.
+ continue;
+ }
+
+ // Check spellProcEvent data requirements
+ if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
+ continue;
+
+ // Check if current equipment allows aura to proc
+ if(!isVictim && GetTypeId() == TYPEID_PLAYER )
+ {
+ if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
+ {
+ Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
+
+ if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ continue;
+ }
+ else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
+ {
+ // Check if player is wearing shield
+ Item *item = ((Player*)this)->GetShield(true);
+ if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ continue;
+ }
+ }
+
+ float chance = (float)spellProto->procChance;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
+
+ if(!isVictim && spellProcEvent->ppmRate != 0)
+ {
+ uint32 WeaponSpeed = GetAttackTime(attType);
+ chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
+ }
+
+ if(roll_chance_f(chance))
+ {
+ uint32 cooldown = spellProcEvent->cooldown;
+
+ uint32 i_spell_eff = (*i)->GetEffIndex();
+
+ int32 i_spell_param;
+ switch(*aur)
+ {
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ i_spell_param = procFlag;
+ break;
+ case SPELL_AURA_DUMMY:
+ case SPELL_AURA_PRAYER_OF_MENDING:
+ case SPELL_AURA_MOD_HASTE:
+ i_spell_param = i_spell_eff;
+ break;
+ case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
+ i_spell_param = (*i)->GetModifier()->m_miscvalue;
+ break;
+ default:
+ i_spell_param = (*i)->GetModifier()->m_amount;
+ break;
+ }
+
+ procTriggered.push_back( ProcTriggeredData(spellProto,i_spell_param,*i, cooldown) );
+ }
+ }
+
+ // Handle effects proceed this time
+ for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
+ {
+ // Some auras can be deleted in function called in this loop (except first, ofc)
+ // Until storing auras in std::multimap to hard check deleting by another way
+ if(i != procTriggered.begin())
+ {
+ bool found = false;
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second==i->triggeredByAura)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ {
+ sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",*aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second);
+ sLog.outError("It can be deleted one from early proccesed auras:");
+ for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2)
+ sLog.outError(" Spell aura %u (id:%u effect:%u)",*aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second);
+ sLog.outError(" <end of list>");
+ continue;
+ }
+ }
+
+ // save charges existence before processing to prevent crash at access to deleted triggered aura after
+ bool triggeredByAuraWithCharges = i->triggeredByAura->m_procCharges > 0;
+
+ bool casted = false;
+ switch(*aur)
+ {
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell,i->spellParam,attType,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_PROC_TRIGGER_DAMAGE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", i->spellParam, i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ uint32 damage = i->spellParam;
+ SpellNonMeleeDamageLog(pTarget, i->spellInfo->Id, damage, true, true);
+ casted = true;
+ break;
+ }
+ case SPELL_AURA_DUMMY:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_PRAYER_OF_MENDING:
+ {
+ sLog.outDebug("ProcDamageAndSpell(mending): casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+
+ // aura can be deleted at casts
+ int32 heal = i->triggeredByAura->GetModifier()->m_amount;
+ uint64 caster_guid = i->triggeredByAura->GetCasterGUID();
+
+ // jumps
+ int32 jumps = i->triggeredByAura->m_procCharges-1;
+
+ // current aura expire
+ i->triggeredByAura->m_procCharges = 1; // will removed at next charges decrease
+
+ // next target selection
+ if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid))
+ {
+ Aura* aura = i->triggeredByAura;
+
+ SpellEntry const* spellProto = aura->GetSpellProto();
+ uint32 effIdx = aura->GetEffIndex();
+
+ float radius;
+ if (spellProto->EffectRadiusIndex[effIdx])
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
+ else
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
+
+ if(Player* caster = ((Player*)aura->GetCaster()))
+ {
+ caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
+
+ if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius))
+ {
+ // aura will applied from caster, but spell casted from current aura holder
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_CHARGES;
+ mod->value = jumps-5; // negative
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = spellProto->Id;
+ mod->effectId = effIdx;
+ mod->lastAffected = NULL;
+ mod->mask = spellProto->SpellFamilyFlags;
+ mod->charges = 0;
+
+ caster->AddSpellMod(mod, true);
+ CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,aura,caster->GetGUID());
+ caster->AddSpellMod(mod, false);
+ }
+ }
+ }
+
+ // heal
+ CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
+ casted = true;
+ break;
+ }
+ case SPELL_AURA_MOD_HASTE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleHasteAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
+ {
+ // nothing do, just charges counter
+ // but count only in case appropriate school damage
+ casted = i->triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask;
+ break;
+ }
+ case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleOverrideClassScriptAuraProc(pTarget, i->spellParam, damage, i->triggeredByAura, procSpell,i->cooldown);
+ break;
+ }
+ default:
+ {
+ // nothing do, just charges counter
+ casted = true;
+ break;
+ }
+ }
+
+ // Update charge (aura can be removed by triggers)
+ if(casted && triggeredByAuraWithCharges)
+ {
+ // need found aura (can be dropped by triggers)
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second == i->triggeredByAura)
+ {
+ if(i->triggeredByAura->m_procCharges > 0)
+ i->triggeredByAura->m_procCharges -= 1;
+
+ i->triggeredByAura->UpdateAuraCharges();
+ break;
+ }
+ }
+ }
+ }
+
+ // Safely remove auras with zero charges
+ for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
+ {
+ next = i; ++next;
+ if((*i)->m_procCharges == 0)
+ {
+ RemoveAurasDueToSpell((*i)->GetId());
+ next = auras.begin();
+ }
+ }
+ }
+}
+
+SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
+{
+ return SPELL_SCHOOL_MASK_NORMAL;
+}
+
+Player* Unit::GetSpellModOwner()
+{
+ if(GetTypeId()==TYPEID_PLAYER)
+ return (Player*)this;
+ if(((Creature*)this)->isPet() || ((Creature*)this)->isTotem())
+ {
+ Unit* owner = GetOwner();
+ if(owner && owner->GetTypeId()==TYPEID_PLAYER)
+ return (Player*)owner;
+ }
+ return NULL;
+}
+
+///----------Pet responses methods-----------------
+void Unit::SendPetCastFail(uint32 spellid, uint8 msg)
+{
+ Unit *owner = GetCharmerOrOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_CAST_FAILED, (4+1));
+ data << uint32(spellid);
+ data << uint8(msg);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetActionFeedback (uint8 msg)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_ACTION_FEEDBACK, 1);
+ data << uint8(msg);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetTalk (uint32 pettalk)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_ACTION_SOUND, 8+4);
+ data << uint64(GetGUID());
+ data << 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);
+ 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();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_AI_REACTION, 12);
+ data << uint64(guid) << uint32(00000002);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+///----------End of Pet responses methods----------
+
+void Unit::StopMoving()
+{
+ clearUnitState(UNIT_STAT_MOVING);
+
+ // send explicit stop packet
+ // rely on vmaps here because for exemple stormwind is in air
+ float z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ(), true);
+ //if (fabs(GetPositionZ() - z) < 2.0f)
+ // Relocate(GetPositionX(), GetPositionY(), z);
+ Relocate(GetPositionX(), GetPositionY(),GetPositionZ());
+
+ SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), 0, true, 0);
+
+ // update position and orientation;
+ WorldPacket data;
+ BuildHeartBeatMsg(&data);
+ SendMessageToSet(&data,false);
+}
+
+void Unit::SetFeared(bool apply, uint64 casterGUID, uint32 spellID)
+{
+ if( apply )
+ {
+ if(HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
+ return;
+
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+
+ GetMotionMaster()->MovementExpired(false);
+ CastStop(GetGUID()==casterGUID ? spellID : 0);
+
+ Unit* caster = ObjectAccessor::GetUnit(*this,casterGUID);
+
+ GetMotionMaster()->MoveFleeing(caster); // caster==NULL processed in MoveFleeing
+ }
+ else
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+
+ GetMotionMaster()->MovementExpired(false);
+
+ if( GetTypeId() != TYPEID_PLAYER && isAlive() )
+ {
+ // restore appropriate movement generator
+ if(getVictim())
+ GetMotionMaster()->MoveChase(getVictim());
+ else
+ GetMotionMaster()->Initialize();
+
+ // attack caster if can
+ Unit* caster = ObjectAccessor::GetObjectInWorld(casterGUID, (Unit*)NULL);
+ if(caster && caster != getVictim() && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(caster);
+ }
+ }
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->SetClientControl(this, !apply);
+}
+
+void Unit::SetConfused(bool apply, uint64 casterGUID, uint32 spellID)
+{
+ if( apply )
+ {
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+
+ CastStop(GetGUID()==casterGUID ? spellID : 0);
+
+ GetMotionMaster()->MoveConfused();
+ }
+ else
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+
+ GetMotionMaster()->MovementExpired(false);
+
+ if (GetTypeId() == TYPEID_UNIT)
+ {
+ // if in combat restore movement generator
+ if(getVictim())
+ GetMotionMaster()->MoveChase(getVictim());
+ }
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->SetClientControl(this, !apply);
+}
+
+bool Unit::IsSitState() const
+{
+ uint8 s = getStandState();
+ return s == PLAYER_STATE_SIT_CHAIR || s == PLAYER_STATE_SIT_LOW_CHAIR ||
+ s == PLAYER_STATE_SIT_MEDIUM_CHAIR || s == PLAYER_STATE_SIT_HIGH_CHAIR ||
+ s == PLAYER_STATE_SIT;
+}
+
+bool Unit::IsStandState() const
+{
+ uint8 s = getStandState();
+ return !IsSitState() && s != PLAYER_STATE_SLEEP && s != PLAYER_STATE_KNEEL;
+}
+
+void Unit::SetStandState(uint8 state)
+{
+ SetByteValue(UNIT_FIELD_BYTES_1, 0, state);
+
+ if (IsStandState())
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_SEATED);
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_STANDSTATE_UPDATE, 1);
+ data << (uint8)state;
+ ((Player*)this)->GetSession()->SendPacket(&data);
+ }
+}
+
+bool Unit::IsPolymorphed() const
+{
+ return GetSpellSpecific(getTransForm())==SPELL_MAGE_POLYMORPH;
+}
+
+void Unit::SetDisplayId(uint32 modelId)
+{
+ SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
+
+ if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(!pet->isControlled())
+ return;
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID);
+ }
+}
+
+void Unit::ClearComboPointHolders()
+{
+ while(!m_ComboPointHolders.empty())
+ {
+ uint32 lowguid = *m_ComboPointHolders.begin();
+
+ Player* plr = objmgr.GetPlayer(MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER));
+ if(plr && plr->GetComboTarget()==GetGUID()) // recheck for safe
+ plr->ClearComboPoints(); // remove also guid from m_ComboPointHolders;
+ else
+ m_ComboPointHolders.erase(lowguid); // or remove manually
+ }
+}
+
+void Unit::ClearAllReactives()
+{
+
+ for(int i=0; i < MAX_REACTIVE; ++i)
+ m_reactiveTimer[i] = 0;
+
+ if (HasAuraState( AURA_STATE_DEFENSE))
+ ModifyAuraState(AURA_STATE_DEFENSE, false);
+ if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_PARRY))
+ ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
+ if (HasAuraState( AURA_STATE_CRIT))
+ ModifyAuraState(AURA_STATE_CRIT, false);
+ if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_CRIT_STRIKE) )
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
+
+ if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->ClearComboPoints();
+}
+
+void Unit::UpdateReactives( uint32 p_time )
+{
+ for(int i = 0; i < MAX_REACTIVE; ++i)
+ {
+ ReactiveType reactive = ReactiveType(i);
+
+ if(!m_reactiveTimer[reactive])
+ continue;
+
+ if ( m_reactiveTimer[reactive] <= p_time)
+ {
+ m_reactiveTimer[reactive] = 0;
+
+ switch ( reactive )
+ {
+ case REACTIVE_DEFENSE:
+ if (HasAuraState(AURA_STATE_DEFENSE))
+ ModifyAuraState(AURA_STATE_DEFENSE, false);
+ break;
+ case REACTIVE_HUNTER_PARRY:
+ if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY))
+ ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
+ break;
+ case REACTIVE_CRIT:
+ if (HasAuraState(AURA_STATE_CRIT))
+ ModifyAuraState(AURA_STATE_CRIT, false);
+ break;
+ case REACTIVE_HUNTER_CRIT:
+ if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_CRIT_STRIKE) )
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
+ break;
+ case REACTIVE_OVERPOWER:
+ if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->ClearComboPoints();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ m_reactiveTimer[reactive] -= p_time;
+ }
+ }
+}
+
+Unit* Unit::SelectNearbyTarget() const
+{
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Unit *> targets;
+
+ {
+ MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, ATTACK_DISTANCE);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // remove current target
+ if(getVictim())
+ targets.remove(getVictim());
+
+ // remove not LoS targets
+ for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();)
+ {
+ if(!IsWithinLOSInMap(*tIter))
+ {
+ std::list<Unit *>::iterator tIter2 = tIter;
+ ++tIter;
+ targets.erase(tIter2);
+ }
+ else
+ ++tIter;
+ }
+
+ // no appropriate targets
+ if(targets.empty())
+ return NULL;
+
+ // select random
+ uint32 rIdx = urand(0,targets.size()-1);
+ std::list<Unit *>::const_iterator tcIter = targets.begin();
+ for(uint32 i = 0; i < rIdx; ++i)
+ ++tcIter;
+
+ return *tcIter;
+}
+
+void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply )
+{
+ if(val > 0)
+ {
+ ApplyPercentModFloatVar(m_modAttackSpeedPct[att], val, !apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val,!apply);
+ }
+ else
+ {
+ ApplyPercentModFloatVar(m_modAttackSpeedPct[att], -val, apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,-val,apply);
+ }
+}
+
+void Unit::ApplyCastTimePercentMod(float val, bool apply )
+{
+ if(val > 0)
+ ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,val,!apply);
+ else
+ ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,-val,apply);
+}
+
+uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime )
+{
+ if (CastingTime > 7000) CastingTime = 7000;
+ if (CastingTime < 1500) CastingTime = 1500;
+
+ if(damagetype == DOT && !IsChanneledSpell(spellProto))
+ CastingTime = 3500;
+
+ int32 overTime = 0;
+ uint8 effects = 0;
+ bool DirectDamage = false;
+ bool AreaEffect = false;
+
+ for ( uint32 i=0; i<3;i++)
+ {
+ switch ( spellProto->Effect[i] )
+ {
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ case SPELL_EFFECT_POWER_DRAIN:
+ case SPELL_EFFECT_HEALTH_LEECH:
+ case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
+ case SPELL_EFFECT_POWER_BURN:
+ case SPELL_EFFECT_HEAL:
+ DirectDamage = true;
+ break;
+ case SPELL_EFFECT_APPLY_AURA:
+ switch ( spellProto->EffectApplyAuraName[i] )
+ {
+ case SPELL_AURA_PERIODIC_DAMAGE:
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_PERIODIC_LEECH:
+ if ( GetSpellDuration(spellProto) )
+ overTime = GetSpellDuration(spellProto);
+ break;
+ default:
+ // -5% per additional effect
+ ++effects;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if(IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetA[i])) || IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetB[i])))
+ AreaEffect = true;
+ }
+
+ // Combined Spells with Both Over Time and Direct Damage
+ if ( overTime > 0 && CastingTime > 0 && DirectDamage )
+ {
+ // mainly for DoTs which are 3500 here otherwise
+ uint32 OriginalCastTime = GetSpellCastTime(spellProto);
+ if (OriginalCastTime > 7000) OriginalCastTime = 7000;
+ if (OriginalCastTime < 1500) OriginalCastTime = 1500;
+ // Portion to Over Time
+ float PtOT = (overTime / 15000.f) / ((overTime / 15000.f) + (OriginalCastTime / 3500.f));
+
+ if ( damagetype == DOT )
+ CastingTime = uint32(CastingTime * PtOT);
+ else if ( PtOT < 1.0f )
+ CastingTime = uint32(CastingTime * (1 - PtOT));
+ else
+ CastingTime = 0;
+ }
+
+ // Area Effect Spells receive only half of bonus
+ if ( AreaEffect )
+ CastingTime /= 2;
+
+ // -5% of total per any additional effect
+ for ( uint8 i=0; i<effects; ++i)
+ {
+ if ( CastingTime > 175 )
+ {
+ CastingTime -= 175;
+ }
+ else
+ {
+ CastingTime = 0;
+ break;
+ }
+ }
+
+ return CastingTime;
+}
+
+void Unit::UpdateAuraForGroup(uint8 slot)
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Player* player = (Player*)this;
+ if(player->GetGroup())
+ {
+ player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_AURAS);
+ player->SetAuraUpdateMask(slot);
+ }
+ }
+ else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ {
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_AURAS);
+ pet->SetAuraUpdateMask(slot);
+ }
+ }
+ }
+}
+
+float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized)
+{
+ if (!normalized || GetTypeId() != TYPEID_PLAYER)
+ return float(GetAttackTime(attType))/1000.0f;
+
+ Item *Weapon = ((Player*)this)->GetWeaponForAttack(attType);
+ if (!Weapon)
+ return 2.4; // fist attack
+
+ switch (Weapon->GetProto()->InventoryType)
+ {
+ case INVTYPE_2HWEAPON:
+ return 3.3;
+ case INVTYPE_RANGED:
+ case INVTYPE_RANGEDRIGHT:
+ case INVTYPE_THROWN:
+ return 2.8;
+ case INVTYPE_WEAPON:
+ case INVTYPE_WEAPONMAINHAND:
+ case INVTYPE_WEAPONOFFHAND:
+ default:
+ return Weapon->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7 : 2.4;
+ }
+}
+
+Aura* Unit::GetDummyAura( uint32 spell_id ) const
+{
+ Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
+ if ((*itr)->GetId() == spell_id)
+ return *itr;
+
+ return NULL;
+}
+
+bool Unit::IsUnderLastManaUseEffect() const
+{
+ return getMSTimeDiff(m_lastManaUse,getMSTime()) < 5000;
+}
+
+void Unit::SetContestedPvP(Player *attackedPlayer)
+{
+ Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ if(!player || attackedPlayer && (attackedPlayer == player || player->duel && player->duel->opponent == attackedPlayer))
+ return;
+
+ player->SetContestedPvPTimer(30000);
+ if(!player->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ {
+ player->addUnitState(UNIT_STAT_ATTACK_PLAYER);
+ player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
+ // call MoveInLineOfSight for nearby contested guards
+ SetVisibility(GetVisibility());
+ }
+ if(!hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ {
+ addUnitState(UNIT_STAT_ATTACK_PLAYER);
+ // call MoveInLineOfSight for nearby contested guards
+ SetVisibility(GetVisibility());
+ }
+}
+
+void Unit::AddPetAura(PetAura const* petSpell)
+{
+ m_petAuras.insert(petSpell);
+ if(Pet* pet = GetPet())
+ pet->CastPetAura(petSpell);
+}
+
+void Unit::RemovePetAura(PetAura const* petSpell)
+{
+ m_petAuras.erase(petSpell);
+ if(Pet* pet = GetPet())
+ pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry()));
+}
diff --git a/src/game/Unit.h b/src/game/Unit.h
new file mode 100644
index 00000000000..b16b5e9ab2d
--- /dev/null
+++ b/src/game/Unit.h
@@ -0,0 +1,1331 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __UNIT_H
+#define __UNIT_H
+
+#include "Common.h"
+#include "Object.h"
+#include "Opcodes.h"
+#include "Mthread.h"
+#include "SpellAuraDefines.h"
+#include "UpdateFields.h"
+#include "SharedDefines.h"
+#include "ThreatManager.h"
+#include "HostilRefManager.h"
+#include "FollowerReference.h"
+#include "FollowerRefManager.h"
+#include "Utilities/EventProcessor.h"
+#include "MotionMaster.h"
+#include "Database/DBCStructure.h"
+#include <list>
+
+enum SpellInterruptFlags
+{
+ SPELL_INTERRUPT_FLAG_MOVEMENT = 0x01,
+ SPELL_INTERRUPT_FLAG_DAMAGE = 0x02,
+ SPELL_INTERRUPT_FLAG_INTERRUPT = 0x04,
+ SPELL_INTERRUPT_FLAG_AUTOATTACK = 0x08,
+ //SPELL_INTERRUPT_FLAG_TURNING = 0x10 // not turning - maybe _complete_ interrupt on direct damage?
+};
+
+enum SpellChannelInterruptFlags
+{
+ CHANNEL_FLAG_DAMAGE = 0x0002,
+ CHANNEL_FLAG_MOVEMENT = 0x0008,
+ CHANNEL_FLAG_TURNING = 0x0010,
+ CHANNEL_FLAG_DAMAGE2 = 0x0080,
+ CHANNEL_FLAG_DELAY = 0x4000
+};
+
+enum SpellAuraInterruptFlags
+{
+ AURA_INTERRUPT_FLAG_UNK0 = 0x00000001, // 0 removed when getting hit by a negative spell?
+ AURA_INTERRUPT_FLAG_DAMAGE = 0x00000002, // 1 removed by any damage
+ AURA_INTERRUPT_FLAG_UNK2 = 0x00000004, // 2
+ AURA_INTERRUPT_FLAG_MOVE = 0x00000008, // 3 removed by any movement
+ AURA_INTERRUPT_FLAG_TURNING = 0x00000010, // 4 removed by any turning
+ AURA_INTERRUPT_FLAG_ENTER_COMBAT = 0x00000020, // 5 removed by entering combat
+ AURA_INTERRUPT_FLAG_NOT_MOUNTED = 0x00000040, // 6 removed by unmounting
+ AURA_INTERRUPT_FLAG_NOT_ABOVEWATER = 0x00000080, // 7 removed by entering water
+ AURA_INTERRUPT_FLAG_NOT_UNDERWATER = 0x00000100, // 8 removed by leaving water
+ AURA_INTERRUPT_FLAG_NOT_SHEATHED = 0x00000200, // 9 removed by unsheathing
+ AURA_INTERRUPT_FLAG_UNK10 = 0x00000400, // 10
+ AURA_INTERRUPT_FLAG_UNK11 = 0x00000800, // 11
+ AURA_INTERRUPT_FLAG_UNK12 = 0x00001000, // 12 removed by attack?
+ AURA_INTERRUPT_FLAG_UNK13 = 0x00002000, // 13
+ AURA_INTERRUPT_FLAG_UNK14 = 0x00004000, // 14
+ AURA_INTERRUPT_FLAG_UNK15 = 0x00008000, // 15 removed by casting a spell?
+ AURA_INTERRUPT_FLAG_UNK16 = 0x00010000, // 16
+ AURA_INTERRUPT_FLAG_MOUNTING = 0x00020000, // 17 removed by mounting
+ AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up
+ AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported
+ AURA_INTERRUPT_FLAG_UNK20 = 0x00100000, // 20
+ AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21
+ AURA_INTERRUPT_FLAG_UNK22 = 0x00400000, // 22
+ AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat
+ AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000 // 24 removed by any direct damage
+};
+
+enum SpellModOp
+{
+ SPELLMOD_DAMAGE = 0,
+ SPELLMOD_DURATION = 1,
+ SPELLMOD_THREAT = 2,
+ SPELLMOD_EFFECT1 = 3,
+ SPELLMOD_CHARGES = 4,
+ SPELLMOD_RANGE = 5,
+ SPELLMOD_RADIUS = 6,
+ SPELLMOD_CRITICAL_CHANCE = 7,
+ SPELLMOD_ALL_EFFECTS = 8,
+ SPELLMOD_NOT_LOSE_CASTING_TIME = 9,
+ SPELLMOD_CASTING_TIME = 10,
+ SPELLMOD_COOLDOWN = 11,
+ SPELLMOD_EFFECT2 = 12,
+ // spellmod 13 unused
+ SPELLMOD_COST = 14,
+ SPELLMOD_CRIT_DAMAGE_BONUS = 15,
+ SPELLMOD_RESIST_MISS_CHANCE = 16,
+ SPELLMOD_JUMP_TARGETS = 17,
+ SPELLMOD_CHANCE_OF_SUCCESS = 18,
+ SPELLMOD_ACTIVATION_TIME = 19,
+ SPELLMOD_EFFECT_PAST_FIRST = 20,
+ SPELLMOD_CASTING_TIME_OLD = 21,
+ SPELLMOD_DOT = 22,
+ SPELLMOD_EFFECT3 = 23,
+ SPELLMOD_SPELL_BONUS_DAMAGE = 24,
+ // spellmod 25, 26 unused
+ SPELLMOD_MULTIPLE_VALUE = 27,
+ SPELLMOD_RESIST_DISPEL_CHANCE = 28
+};
+
+#define MAX_SPELLMOD 32
+
+#define BASE_MINDAMAGE 1.0f
+#define BASE_MAXDAMAGE 2.0f
+#define BASE_ATTACK_TIME 2000
+
+// high byte (3 from 0..3) of UNIT_FIELD_BYTES_2
+enum ShapeshiftForm
+{
+ FORM_NONE = 0x00,
+ FORM_CAT = 0x01,
+ FORM_TREE = 0x02,
+ FORM_TRAVEL = 0x03,
+ FORM_AQUA = 0x04,
+ FORM_BEAR = 0x05,
+ FORM_AMBIENT = 0x06,
+ FORM_GHOUL = 0x07,
+ FORM_DIREBEAR = 0x08,
+ FORM_CREATUREBEAR = 0x0E,
+ FORM_CREATURECAT = 0x0F,
+ FORM_GHOSTWOLF = 0x10,
+ FORM_BATTLESTANCE = 0x11,
+ FORM_DEFENSIVESTANCE = 0x12,
+ FORM_BERSERKERSTANCE = 0x13,
+ FORM_TEST = 0x14,
+ FORM_ZOMBIE = 0x15,
+ FORM_FLIGHT_EPIC = 0x1B,
+ FORM_SHADOW = 0x1C,
+ FORM_FLIGHT = 0x1D,
+ FORM_STEALTH = 0x1E,
+ FORM_MOONKIN = 0x1F,
+ FORM_SPIRITOFREDEMPTION = 0x20
+};
+
+// low byte ( 0 from 0..3 ) of UNIT_FIELD_BYTES_2
+enum SheathState
+{
+ SHEATH_STATE_UNARMED = 0, // non prepared weapon
+ SHEATH_STATE_MELEE = 1, // prepared melee weapon
+ SHEATH_STATE_RANGED = 2 // prepared ranged weapon
+};
+
+// byte (1 from 0..3) of UNIT_FIELD_BYTES_2
+enum UnitBytes2_Flags
+{
+ UNIT_BYTE2_FLAG_UNK0 = 0x01,
+ UNIT_BYTE2_FLAG_UNK1 = 0x02,
+ UNIT_BYTE2_FLAG_UNK2 = 0x04,
+ UNIT_BYTE2_FLAG_UNK3 = 0x08,
+ UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel
+ UNIT_BYTE2_FLAG_UNK5 = 0x20,
+ UNIT_BYTE2_FLAG_UNK6 = 0x40,
+ UNIT_BYTE2_FLAG_UNK7 = 0x80
+};
+
+// byte (2 from 0..3) of UNIT_FIELD_BYTES_2
+enum UnitRename
+{
+ UNIT_RENAME_NOT_ALLOWED = 0x02,
+ UNIT_RENAME_ALLOWED = 0x03
+};
+
+#define CREATURE_MAX_SPELLS 4
+
+enum Swing
+{
+ NOSWING = 0,
+ SINGLEHANDEDSWING = 1,
+ TWOHANDEDSWING = 2
+};
+
+enum VictimState
+{
+ VICTIMSTATE_UNKNOWN1 = 0,
+ VICTIMSTATE_NORMAL = 1,
+ VICTIMSTATE_DODGE = 2,
+ VICTIMSTATE_PARRY = 3,
+ VICTIMSTATE_INTERRUPT = 4,
+ VICTIMSTATE_BLOCKS = 5,
+ VICTIMSTATE_EVADES = 6,
+ VICTIMSTATE_IS_IMMUNE = 7,
+ VICTIMSTATE_DEFLECTS = 8
+};
+
+enum HitInfo
+{
+ HITINFO_NORMALSWING = 0x00000000,
+ HITINFO_UNK1 = 0x00000001, // req correct packet structure
+ HITINFO_NORMALSWING2 = 0x00000002,
+ HITINFO_LEFTSWING = 0x00000004,
+ HITINFO_MISS = 0x00000010,
+ HITINFO_ABSORB = 0x00000020, // plays absorb sound
+ HITINFO_RESIST = 0x00000040, // resisted atleast some damage
+ HITINFO_CRITICALHIT = 0x00000080,
+ HITINFO_GLANCING = 0x00004000,
+ HITINFO_CRUSHING = 0x00008000,
+ HITINFO_NOACTION = 0x00010000,
+ HITINFO_SWINGNOHITSOUND = 0x00080000
+};
+
+//i would like to remove this: (it is defined in item.h
+enum InventorySlot
+{
+ NULL_BAG = 0,
+ NULL_SLOT = 255
+};
+
+struct FactionTemplateEntry;
+struct Modifier;
+struct SpellEntry;
+struct SpellEntryExt;
+
+class Aura;
+class Creature;
+class Spell;
+class DynamicObject;
+class GameObject;
+class Item;
+class Pet;
+class Path;
+class PetAura;
+
+struct SpellImmune
+{
+ uint32 type;
+ uint32 spellId;
+};
+
+typedef std::list<SpellImmune> SpellImmuneList;
+
+enum UnitModifierType
+{
+ BASE_VALUE = 0,
+ BASE_PCT = 1,
+ TOTAL_VALUE = 2,
+ TOTAL_PCT = 3,
+ MODIFIER_TYPE_END = 4
+};
+
+enum WeaponDamageRange
+{
+ MINDAMAGE,
+ MAXDAMAGE
+};
+
+enum DamageTypeToSchool
+{
+ RESISTANCE,
+ DAMAGE_DEALT,
+ DAMAGE_TAKEN
+};
+
+enum AuraRemoveMode
+{
+ AURA_REMOVE_BY_DEFAULT,
+ AURA_REMOVE_BY_STACK, // at replace by semillar aura
+ AURA_REMOVE_BY_CANCEL,
+ AURA_REMOVE_BY_DISPEL,
+ AURA_REMOVE_BY_DEATH
+};
+
+enum UnitMods
+{
+ UNIT_MOD_STAT_STRENGTH, // UNIT_MOD_STAT_STRENGTH..UNIT_MOD_STAT_SPIRIT must be in existed order, it's accessed by index values of Stats enum.
+ UNIT_MOD_STAT_AGILITY,
+ UNIT_MOD_STAT_STAMINA,
+ UNIT_MOD_STAT_INTELLECT,
+ UNIT_MOD_STAT_SPIRIT,
+ UNIT_MOD_HEALTH,
+ UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_HAPPINESS must be in existed order, it's accessed by index values of Powers enum.
+ UNIT_MOD_RAGE,
+ UNIT_MOD_FOCUS,
+ UNIT_MOD_ENERGY,
+ UNIT_MOD_HAPPINESS,
+ UNIT_MOD_ARMOR, // UNIT_MOD_ARMOR..UNIT_MOD_RESISTANCE_ARCANE must be in existed order, it's accessed by index values of SpellSchools enum.
+ UNIT_MOD_RESISTANCE_HOLY,
+ UNIT_MOD_RESISTANCE_FIRE,
+ UNIT_MOD_RESISTANCE_NATURE,
+ UNIT_MOD_RESISTANCE_FROST,
+ UNIT_MOD_RESISTANCE_SHADOW,
+ UNIT_MOD_RESISTANCE_ARCANE,
+ UNIT_MOD_ATTACK_POWER,
+ UNIT_MOD_ATTACK_POWER_RANGED,
+ UNIT_MOD_DAMAGE_MAINHAND,
+ UNIT_MOD_DAMAGE_OFFHAND,
+ UNIT_MOD_DAMAGE_RANGED,
+ UNIT_MOD_END,
+ // synonyms
+ UNIT_MOD_STAT_START = UNIT_MOD_STAT_STRENGTH,
+ UNIT_MOD_STAT_END = UNIT_MOD_STAT_SPIRIT + 1,
+ UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR,
+ UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1,
+ UNIT_MOD_POWER_START = UNIT_MOD_MANA,
+ UNIT_MOD_POWER_END = UNIT_MOD_HAPPINESS + 1
+};
+
+enum BaseModGroup
+{
+ CRIT_PERCENTAGE,
+ RANGED_CRIT_PERCENTAGE,
+ OFFHAND_CRIT_PERCENTAGE,
+ SHIELD_BLOCK_VALUE,
+ BASEMOD_END
+};
+
+enum BaseModType
+{
+ FLAT_MOD,
+ PCT_MOD
+};
+
+#define MOD_END (PCT_MOD+1)
+
+enum DeathState
+{
+ ALIVE = 0,
+ JUST_DIED = 1,
+ CORPSE = 2,
+ DEAD = 3,
+ JUST_ALIVED = 4
+};
+
+enum UnitState
+{
+ UNIT_STAT_DIED = 0x0001,
+ UNIT_STAT_MELEE_ATTACKING = 0x0002, // player is melee attacking someone
+ //UNIT_STAT_MELEE_ATTACK_BY = 0x0004, // player is melee attack by someone
+ UNIT_STAT_STUNDED = 0x0008,
+ UNIT_STAT_ROAMING = 0x0010,
+ UNIT_STAT_CHASE = 0x0020,
+ UNIT_STAT_SEARCHING = 0x0040,
+ UNIT_STAT_FLEEING = 0x0080,
+ UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING),
+ UNIT_STAT_IN_FLIGHT = 0x0100, // player is in flight mode
+ UNIT_STAT_FOLLOW = 0x0200,
+ UNIT_STAT_ROOT = 0x0400,
+ UNIT_STAT_CONFUSED = 0x0800,
+ UNIT_STAT_DISTRACTED = 0x1000,
+ UNIT_STAT_ISOLATED = 0x2000, // area auras do not affect other players
+ UNIT_STAT_ATTACK_PLAYER = 0x4000,
+ UNIT_STAT_ALL_STATE = 0xffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT)
+};
+
+enum UnitMoveType
+{
+ MOVE_WALK = 0,
+ MOVE_RUN = 1,
+ MOVE_WALKBACK = 2,
+ MOVE_SWIM = 3,
+ MOVE_SWIMBACK = 4,
+ MOVE_TURN = 5,
+ MOVE_FLY = 6,
+ MOVE_FLYBACK = 7
+};
+
+#define MAX_MOVE_TYPE 8
+
+extern float baseMoveSpeed[MAX_MOVE_TYPE];
+
+enum WeaponAttackType
+{
+ BASE_ATTACK = 0,
+ OFF_ATTACK = 1,
+ RANGED_ATTACK = 2
+};
+
+#define MAX_ATTACK 3
+
+enum CombatRating
+{
+ CR_WEAPON_SKILL = 0,
+ CR_DEFENSE_SKILL = 1,
+ CR_DODGE = 2,
+ CR_PARRY = 3,
+ CR_BLOCK = 4,
+ CR_HIT_MELEE = 5,
+ CR_HIT_RANGED = 6,
+ CR_HIT_SPELL = 7,
+ CR_CRIT_MELEE = 8,
+ CR_CRIT_RANGED = 9,
+ CR_CRIT_SPELL = 10,
+ CR_HIT_TAKEN_MELEE = 11,
+ CR_HIT_TAKEN_RANGED = 12,
+ CR_HIT_TAKEN_SPELL = 13,
+ CR_CRIT_TAKEN_MELEE = 14,
+ CR_CRIT_TAKEN_RANGED = 15,
+ CR_CRIT_TAKEN_SPELL = 16,
+ CR_HASTE_MELEE = 17,
+ CR_HASTE_RANGED = 18,
+ CR_HASTE_SPELL = 19,
+ CR_WEAPON_SKILL_MAINHAND = 20,
+ CR_WEAPON_SKILL_OFFHAND = 21,
+ CR_WEAPON_SKILL_RANGED = 22,
+ CR_EXPERTISE = 23
+};
+
+#define MAX_COMBAT_RATING 24
+
+enum DamageEffectType
+{
+ DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells)
+ SPELL_DIRECT_DAMAGE = 1, // spell/class abilities damage
+ DOT = 2,
+ HEAL = 3,
+ NODAMAGE = 4, // used also in case when damage applied to health but not applied to spell channelInterruptFlags/etc
+ SELF_DAMAGE = 5
+};
+
+enum UnitVisibility
+{
+ VISIBILITY_OFF = 0, // absolute, not detectable, GM-like, can see all other
+ VISIBILITY_ON = 1,
+ VISIBILITY_GROUP_STEALTH = 2, // detect chance, seen and can see group members
+ VISIBILITY_GROUP_INVISIBILITY = 3, // invisibility, can see and can be seen only another invisible unit or invisible detection unit, set only if not stealthed, and in checks not used (mask used instead)
+ VISIBILITY_GROUP_NO_DETECT = 4, // state just at stealth apply for update Grid state. Don't remove, otherwise stealth spells will break
+ VISIBILITY_RESPAWN = 5 // special totally not detectable visibility for force delete object at respawn command
+};
+
+// Value masks for UNIT_FIELD_FLAGS
+enum UnitFlags
+{
+ UNIT_FLAG_UNKNOWN7 = 0x00000001,
+ UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable
+ UNIT_FLAG_DISABLE_MOVE = 0x00000004,
+ UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state
+ UNIT_FLAG_RENAME = 0x00000010,
+ UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
+ UNIT_FLAG_UNKNOWN9 = 0x00000040,
+ UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE
+ UNIT_FLAG_UNKNOWN2 = 0x00000100, // 2.0.8
+ UNIT_FLAG_UNKNOWN11 = 0x00000200,
+ UNIT_FLAG_LOOTING = 0x00000400, // loot animation
+ UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
+ UNIT_FLAG_PVP = 0x00001000,
+ UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
+ UNIT_FLAG_UNKNOWN4 = 0x00004000, // 2.0.8
+ UNIT_FLAG_UNKNOWN13 = 0x00008000,
+ UNIT_FLAG_UNKNOWN14 = 0x00010000,
+ UNIT_FLAG_PACIFIED = 0x00020000,
+ UNIT_FLAG_DISABLE_ROTATE = 0x00040000, // stunned, 2.1.1
+ UNIT_FLAG_IN_COMBAT = 0x00080000,
+ UNIT_FLAG_TAXI_FLIGHT = 0x00100000, // disable casting at client side spell not allowed by taxi flight (mounted?), probably used with 0x4 flag
+ UNIT_FLAG_DISARMED = 0x00200000, // disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
+ UNIT_FLAG_CONFUSED = 0x00400000,
+ UNIT_FLAG_FLEEING = 0x00800000,
+ UNIT_FLAG_UNKNOWN5 = 0x01000000, // used in spell Eyes of the Beast for pet...
+ UNIT_FLAG_NOT_SELECTABLE = 0x02000000,
+ UNIT_FLAG_SKINNABLE = 0x04000000,
+ UNIT_FLAG_MOUNT = 0x08000000,
+ UNIT_FLAG_UNKNOWN17 = 0x10000000,
+ UNIT_FLAG_UNKNOWN6 = 0x20000000, // used in Feing Death spell
+ UNIT_FLAG_SHEATHE = 0x40000000
+};
+
+// Value masks for UNIT_FIELD_FLAGS_2
+enum UnitFlags2
+{
+ UNIT_FLAG2_FEIGN_DEATH = 0x00000001,
+ UNIT_FLAG2_COMPREHEND_LANG= 0x00000008,
+ UNIT_FLAG2_FORCE_MOVE = 0x00000040
+};
+
+/// Non Player Character flags
+enum NPCFlags
+{
+ UNIT_NPC_FLAG_NONE = 0x00000000,
+ UNIT_NPC_FLAG_GOSSIP = 0x00000001, // 100%
+ UNIT_NPC_FLAG_QUESTGIVER = 0x00000002, // guessed, probably ok
+ UNIT_NPC_FLAG_UNK1 = 0x00000004,
+ UNIT_NPC_FLAG_UNK2 = 0x00000008,
+ UNIT_NPC_FLAG_TRAINER = 0x00000010, // 100%
+ UNIT_NPC_FLAG_TRAINER_CLASS = 0x00000020, // 100%
+ UNIT_NPC_FLAG_TRAINER_PROFESSION = 0x00000040, // 100%
+ UNIT_NPC_FLAG_VENDOR = 0x00000080, // 100%
+ UNIT_NPC_FLAG_VENDOR_AMMO = 0x00000100, // 100%, general goods vendor
+ UNIT_NPC_FLAG_VENDOR_FOOD = 0x00000200, // 100%
+ UNIT_NPC_FLAG_VENDOR_POISON = 0x00000400, // guessed
+ UNIT_NPC_FLAG_VENDOR_REAGENT = 0x00000800, // 100%
+ UNIT_NPC_FLAG_REPAIR = 0x00001000, // 100%
+ UNIT_NPC_FLAG_FLIGHTMASTER = 0x00002000, // 100%
+ UNIT_NPC_FLAG_SPIRITHEALER = 0x00004000, // guessed
+ UNIT_NPC_FLAG_SPIRITGUIDE = 0x00008000, // guessed
+ UNIT_NPC_FLAG_INNKEEPER = 0x00010000, // 100%
+ UNIT_NPC_FLAG_BANKER = 0x00020000, // 100%
+ UNIT_NPC_FLAG_PETITIONER = 0x00040000, // 100% 0xC0000 = guild petitions, 0x40000 = arena team petitions
+ UNIT_NPC_FLAG_TABARDDESIGNER = 0x00080000, // 100%
+ UNIT_NPC_FLAG_BATTLEMASTER = 0x00100000, // 100%
+ UNIT_NPC_FLAG_AUCTIONEER = 0x00200000, // 100%
+ UNIT_NPC_FLAG_STABLEMASTER = 0x00400000, // 100%
+ UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode
+ UNIT_NPC_FLAG_UNK3 = 0x01000000, // cause client to send 1015 opcode
+ UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards
+};
+
+enum MovementFlags
+{
+ MOVEMENTFLAG_NONE = 0x00000000,
+ MOVEMENTFLAG_FORWARD = 0x00000001,
+ MOVEMENTFLAG_BACKWARD = 0x00000002,
+ MOVEMENTFLAG_STRAFE_LEFT = 0x00000004,
+ MOVEMENTFLAG_STRAFE_RIGHT = 0x00000008,
+ MOVEMENTFLAG_LEFT = 0x00000010,
+ MOVEMENTFLAG_RIGHT = 0x00000020,
+ MOVEMENTFLAG_PITCH_UP = 0x00000040,
+ MOVEMENTFLAG_PITCH_DOWN = 0x00000080,
+ MOVEMENTFLAG_WALK_MODE = 0x00000100, // Walking
+ MOVEMENTFLAG_ONTRANSPORT = 0x00000200, // Used for flying on some creatures
+ MOVEMENTFLAG_LEVITATING = 0x00000400,
+ MOVEMENTFLAG_FLY_UNK1 = 0x00000800,
+ MOVEMENTFLAG_JUMPING = 0x00001000,
+ MOVEMENTFLAG_UNK4 = 0x00002000,
+ MOVEMENTFLAG_FALLING = 0x00004000,
+ // 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000
+ MOVEMENTFLAG_SWIMMING = 0x00200000, // appears with fly flag also
+ MOVEMENTFLAG_FLY_UP = 0x00400000,
+ MOVEMENTFLAG_CAN_FLY = 0x00800000,
+ MOVEMENTFLAG_FLYING = 0x01000000,
+ MOVEMENTFLAG_FLYING2 = 0x02000000, // Actual flying mode
+ MOVEMENTFLAG_SPLINE = 0x04000000, // used for flight paths
+ MOVEMENTFLAG_SPLINE2 = 0x08000000, // used for flight paths
+ MOVEMENTFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water
+ MOVEMENTFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive)
+ MOVEMENTFLAG_UNK3 = 0x40000000
+};
+
+enum DiminishingLevels
+{
+ DIMINISHING_LEVEL_1 = 0,
+ DIMINISHING_LEVEL_2 = 1,
+ DIMINISHING_LEVEL_3 = 2,
+ DIMINISHING_LEVEL_IMMUNE = 3
+};
+
+struct DiminishingReturn
+{
+ DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) : DRGroup(group), hitTime(t), hitCount(count), stack(0) {}
+
+ DiminishingGroup DRGroup:16;
+ uint16 stack:16;
+ uint32 hitTime;
+ uint32 hitCount;
+};
+
+enum MeleeHitOutcome
+{
+ MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY,
+ MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL, MELEE_HIT_BLOCK_CRIT
+};
+struct CleanDamage
+{
+ CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) :
+ damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome) {}
+
+ uint32 damage;
+ WeaponAttackType attackType;
+ MeleeHitOutcome hitOutCome;
+};
+
+struct UnitActionBarEntry
+{
+ uint32 Type;
+ uint32 SpellOrAction;
+};
+
+#define MAX_DECLINED_NAME_CASES 5
+
+struct DeclinedName
+{
+ std::string name[MAX_DECLINED_NAME_CASES];
+};
+
+enum CurrentSpellTypes
+{
+ CURRENT_MELEE_SPELL = 0,
+ CURRENT_FIRST_NON_MELEE_SPELL = 1, // just counter
+ CURRENT_GENERIC_SPELL = 1,
+ CURRENT_AUTOREPEAT_SPELL = 2,
+ CURRENT_CHANNELED_SPELL = 3,
+ CURRENT_MAX_SPELL = 4 // just counter
+};
+
+enum ActiveStates
+{
+ ACT_ENABLED = 0xC100,
+ ACT_DISABLED = 0x8100,
+ ACT_COMMAND = 0x0700,
+ ACT_REACTION = 0x0600,
+ ACT_CAST = 0x0100,
+ ACT_PASSIVE = 0x0000,
+ ACT_DECIDE = 0x0001
+};
+
+enum ReactStates
+{
+ REACT_PASSIVE = 0,
+ REACT_DEFENSIVE = 1,
+ REACT_AGGRESSIVE = 2
+};
+
+enum CommandStates
+{
+ COMMAND_STAY = 0,
+ COMMAND_FOLLOW = 1,
+ COMMAND_ATTACK = 2,
+ COMMAND_ABANDON = 3
+};
+
+struct CharmSpellEntry
+{
+ uint16 spellId;
+ uint16 active;
+};
+
+struct CharmInfo
+{
+ public:
+ explicit CharmInfo(Unit* unit);
+ uint32 GetPetNumber() const { return m_petnumber; }
+ void SetPetNumber(uint32 petnumber, bool statwindow);
+
+ void SetCommandState(CommandStates st) { m_CommandState = st; }
+ CommandStates GetCommandState() { return m_CommandState; }
+ bool HasCommandState(CommandStates state) { return (m_CommandState == state); }
+ void SetReactState(ReactStates st) { m_ReactSate = st; }
+ ReactStates GetReactState() { return m_ReactSate; }
+ bool HasReactState(ReactStates state) { return (m_ReactSate == state); }
+
+ void InitPossessCreateSpells();
+ void InitCharmCreateSpells();
+ void InitPetActionBar();
+ void InitEmptyActionBar();
+ //return true if successful
+ bool AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate = ACT_DECIDE);
+ void ToggleCreatureAutocast(uint32 spellid, bool apply);
+
+ UnitActionBarEntry* GetActionBarEntry(uint8 index) { return &(PetActionBar[index]); }
+ CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
+ private:
+ Unit* m_unit;
+ UnitActionBarEntry PetActionBar[10];
+ CharmSpellEntry m_charmspells[4];
+ CommandStates m_CommandState;
+ ReactStates m_ReactSate;
+ uint32 m_petnumber;
+};
+
+// for clearing special attacks
+#define REACTIVE_TIMER_START 4000
+
+enum ReactiveType
+{
+ REACTIVE_DEFENSE = 1,
+ REACTIVE_HUNTER_PARRY = 2,
+ REACTIVE_CRIT = 3,
+ REACTIVE_HUNTER_CRIT = 4,
+ REACTIVE_OVERPOWER = 5
+};
+
+#define MAX_REACTIVE 6
+#define MAX_TOTEM 4
+
+// delay time next attack to prevent client attack animation problems
+#define ATTACK_DISPLAY_DELAY 200
+
+class MANGOS_DLL_SPEC Unit : public WorldObject
+{
+ public:
+ typedef std::set<Unit*> AttackerSet;
+ typedef std::pair<uint32, uint8> spellEffectPair;
+ typedef std::multimap< spellEffectPair, Aura*> AuraMap;
+ typedef std::list<Aura *> AuraList;
+ typedef std::list<DiminishingReturn> Diminishing;
+ typedef std::set<AuraType> AuraTypeSet;
+ typedef std::set<uint32> ComboPointHolderSet;
+
+ virtual ~Unit ( );
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ void CleanupsBeforeDelete(); // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units)
+
+ DiminishingLevels GetDiminishing(DiminishingGroup group);
+ void IncrDiminishing(DiminishingGroup group);
+ void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level);
+ void ApplyDiminishingAura(DiminishingGroup group, bool apply);
+ void ClearDiminishings() { m_Diminishing.clear(); }
+
+ virtual void Update( uint32 time );
+
+ void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; }
+ void resetAttackTimer(WeaponAttackType type = BASE_ATTACK);
+ uint32 getAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; }
+ bool isAttackReady(WeaponAttackType type = BASE_ATTACK) const { return m_attackTimer[type] == 0; }
+ bool haveOffhandWeapon() const;
+ bool canReachWithAttack(Unit *pVictim) const;
+ uint32 m_extraAttacks;
+
+ void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*)
+ {
+ AttackerSet::iterator itr = m_attackers.find(pAttacker);
+ if(itr == m_attackers.end())
+ m_attackers.insert(pAttacker);
+ }
+ void _removeAttacker(Unit *pAttacker) // must be called only from Unit::AttackStop()
+ {
+ AttackerSet::iterator itr = m_attackers.find(pAttacker);
+ if(itr != m_attackers.end())
+ m_attackers.erase(itr);
+ }
+ Unit * getAttackerForHelper() // If someone wants to help, who to give them
+ {
+ if (getVictim() != NULL)
+ return getVictim();
+
+ if (!m_attackers.empty())
+ return *(m_attackers.begin());
+
+ return NULL;
+ }
+ bool Attack(Unit *victim, bool meleeAttack);
+ void CastStop(uint32 except_spellid = 0);
+ bool AttackStop();
+ void RemoveAllAttackers();
+ AttackerSet const& getAttackers() const { return m_attackers; }
+ bool isAttackingPlayer() const;
+ Unit* getVictim() const { return m_attacking; }
+ void CombatStop(bool cast = false);
+ void CombatStopWithPets(bool cast = false);
+ Unit* SelectNearbyTarget() const;
+
+ void addUnitState(uint32 f) { m_state |= f; }
+ bool hasUnitState(const uint32 f) const { return (m_state & f); }
+ void clearUnitState(uint32 f) { m_state &= ~f; }
+ bool CanFreeMove() const
+ {
+ return !hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING | UNIT_STAT_IN_FLIGHT |
+ UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
+ }
+
+ uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); }
+ virtual uint32 getLevelForTarget(Unit const* /*target*/) const { return getLevel(); }
+ void SetLevel(uint32 lvl);
+ uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
+ uint32 getRaceMask() const { return 1 << (getRace()-1); }
+ uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
+ uint32 getClassMask() const { return 1 << (getClass()-1); }
+ uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
+
+ float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
+ void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
+ uint32 GetArmor() const { return GetResistance(SPELL_SCHOOL_NORMAL) ; }
+ void SetArmor(int32 val) { SetResistance(SPELL_SCHOOL_NORMAL, val); }
+
+ uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); }
+ void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+school,val); }
+
+ uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); }
+ uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); }
+ void SetHealth( uint32 val);
+ void SetMaxHealth(uint32 val);
+ int32 ModifyHealth(int32 val);
+
+ Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
+ void setPowerType(Powers power);
+ uint32 GetPower( Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
+ uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
+ void SetPower( Powers power, uint32 val);
+ void SetMaxPower(Powers power, uint32 val);
+ int32 ModifyPower(Powers power, int32 val);
+ void ApplyPowerMod(Powers power, uint32 val, bool apply);
+ void ApplyMaxPowerMod(Powers power, uint32 val, bool apply);
+
+ uint32 GetAttackTime(WeaponAttackType att) const { return (uint32)(GetFloatValue(UNIT_FIELD_BASEATTACKTIME+att)/m_modAttackSpeedPct[att]); }
+ void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val*m_modAttackSpeedPct[att]); }
+ void ApplyAttackTimePercentMod(WeaponAttackType att,float val, bool apply);
+ void ApplyCastTimePercentMod(float val, bool apply);
+
+ // faction template id
+ uint32 getFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
+ void setFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction ); }
+ FactionTemplateEntry const* getFactionTemplateEntry() const;
+ bool IsHostileTo(Unit const* unit) const;
+ bool IsHostileToPlayers() const;
+ bool IsFriendlyTo(Unit const* unit) const;
+ bool IsNeutralToAll() const;
+ bool IsContestedGuard() const
+ {
+ if(FactionTemplateEntry const* entry = getFactionTemplateEntry())
+ return entry->IsContestedGuardFaction();
+
+ return false;
+ }
+ bool IsPvP() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
+ void SetPvP(bool state) { if(state) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); else RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
+ uint32 GetCreatureType() const;
+ uint32 GetCreatureTypeMask() const
+ {
+ uint32 creatureType = GetCreatureType();
+ return (creatureType >= 1) ? (1 << (creatureType - 1)) : 0;
+ }
+
+ uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
+ bool IsSitState() const;
+ bool IsStandState() const;
+ void SetStandState(uint8 state);
+
+ bool IsMounted() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT ); }
+ uint32 GetMountID() const { return GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); }
+ void Mount(uint32 mount);
+ void Unmount();
+
+ uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
+ uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss);
+ void DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false);
+ void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
+
+ void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
+ void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage = 0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK);
+ void HandleEmoteCommand(uint32 anim_id);
+ void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false );
+
+ float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const;
+ SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell);
+ SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false);
+
+ float GetUnitDodgeChance() const;
+ float GetUnitParryChance() const;
+ float GetUnitBlockChance() const;
+ float GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const;
+
+ virtual uint32 GetShieldBlockValue() const =0;
+ uint32 GetUnitMeleeSkill(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
+ uint32 GetDefenseSkillValue(Unit const* target = NULL) const;
+ uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const;
+ float GetWeaponProcChance() const;
+ float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const;
+ MeleeHitOutcome RollPhysicalOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo);
+ MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const;
+ MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const;
+
+ bool isVendor() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ); }
+ bool isTrainer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER ); }
+ bool isQuestGiver() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ); }
+ bool isGossip() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP ); }
+ bool isTaxi() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER ); }
+ bool isGuildMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PETITIONER ); }
+ bool isBattleMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BATTLEMASTER ); }
+ bool isBanker() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER ); }
+ bool isInnkeeper() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER ); }
+ bool isSpiritHealer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER ); }
+ bool isSpiritGuide() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITGUIDE ); }
+ bool isTabardDesigner()const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TABARDDESIGNER ); }
+ bool isAuctioner() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER ); }
+ bool isArmorer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_REPAIR ); }
+ bool isServiceProvider() const
+ {
+ return HasFlag( UNIT_NPC_FLAGS,
+ UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
+ UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
+ UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_GUARD | UNIT_NPC_FLAG_SPIRITHEALER |
+ UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER );
+ }
+ bool isSpiritService() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE ); }
+
+ //Need fix or use this
+ bool isGuard() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GUARD); }
+
+ bool isInFlight() const { return hasUnitState(UNIT_STAT_IN_FLIGHT); }
+
+ bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
+ void SetInCombatState(bool PvP);
+ void SetInCombatWith(Unit* enemy);
+ void ClearInCombat();
+ uint32 GetCombatTimer() const { return m_CombatTimer; }
+
+ bool HasAuraType(AuraType auraType) const;
+ bool HasAura(uint32 spellId, uint32 effIndex) const
+ { return m_Auras.find(spellEffectPair(spellId, effIndex)) != m_Auras.end(); }
+
+ bool virtual HasSpell(uint32 /*spellID*/) const { return false; }
+
+ bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); }
+ bool HasInvisibilityAura() const { return HasAuraType(SPELL_AURA_MOD_INVISIBILITY); }
+ bool isFeared() const { return HasAuraType(SPELL_AURA_MOD_FEAR); }
+ bool isInRoots() const { return HasAuraType(SPELL_AURA_MOD_ROOT); }
+ bool IsPolymorphed() const;
+
+ bool isFrozen() const;
+
+ void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage);
+
+ bool isTargetableForAttack() const;
+ virtual bool IsInWater() const;
+ virtual bool IsUnderWater() const;
+ bool isInAccessablePlaceFor(Creature const* c) const;
+
+ void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical = false);
+ void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype, bool critical = false);
+ uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell = false, bool useSpellDamage = true);
+ void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+
+ bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const;
+
+ void DeMorph();
+
+ void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
+ void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
+ void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo);
+
+ void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
+ void SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags);
+ void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime = 0, Player* player = NULL);
+ void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL);
+
+ virtual void MoveOutOfRange(Player &) { };
+
+ bool isAlive() const { return (m_deathState == ALIVE); };
+ bool isDead() const { return ( m_deathState == DEAD || m_deathState == CORPSE ); };
+ DeathState getDeathState() { return m_deathState; };
+ virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet
+
+ uint64 const& GetOwnerGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMONEDBY); }
+ uint64 GetPetGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMON); }
+ uint64 GetCharmerGUID() const { return GetUInt64Value(UNIT_FIELD_CHARMEDBY); }
+ uint64 GetCharmGUID() const { return GetUInt64Value(UNIT_FIELD_CHARM); }
+ void SetCharmerGUID(uint64 owner) { SetUInt64Value(UNIT_FIELD_CHARMEDBY, owner); }
+
+ uint64 GetCharmerOrOwnerGUID() const { return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); }
+ uint64 GetCharmerOrOwnerOrOwnGUID() const
+ {
+ if(uint64 guid = GetCharmerOrOwnerGUID())
+ return guid;
+ return GetGUID();
+ }
+ bool isCharmedOwnedByPlayerOrPlayer() const { return IS_PLAYER_GUID(GetCharmerOrOwnerOrOwnGUID()); }
+
+ Player* GetSpellModOwner();
+
+ Unit* GetOwner() const;
+ Pet* GetPet() const;
+ Unit* GetCharmer() const;
+ Unit* GetCharm() const;
+ Unit* GetCharmerOrOwner() const { return GetCharmerGUID() ? GetCharmer() : GetOwner(); }
+ Unit* GetCharmerOrOwnerOrSelf()
+ {
+ if(Unit* u = GetCharmerOrOwner())
+ return u;
+
+ return this;
+ }
+ Player* GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ void SetPet(Pet* pet);
+ void SetCharm(Unit* pet);
+ bool isCharmed() const { return GetCharmerGUID() != 0; }
+
+ CharmInfo* GetCharmInfo() { return m_charmInfo; }
+ CharmInfo* InitCharmInfo(Unit* charm);
+
+ bool AddAura(Aura *aur);
+
+ void RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
+ void RemoveAura(uint32 spellId, uint32 effindex, Aura* except = NULL);
+ void RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex);
+ void RemoveAurasDueToSpell(uint32 spellId, Aura* except = NULL);
+ void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId);
+ void RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler);
+ void RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer);
+ void RemoveAurasDueToSpellByCancel(uint32 spellId);
+ void RemoveNotOwnSingleTargetAuras();
+
+ void RemoveSpellsCausingAura(AuraType auraType);
+ void RemoveRankAurasDueToSpell(uint32 spellId);
+ bool RemoveNoStackAurasDueToAura(Aura *Aur);
+ void RemoveAurasWithInterruptFlags(uint32 flags);
+ void RemoveAurasWithDispelType( DispelType type );
+
+ void RemoveAllAuras();
+ void RemoveAllAurasOnDeath();
+ void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime);
+
+ float GetResistanceBuffMods(SpellSchools school, bool positive) const { return GetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school ); }
+ void SetResistanceBuffMods(SpellSchools school, bool positive, float val) { SetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school,val); }
+ void ApplyResistanceBuffModsMod(SpellSchools school, bool positive, float val, bool apply) { ApplyModSignedFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
+ void ApplyResistanceBuffModsPercentMod(SpellSchools school, bool positive, float val, bool apply) { ApplyPercentModFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
+ void InitStatBuffMods()
+ {
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_POSSTAT0+i, 0);
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_NEGSTAT0+i, 0);
+ }
+ void ApplyStatBuffMod(Stats stat, float val, bool apply) { ApplyModSignedFloatValue((val > 0 ? UNIT_FIELD_POSSTAT0+stat : UNIT_FIELD_NEGSTAT0+stat), val, apply); }
+ void ApplyStatPercentBuffMod(Stats stat, float val, bool apply)
+ {
+ ApplyPercentModFloatValue(UNIT_FIELD_POSSTAT0+stat, val, apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_NEGSTAT0+stat, val, apply);
+ }
+ void SetCreateStat(Stats stat, float val) { m_createStats[stat] = val; }
+ void SetCreateHealth(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_HEALTH, val); }
+ uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); }
+ void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); }
+ uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); }
+ uint32 GetCreatePowers(Powers power) const;
+ float GetPosStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_POSSTAT0+stat); }
+ float GetNegStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_NEGSTAT0+stat); }
+ float GetCreateStat(Stats stat) const { return m_createStats[stat]; }
+
+ void SetCurrentCastedSpell(Spell * pSpell);
+ virtual void ProhibitSpellScholl(SpellSchoolMask /*idSchoolMask*/, uint32 /*unTimeMs*/ ) { }
+ void InterruptSpell(uint32 spellType, bool withDelayed = true);
+
+ // set withDelayed to true to account delayed spells as casted
+ // delayed+channeled spells are always accounted as casted
+ // we can skip channeled or delayed checks using flags
+ bool IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled = false, bool skipAutorepeat = false) const;
+
+ // set withDelayed to true to interrupt delayed spells too
+ // delayed+channeled spells are always interrupted
+ void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0);
+
+ Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
+
+ Spell* m_currentSpells[CURRENT_MAX_SPELL];
+
+ uint32 m_addDmgOnce;
+ uint64 m_TotemSlot[MAX_TOTEM];
+ uint64 m_ObjectSlot[4];
+ uint32 m_detectInvisibilityMask;
+ uint32 m_invisibilityMask;
+ uint32 m_ShapeShiftFormSpellId;
+ ShapeshiftForm m_form;
+ float m_modMeleeHitChance;
+ float m_modRangedHitChance;
+ float m_modSpellHitChance;
+ int32 m_baseSpellCritChance;
+
+ float m_threatModifier[MAX_SPELL_SCHOOL];
+ float m_modAttackSpeedPct[3];
+
+ // Event handler
+ EventProcessor m_Events;
+
+ // stat system
+ bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply);
+ void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; }
+ float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const;
+ float GetTotalStatValue(Stats stat) const;
+ float GetTotalAuraModValue(UnitMods unitMod) const;
+ SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
+ Stats GetStatByAuraGroup(UnitMods unitMod) const;
+ Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
+ bool CanModifyStats() const { return m_canModifyStats; }
+ void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
+ virtual bool UpdateStats(Stats stat) = 0;
+ virtual bool UpdateAllStats() = 0;
+ virtual void UpdateResistances(uint32 school) = 0;
+ virtual void UpdateArmor() = 0;
+ virtual void UpdateMaxHealth() = 0;
+ virtual void UpdateMaxPower(Powers power) = 0;
+ virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
+ virtual void UpdateDamagePhysical(WeaponAttackType attType) = 0;
+ float GetTotalAttackPowerValue(WeaponAttackType attType) const;
+ float GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const;
+ void SetBaseWeaponDamage(WeaponAttackType attType ,WeaponDamageRange damageRange, float value) { m_weaponDamage[attType][damageRange] = value; }
+
+ bool isInFront(Unit const* target,float distance, float arc = M_PI) const;
+ void SetInFront(Unit const* target);
+ bool isInBack(Unit const* target, float distance, float arc = M_PI) const;
+
+ // Visibility system
+ UnitVisibility GetVisibility() const { return m_Visibility; }
+ void SetVisibility(UnitVisibility x);
+
+ // common function for visibility checks for player/creatures with detection code
+ bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false) const;
+ bool canDetectInvisibilityOf(Unit const* u) const;
+
+ // virtual functions for all world objects types
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+ // function for low level grid visibility checks in player/creature cases
+ virtual bool IsVisibleInGridForPlayer(Player* pl) const = 0;
+
+ bool waterbreath;
+ AuraList & GetSingleCastAuras() { return m_scAuras; }
+ AuraList const& GetSingleCastAuras() const { return m_scAuras; }
+ SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
+
+ // Threat related methodes
+ bool CanHaveThreatList() const;
+ void AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+ float ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
+ void DeleteThreatList();
+ bool SelectHostilTarget();
+ void TauntApply(Unit* pVictim);
+ void TauntFadeOut(Unit *taunter);
+ ThreatManager& getThreatManager() { return m_ThreatManager; }
+ void addHatedBy(HostilReference* pHostilReference) { m_HostilRefManager.insertFirst(pHostilReference); };
+ void removeHatedBy(HostilReference* /*pHostilReference*/ ) { /* nothing to do yet */ }
+ HostilRefManager& getHostilRefManager() { return m_HostilRefManager; }
+
+ Aura* GetAura(uint32 spellId, uint32 effindex);
+ AuraMap & GetAuras() { return m_Auras; }
+ AuraMap const& GetAuras() const { return m_Auras; }
+ AuraList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; }
+ void ApplyAuraProcTriggerDamage(Aura* aura, bool apply);
+
+ int32 GetTotalAuraModifier(AuraType auratype) const;
+ float GetTotalAuraMultiplier(AuraType auratype) const;
+ int32 GetMaxPositiveAuraModifier(AuraType auratype) const;
+ int32 GetMaxNegativeAuraModifier(AuraType auratype) const;
+
+ int32 GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ float GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ int32 GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ int32 GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+
+ int32 GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+ float GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const;
+ int32 GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+ int32 GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+
+ Aura* GetDummyAura(uint32 spell_id) const;
+
+ uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
+ void SetDisplayId(uint32 modelId);
+ uint32 GetNativeDisplayId() { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
+ void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
+ void setTransForm(uint32 spellid) { m_transform = spellid;}
+ uint32 getTransForm() const { return m_transform;}
+ void AddDynObject(DynamicObject* dynObj);
+ void RemoveDynObject(uint32 spellid);
+ void RemoveDynObjectWithGUID(uint64 guid) { m_dynObjGUIDs.remove(guid); }
+ void RemoveAllDynObjects();
+ void AddGameObject(GameObject* gameObj);
+ void RemoveGameObject(GameObject* gameObj, bool del);
+ void RemoveGameObject(uint32 spellid, bool del);
+ void RemoveAllGameObjects();
+ DynamicObject *GetDynObject(uint32 spellId, uint32 effIndex);
+ DynamicObject *GetDynObject(uint32 spellId);
+ uint32 CalculateDamage(WeaponAttackType attType, bool normalized);
+ float GetAPMultiplier(WeaponAttackType attType, bool normalized);
+ void ModifyAuraState(AuraState flag, bool apply);
+ bool HasAuraState(AuraState flag) const { return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); }
+ void UnsummonAllTotems();
+ int32 SpellBaseDamageBonus(SpellSchoolMask schoolMask);
+ int32 SpellBaseHealingBonus(SpellSchoolMask schoolMask);
+ int32 SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
+ int32 SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
+ uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype);
+ uint32 SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim);
+ bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType);
+ uint32 SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim);
+
+ void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; }
+ bool IsUnderLastManaUseEffect() const;
+
+ void SetContestedPvP(Player *attackedPlayer = NULL);
+
+ void MeleeDamageBonus(Unit *pVictim, uint32 *damage, WeaponAttackType attType, SpellEntry const *spellProto = NULL);
+ uint32 GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime );
+
+ void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply);
+ void ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply);
+ virtual bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
+ // redefined in Creature
+ bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask, bool useCharges = false);
+ virtual bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
+ // redefined in Creature
+
+ uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage);
+ void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist);
+
+ void UpdateSpeed(UnitMoveType mtype, bool forced);
+ float GetSpeed( UnitMoveType mtype ) const;
+ float GetSpeedRate( UnitMoveType mtype ) const { return m_speed_rate[mtype]; }
+ void SetSpeed(UnitMoveType mtype, float rate, bool forced = false);
+
+ void SetHover(bool on);
+ bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); }
+
+ void _RemoveAllAuraMods();
+ void _ApplyAllAuraMods();
+
+ int32 CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 basePoints, Unit const* target);
+ int32 CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target);
+ float CalculateLevelPenalty(SpellEntry const* spellProto) const;
+
+ void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
+ void removeFollower(FollowerReference* /*pRef*/ ) { /* nothing to do yet */ }
+ static Unit* GetUnit(WorldObject& object, uint64 guid);
+
+ MotionMaster* GetMotionMaster() { return &i_motionMaster; }
+
+ bool IsStopped() const { return !(hasUnitState(UNIT_STAT_MOVING)); }
+ void StopMoving();
+
+ void AddUnitMovementFlag(uint32 f) { m_unit_movement_flags |= f; }
+ void RemoveUnitMovementFlag(uint32 f)
+ {
+ uint32 oldval = m_unit_movement_flags;
+ m_unit_movement_flags = oldval & ~f;
+ }
+ uint32 HasUnitMovementFlag(uint32 f) const { return m_unit_movement_flags & f; }
+ uint32 GetUnitMovementFlags() const { return m_unit_movement_flags; }
+ void SetUnitMovementFlags(uint32 f) { m_unit_movement_flags = f; }
+
+ void SetFeared(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
+ void SetConfused(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
+
+ void AddComboPointHolder(uint32 lowguid) { m_ComboPointHolders.insert(lowguid); }
+ void RemoveComboPointHolder(uint32 lowguid) { m_ComboPointHolders.erase(lowguid); }
+ void ClearComboPointHolders();
+
+ ///----------Pet responses methods-----------------
+ void SendPetCastFail(uint32 spellid, uint8 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----------
+
+ void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); }
+
+ // reactive attacks
+ void ClearAllReactives();
+ void StartReactiveTimer( ReactiveType reactive ) { m_reactiveTimer[reactive] = REACTIVE_TIMER_START;}
+ void UpdateReactives(uint32 p_time);
+
+ // group updates
+ void UpdateAuraForGroup(uint8 slot);
+
+ // pet auras
+ typedef std::set<PetAura const*> PetAuraSet;
+ PetAuraSet m_petAuras;
+ void AddPetAura(PetAura const* petSpell);
+ void RemovePetAura(PetAura const* petSpell);
+
+ protected:
+ explicit Unit ();
+
+ void _UpdateSpells(uint32 time);
+
+ void _UpdateAutoRepeatSpell();
+ bool m_AutoRepeatFirstCast;
+
+ uint32 m_attackTimer[MAX_ATTACK];
+
+ float m_createStats[MAX_STATS];
+
+ AttackerSet m_attackers;
+ Unit* m_attacking;
+
+ DeathState m_deathState;
+
+ AuraMap m_Auras;
+
+ std::list<Aura *> m_scAuras; // casted singlecast auras
+
+ typedef std::list<uint64> DynObjectGUIDs;
+ DynObjectGUIDs m_dynObjGUIDs;
+
+ std::list<GameObject*> m_gameObj;
+ bool m_isSorted;
+ uint32 m_transform;
+ uint32 m_removedAuras;
+
+ AuraList m_modAuras[TOTAL_AURAS];
+ 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
+
+ float m_speed_rate[MAX_MOVE_TYPE];
+
+ CharmInfo *m_charmInfo;
+
+ virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
+
+ MotionMaster i_motionMaster;
+ uint32 m_unit_movement_flags;
+
+ uint32 m_reactiveTimer[MAX_REACTIVE];
+
+ private:
+ void SendAttackStop(Unit* victim); // only from AttackStop(Unit*)
+ void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*)
+
+ void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask );
+ bool HandleDummyAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
+ bool HandleProcTriggerSpell(Unit *pVictim,uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attType,uint32 cooldown);
+ bool HandleHasteAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
+ bool HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell,uint32 cooldown);
+ uint32 m_state; // Even derived shouldn't modify
+ uint32 m_CombatTimer;
+ uint32 m_lastManaUse; // msecs
+
+ UnitVisibility m_Visibility;
+
+ Diminishing m_Diminishing;
+ // Manage all Units threatening us
+ ThreatManager m_ThreatManager;
+ // Manage all Units that are threatened by us
+ HostilRefManager m_HostilRefManager;
+
+ FollowerRefManager m_FollowingRefManager;
+
+ ComboPointHolderSet m_ComboPointHolders;
+};
+#endif
diff --git a/src/game/UnitEvents.h b/src/game/UnitEvents.h
new file mode 100644
index 00000000000..4ff99535035
--- /dev/null
+++ b/src/game/UnitEvents.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _UNITEVENTS
+#define _UNITEVENTS
+
+#include "Common.h"
+
+class ThreatContainer;
+class ThreatManager;
+class HostilReference;
+
+//==============================================================
+//==============================================================
+
+enum UNIT_EVENT_TYPE
+{
+ // Player/Pet changed on/offline status
+ UEV_THREAT_REF_ONLINE_STATUS = 1<<0,
+
+ // Threat for Player/Pet changed
+ UEV_THREAT_REF_THREAT_CHANGE = 1<<1,
+
+ // Player/Pet will be removed from list (dead) [for internal use]
+ UEV_THREAT_REF_REMOVE_FROM_LIST = 1<<2,
+
+ // Player/Pet entered/left water or some other place where it is/was not accessible for the creature
+ UEV_THREAT_REF_ASSECCIBLE_STATUS = 1<<3,
+
+ // Threat list is going to be sorted (if dirty flag is set)
+ UEV_THREAT_SORT_LIST = 1<<4,
+
+ // New target should be fetched, could tbe the current target as well
+ UEV_THREAT_SET_NEXT_TARGET = 1<<5,
+
+ // A new victim (target) was set. Could be NULL
+ UEV_THREAT_VICTIM_CHANGED = 1<<6,
+
+ // Future use
+ //UEV_UNIT_KILLED = 1<<7,
+
+ //Future use
+ //UEV_UNIT_HEALTH_CHANGE = 1<<8,
+};
+
+#define UEV_THREAT_REF_EVENT_MASK ( UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ASSECCIBLE_STATUS)
+#define UEV_THREAT_MANAGER_EVENT_MASK (UEV_THREAT_SORT_LIST | UEV_THREAT_SET_NEXT_TARGET | UEV_THREAT_VICTIM_CHANGED)
+#define UEV_ALL_EVENT_MASK (0xffffffff)
+
+// Future use
+//#define UEV_UNIT_EVENT_MASK (UEV_UNIT_KILLED | UEV_UNIT_HEALTH_CHANGE)
+
+//==============================================================
+
+class MANGOS_DLL_SPEC UnitBaseEvent
+{
+ private:
+ uint32 iType;
+ public:
+ UnitBaseEvent(uint32 pType) { iType = pType; }
+ uint32 getType() const { return iType; }
+ bool matchesTypeMask(uint32 pMask) const { return iType & pMask; }
+
+ void setType(uint32 pType) { iType = pType; }
+
+};
+
+//==============================================================
+
+class MANGOS_DLL_SPEC ThreatRefStatusChangeEvent : public UnitBaseEvent
+{
+ private:
+ HostilReference* iHostilReference;
+ union
+ {
+ float iFValue;
+ int32 iIValue;
+ bool iBValue;
+ };
+ ThreatManager* iThreatManager;
+ public:
+ ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType) { iHostilReference = NULL; }
+
+ ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; }
+
+ ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference, float pValue) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; iFValue = pValue; }
+
+ ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference, bool pValue) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; iBValue = pValue; }
+
+ int32 getIValue() const { return iIValue; }
+
+ float getFValue() const { return iFValue; }
+
+ bool getBValue() const { return iBValue; }
+
+ void setBValue(bool pValue) { iBValue = pValue; }
+
+ HostilReference* getReference() const { return iHostilReference; }
+
+ void setThreatManager(ThreatManager* pThreatManager) { iThreatManager = pThreatManager; }
+
+ ThreatManager* getThreatManager() const { return iThreatManager; }
+};
+
+//==============================================================
+
+class MANGOS_DLL_SPEC ThreatManagerEvent : public ThreatRefStatusChangeEvent
+{
+ private:
+ ThreatContainer* iThreatContainer;
+ public:
+ ThreatManagerEvent(uint32 pType) : ThreatRefStatusChangeEvent(pType) {}
+ ThreatManagerEvent(uint32 pType, HostilReference* pHostilReference) : ThreatRefStatusChangeEvent(pType, pHostilReference) {}
+
+ void setThreatContainer(ThreatContainer* pThreatContainer) { iThreatContainer = pThreatContainer; }
+
+ ThreatContainer* getThreatContainer() const { return iThreatContainer; }
+};
+
+//==============================================================
+#endif
diff --git a/src/game/UpdateData.cpp b/src/game/UpdateData.cpp
new file mode 100644
index 00000000000..eded8cb7395
--- /dev/null
+++ b/src/game/UpdateData.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "ByteBuffer.h"
+#include "WorldPacket.h"
+#include "UpdateData.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "World.h"
+#include <zlib/zlib.h>
+
+UpdateData::UpdateData() : m_blockCount(0)
+{
+}
+
+void UpdateData::AddOutOfRangeGUID(std::set<uint64>& guids)
+{
+ m_outOfRangeGUIDs.insert(guids.begin(),guids.end());
+}
+
+void UpdateData::AddOutOfRangeGUID(const uint64 &guid)
+{
+ m_outOfRangeGUIDs.insert(guid);
+}
+
+void UpdateData::AddUpdateBlock(const ByteBuffer &block)
+{
+ m_data.append(block);
+ ++m_blockCount;
+}
+
+void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size)
+{
+ z_stream c_stream;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ // default Z_BEST_SPEED (1)
+ int z_res = deflateInit(&c_stream, sWorld.getConfig(CONFIG_COMPRESSION));
+ if (z_res != Z_OK)
+ {
+ sLog.outError("Can't compress update packet (zlib: deflateInit) Error code: %i (%s)",z_res,zError(z_res));
+ *dst_size = 0;
+ return;
+ }
+
+ c_stream.next_out = (Bytef*)dst;
+ c_stream.avail_out = *dst_size;
+ c_stream.next_in = (Bytef*)src;
+ c_stream.avail_in = (uInt)src_size;
+
+ z_res = deflate(&c_stream, Z_NO_FLUSH);
+ if (z_res != Z_OK)
+ {
+ sLog.outError("Can't compress update packet (zlib: deflate) Error code: %i (%s)",z_res,zError(z_res));
+ *dst_size = 0;
+ return;
+ }
+
+ if (c_stream.avail_in != 0)
+ {
+ sLog.outError("Can't compress update packet (zlib: deflate not greedy)");
+ *dst_size = 0;
+ return;
+ }
+
+ z_res = deflate(&c_stream, Z_FINISH);
+ if (z_res != Z_STREAM_END)
+ {
+ sLog.outError("Can't compress update packet (zlib: deflate should report Z_STREAM_END instead %i (%s)",z_res,zError(z_res));
+ *dst_size = 0;
+ return;
+ }
+
+ z_res = deflateEnd(&c_stream);
+ if (z_res != Z_OK)
+ {
+ sLog.outError("Can't compress update packet (zlib: deflateEnd) Error code: %i (%s)",z_res,zError(z_res));
+ *dst_size = 0;
+ return;
+ }
+
+ *dst_size = c_stream.total_out;
+}
+
+bool UpdateData::BuildPacket(WorldPacket *packet, bool hasTransport)
+{
+ ByteBuffer buf(m_data.size() + 10 + m_outOfRangeGUIDs.size()*8);
+
+ buf << (uint32) (!m_outOfRangeGUIDs.empty() ? m_blockCount + 1 : m_blockCount);
+ buf << (uint8) (hasTransport ? 1 : 0);
+
+ if(!m_outOfRangeGUIDs.empty())
+ {
+ buf << (uint8) UPDATETYPE_OUT_OF_RANGE_OBJECTS;
+ buf << (uint32) m_outOfRangeGUIDs.size();
+
+ for(std::set<uint64>::const_iterator i = m_outOfRangeGUIDs.begin();
+ i != m_outOfRangeGUIDs.end(); i++)
+ {
+ //buf.appendPackGUID(*i);
+ buf << (uint8)0xFF;
+ buf << (uint64) *i;
+ }
+ }
+
+ buf.append(m_data);
+
+ packet->clear();
+
+ if (m_data.size() > 50 )
+ {
+ uint32 destsize = buf.size() + buf.size()/10 + 16;
+ packet->resize( destsize );
+
+ packet->put(0, (uint32)buf.size());
+
+ Compress(const_cast<uint8*>(packet->contents()) + sizeof(uint32),
+ &destsize,
+ (void*)buf.contents(),
+ buf.size());
+ if (destsize == 0)
+ return false;
+
+ packet->resize( destsize + sizeof(uint32) );
+ packet->SetOpcode( SMSG_COMPRESSED_UPDATE_OBJECT );
+ }
+ else
+ {
+ packet->append( buf );
+ packet->SetOpcode( SMSG_UPDATE_OBJECT );
+ }
+
+ return true;
+}
+
+void UpdateData::Clear()
+{
+ m_data.clear();
+ m_outOfRangeGUIDs.clear();
+ m_blockCount = 0;
+}
diff --git a/src/game/UpdateData.h b/src/game/UpdateData.h
new file mode 100644
index 00000000000..20f5749d507
--- /dev/null
+++ b/src/game/UpdateData.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __UPDATEDATA_H
+#define __UPDATEDATA_H
+
+class WorldPacket;
+
+enum OBJECT_UPDATE_TYPE
+{
+ UPDATETYPE_VALUES = 0,
+ UPDATETYPE_MOVEMENT = 1,
+ UPDATETYPE_CREATE_OBJECT = 2,
+ UPDATETYPE_CREATE_OBJECT2 = 3,
+ UPDATETYPE_OUT_OF_RANGE_OBJECTS = 4,
+ UPDATETYPE_NEAR_OBJECTS = 5
+};
+
+enum OBJECT_UPDATE_FLAGS
+{
+ UPDATEFLAG_NONE = 0x00,
+ UPDATEFLAG_SELF = 0x01,
+ UPDATEFLAG_TRANSPORT = 0x02,
+ UPDATEFLAG_FULLGUID = 0x04,
+ UPDATEFLAG_LOWGUID = 0x08,
+ UPDATEFLAG_HIGHGUID = 0x10,
+ UPDATEFLAG_LIVING = 0x20,
+ UPDATEFLAG_HASPOSITION = 0x40
+};
+
+class UpdateData
+{
+ public:
+ UpdateData();
+
+ void AddOutOfRangeGUID(std::set<uint64>& guids);
+ void AddOutOfRangeGUID(const uint64 &guid);
+ void AddUpdateBlock(const ByteBuffer &block);
+ bool BuildPacket(WorldPacket *packet, bool hasTransport = false);
+ bool HasData() { return m_blockCount > 0 || !m_outOfRangeGUIDs.empty(); }
+ void Clear();
+
+ std::set<uint64> const& GetOutOfRangeGUIDs() const { return m_outOfRangeGUIDs; }
+
+ protected:
+ uint32 m_blockCount;
+ std::set<uint64> m_outOfRangeGUIDs;
+ ByteBuffer m_data;
+
+ void Compress(void* dst, uint32 *dst_size, void* src, int src_size);
+};
+#endif
diff --git a/src/game/UpdateFields.h b/src/game/UpdateFields.h
new file mode 100644
index 00000000000..5cff686d209
--- /dev/null
+++ b/src/game/UpdateFields.h
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef _UPDATEFIELDS_AUTO_H
+#define _UPDATEFIELDS_AUTO_H
+
+// Auto generated for version 2, 4, 3, 8606
+
+enum EObjectFields
+{
+ OBJECT_FIELD_GUID = 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ OBJECT_FIELD_TYPE = 0x0002, // Size: 1, Type: INT, Flags: PUBLIC
+ OBJECT_FIELD_ENTRY = 0x0003, // Size: 1, Type: INT, Flags: PUBLIC
+ OBJECT_FIELD_SCALE_X = 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ OBJECT_FIELD_PADDING = 0x0005, // Size: 1, Type: INT, Flags: NONE
+ OBJECT_END = 0x0006,
+};
+
+enum EItemFields
+{
+ ITEM_FIELD_OWNER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ ITEM_FIELD_CONTAINED = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC
+ ITEM_FIELD_CREATOR = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC
+ ITEM_FIELD_GIFTCREATOR = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC
+ ITEM_FIELD_STACK_COUNT = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
+ ITEM_FIELD_DURATION = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
+ ITEM_FIELD_SPELL_CHARGES = OBJECT_END + 0x000A, // Size: 5, Type: INT, Flags: OWNER_ONLY, UNK2
+ ITEM_FIELD_FLAGS = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC
+ ITEM_FIELD_ENCHANTMENT = OBJECT_END + 0x0010, // Size: 33, Type: INT, Flags: PUBLIC
+ ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x0031, // Size: 1, Type: INT, Flags: PUBLIC
+ ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x0032, // Size: 1, Type: INT, Flags: PUBLIC
+ ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x0033, // Size: 1, Type: INT, Flags: OWNER_ONLY
+ ITEM_FIELD_DURABILITY = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
+ ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2
+ ITEM_END = OBJECT_END + 0x0036,
+};
+
+enum EContainerFields
+{
+ CONTAINER_FIELD_NUM_SLOTS = ITEM_END + 0x0000, // Size: 1, Type: INT, Flags: PUBLIC
+ CONTAINER_ALIGN_PAD = ITEM_END + 0x0001, // Size: 1, Type: BYTES, Flags: NONE
+ CONTAINER_FIELD_SLOT_1 = ITEM_END + 0x0002, // Size: 72, Type: LONG, Flags: PUBLIC
+ CONTAINER_END = ITEM_END + 0x004A,
+};
+
+enum EUnitFields
+{
+ UNIT_FIELD_CHARM = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_SUMMON = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_CHARMEDBY = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_SUMMONEDBY = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_CREATEDBY = OBJECT_END + 0x0008, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_TARGET = OBJECT_END + 0x000A, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_PERSUADED = OBJECT_END + 0x000C, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_CHANNEL_OBJECT = OBJECT_END + 0x000E, // Size: 2, Type: LONG, Flags: PUBLIC
+ UNIT_FIELD_HEALTH = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: DYNAMIC
+ UNIT_FIELD_POWER1 = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_POWER2 = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_POWER3 = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_POWER4 = OBJECT_END + 0x0014, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_POWER5 = OBJECT_END + 0x0015, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MAXHEALTH = OBJECT_END + 0x0016, // Size: 1, Type: INT, Flags: DYNAMIC
+ UNIT_FIELD_MAXPOWER1 = OBJECT_END + 0x0017, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MAXPOWER2 = OBJECT_END + 0x0018, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MAXPOWER3 = OBJECT_END + 0x0019, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MAXPOWER4 = OBJECT_END + 0x001A, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MAXPOWER5 = OBJECT_END + 0x001B, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_LEVEL = OBJECT_END + 0x001C, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_FACTIONTEMPLATE = OBJECT_END + 0x001D, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_BYTES_0 = OBJECT_END + 0x001E, // Size: 1, Type: BYTES, Flags: PUBLIC
+ UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = OBJECT_END + 0x001F, // Size: 3, Type: INT, Flags: PUBLIC
+ UNIT_VIRTUAL_ITEM_INFO = OBJECT_END + 0x0022, // Size: 6, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_FLAGS = OBJECT_END + 0x0028, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_FLAGS_2 = OBJECT_END + 0x0029, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_AURA = OBJECT_END + 0x002A, // Size: 56, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_AURAFLAGS = OBJECT_END + 0x0062, // Size: 14, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_AURALEVELS = OBJECT_END + 0x0070, // Size: 14, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_AURAAPPLICATIONS = OBJECT_END + 0x007E, // Size: 14, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_AURASTATE = OBJECT_END + 0x008C, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_BASEATTACKTIME = OBJECT_END + 0x008D, // Size: 2, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_RANGEDATTACKTIME = OBJECT_END + 0x008F, // Size: 1, Type: INT, Flags: PRIVATE
+ UNIT_FIELD_BOUNDINGRADIUS = OBJECT_END + 0x0090, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ UNIT_FIELD_COMBATREACH = OBJECT_END + 0x0091, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ UNIT_FIELD_DISPLAYID = OBJECT_END + 0x0092, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_NATIVEDISPLAYID = OBJECT_END + 0x0093, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MOUNTDISPLAYID = OBJECT_END + 0x0094, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_MINDAMAGE = OBJECT_END + 0x0095, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3
+ UNIT_FIELD_MAXDAMAGE = OBJECT_END + 0x0096, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3
+ UNIT_FIELD_MINOFFHANDDAMAGE = OBJECT_END + 0x0097, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3
+ UNIT_FIELD_MAXOFFHANDDAMAGE = OBJECT_END + 0x0098, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3
+ UNIT_FIELD_BYTES_1 = OBJECT_END + 0x0099, // Size: 1, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_PETNUMBER = OBJECT_END + 0x009A, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_PET_NAME_TIMESTAMP = OBJECT_END + 0x009B, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_PETEXPERIENCE = OBJECT_END + 0x009C, // Size: 1, Type: INT, Flags: OWNER_ONLY
+ UNIT_FIELD_PETNEXTLEVELEXP = OBJECT_END + 0x009D, // Size: 1, Type: INT, Flags: OWNER_ONLY
+ UNIT_DYNAMIC_FLAGS = OBJECT_END + 0x009E, // Size: 1, Type: INT, Flags: DYNAMIC
+ UNIT_CHANNEL_SPELL = OBJECT_END + 0x009F, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_MOD_CAST_SPEED = OBJECT_END + 0x00A0, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ UNIT_CREATED_BY_SPELL = OBJECT_END + 0x00A1, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_NPC_FLAGS = OBJECT_END + 0x00A2, // Size: 1, Type: INT, Flags: DYNAMIC
+ UNIT_NPC_EMOTESTATE = OBJECT_END + 0x00A3, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_TRAINING_POINTS = OBJECT_END + 0x00A4, // Size: 1, Type: TWO_SHORT, Flags: OWNER_ONLY
+ UNIT_FIELD_STAT0 = OBJECT_END + 0x00A5, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_STAT1 = OBJECT_END + 0x00A6, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_STAT2 = OBJECT_END + 0x00A7, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_STAT3 = OBJECT_END + 0x00A8, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_STAT4 = OBJECT_END + 0x00A9, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POSSTAT0 = OBJECT_END + 0x00AA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POSSTAT1 = OBJECT_END + 0x00AB, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POSSTAT2 = OBJECT_END + 0x00AC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POSSTAT3 = OBJECT_END + 0x00AD, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POSSTAT4 = OBJECT_END + 0x00AE, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_NEGSTAT0 = OBJECT_END + 0x00AF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_NEGSTAT1 = OBJECT_END + 0x00B0, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_NEGSTAT2 = OBJECT_END + 0x00B1, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_NEGSTAT3 = OBJECT_END + 0x00B2, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_NEGSTAT4 = OBJECT_END + 0x00B3, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_RESISTANCES = OBJECT_END + 0x00B4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY, UNK3
+ UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = OBJECT_END + 0x00BB, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = OBJECT_END + 0x00C2, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_BASE_MANA = OBJECT_END + 0x00C9, // Size: 1, Type: INT, Flags: PUBLIC
+ UNIT_FIELD_BASE_HEALTH = OBJECT_END + 0x00CA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_BYTES_2 = OBJECT_END + 0x00CB, // Size: 1, Type: BYTES, Flags: PUBLIC
+ UNIT_FIELD_ATTACK_POWER = OBJECT_END + 0x00CC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_ATTACK_POWER_MODS = OBJECT_END + 0x00CD, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00CE, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_RANGED_ATTACK_POWER = OBJECT_END + 0x00CF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_RANGED_ATTACK_POWER_MODS = OBJECT_END + 0x00D0, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00D1, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_MINRANGEDDAMAGE = OBJECT_END + 0x00D2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_MAXRANGEDDAMAGE = OBJECT_END + 0x00D3, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POWER_COST_MODIFIER = OBJECT_END + 0x00D4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_POWER_COST_MULTIPLIER = OBJECT_END + 0x00DB, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_MAXHEALTHMODIFIER = OBJECT_END + 0x00E2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY
+ UNIT_FIELD_PADDING = OBJECT_END + 0x00E3, // Size: 1, Type: INT, Flags: NONE
+ UNIT_END = OBJECT_END + 0x00E4,
+
+ PLAYER_DUEL_ARBITER = UNIT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_FLAGS = UNIT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_GUILDID = UNIT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_GUILDRANK = UNIT_END + 0x0004, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_BYTES = UNIT_END + 0x0005, // Size: 1, Type: BYTES, Flags: PUBLIC
+ PLAYER_BYTES_2 = UNIT_END + 0x0006, // Size: 1, Type: BYTES, Flags: PUBLIC
+ PLAYER_BYTES_3 = UNIT_END + 0x0007, // Size: 1, Type: BYTES, Flags: PUBLIC
+ PLAYER_DUEL_TEAM = UNIT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_GUILD_TIMESTAMP = UNIT_END + 0x0009, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_QUEST_LOG_1_1 = UNIT_END + 0x000A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_1_2 = UNIT_END + 0x000B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_1_3 = UNIT_END + 0x000C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_1_4 = UNIT_END + 0x000D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_2_1 = UNIT_END + 0x000E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_2_2 = UNIT_END + 0x000F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_2_3 = UNIT_END + 0x0010, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_2_4 = UNIT_END + 0x0011, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_3_1 = UNIT_END + 0x0012, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_3_2 = UNIT_END + 0x0013, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_3_3 = UNIT_END + 0x0014, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_3_4 = UNIT_END + 0x0015, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_4_1 = UNIT_END + 0x0016, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_4_2 = UNIT_END + 0x0017, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_4_3 = UNIT_END + 0x0018, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_4_4 = UNIT_END + 0x0019, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_5_1 = UNIT_END + 0x001A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_5_2 = UNIT_END + 0x001B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_5_3 = UNIT_END + 0x001C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_5_4 = UNIT_END + 0x001D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_6_1 = UNIT_END + 0x001E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_6_2 = UNIT_END + 0x001F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_6_3 = UNIT_END + 0x0020, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_6_4 = UNIT_END + 0x0021, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_7_1 = UNIT_END + 0x0022, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_7_2 = UNIT_END + 0x0023, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_7_3 = UNIT_END + 0x0024, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_7_4 = UNIT_END + 0x0025, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_8_1 = UNIT_END + 0x0026, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_8_2 = UNIT_END + 0x0027, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_8_3 = UNIT_END + 0x0028, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_8_4 = UNIT_END + 0x0029, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_9_1 = UNIT_END + 0x002A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_9_2 = UNIT_END + 0x002B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_9_3 = UNIT_END + 0x002C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_9_4 = UNIT_END + 0x002D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_10_1 = UNIT_END + 0x002E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_10_2 = UNIT_END + 0x002F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_10_3 = UNIT_END + 0x0030, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_10_4 = UNIT_END + 0x0031, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_11_1 = UNIT_END + 0x0032, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_11_2 = UNIT_END + 0x0033, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_11_3 = UNIT_END + 0x0034, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_11_4 = UNIT_END + 0x0035, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_12_1 = UNIT_END + 0x0036, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_12_2 = UNIT_END + 0x0037, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_12_3 = UNIT_END + 0x0038, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_12_4 = UNIT_END + 0x0039, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_13_1 = UNIT_END + 0x003A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_13_2 = UNIT_END + 0x003B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_13_3 = UNIT_END + 0x003C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_13_4 = UNIT_END + 0x003D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_14_1 = UNIT_END + 0x003E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_14_2 = UNIT_END + 0x003F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_14_3 = UNIT_END + 0x0040, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_14_4 = UNIT_END + 0x0041, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_15_1 = UNIT_END + 0x0042, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_15_2 = UNIT_END + 0x0043, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_15_3 = UNIT_END + 0x0044, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_15_4 = UNIT_END + 0x0045, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_16_1 = UNIT_END + 0x0046, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_16_2 = UNIT_END + 0x0047, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_16_3 = UNIT_END + 0x0048, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_16_4 = UNIT_END + 0x0049, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_17_1 = UNIT_END + 0x004A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_17_2 = UNIT_END + 0x004B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_17_3 = UNIT_END + 0x004C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_17_4 = UNIT_END + 0x004D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_18_1 = UNIT_END + 0x004E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_18_2 = UNIT_END + 0x004F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_18_3 = UNIT_END + 0x0050, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_18_4 = UNIT_END + 0x0051, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_19_1 = UNIT_END + 0x0052, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_19_2 = UNIT_END + 0x0053, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_19_3 = UNIT_END + 0x0054, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_19_4 = UNIT_END + 0x0055, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_20_1 = UNIT_END + 0x0056, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_20_2 = UNIT_END + 0x0057, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_20_3 = UNIT_END + 0x0058, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_20_4 = UNIT_END + 0x0059, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_21_1 = UNIT_END + 0x005A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_21_2 = UNIT_END + 0x005B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_21_3 = UNIT_END + 0x005C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_21_4 = UNIT_END + 0x005D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_22_1 = UNIT_END + 0x005E, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_22_2 = UNIT_END + 0x005F, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_22_3 = UNIT_END + 0x0060, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_22_4 = UNIT_END + 0x0061, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_23_1 = UNIT_END + 0x0062, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_23_2 = UNIT_END + 0x0063, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_23_3 = UNIT_END + 0x0064, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_23_4 = UNIT_END + 0x0065, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_24_1 = UNIT_END + 0x0066, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_24_2 = UNIT_END + 0x0067, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_24_3 = UNIT_END + 0x0068, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_24_4 = UNIT_END + 0x0069, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_25_1 = UNIT_END + 0x006A, // Size: 1, Type: INT, Flags: GROUP_ONLY
+ PLAYER_QUEST_LOG_25_2 = UNIT_END + 0x006B, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_QUEST_LOG_25_3 = UNIT_END + 0x006C, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_QUEST_LOG_25_4 = UNIT_END + 0x006D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_VISIBLE_ITEM_1_CREATOR = UNIT_END + 0x006E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_1_0 = UNIT_END + 0x0070, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_1_PROPERTIES = UNIT_END + 0x007C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_1_PAD = UNIT_END + 0x007D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_2_CREATOR = UNIT_END + 0x007E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_2_0 = UNIT_END + 0x0080, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_2_PROPERTIES = UNIT_END + 0x008C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_2_PAD = UNIT_END + 0x008D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_3_CREATOR = UNIT_END + 0x008E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_3_0 = UNIT_END + 0x0090, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_3_PROPERTIES = UNIT_END + 0x009C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_3_PAD = UNIT_END + 0x009D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_4_CREATOR = UNIT_END + 0x009E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_4_0 = UNIT_END + 0x00A0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_4_PROPERTIES = UNIT_END + 0x00AC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_4_PAD = UNIT_END + 0x00AD, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_5_CREATOR = UNIT_END + 0x00AE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_5_0 = UNIT_END + 0x00B0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_5_PROPERTIES = UNIT_END + 0x00BC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_5_PAD = UNIT_END + 0x00BD, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_6_CREATOR = UNIT_END + 0x00BE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_6_0 = UNIT_END + 0x00C0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_6_PROPERTIES = UNIT_END + 0x00CC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_6_PAD = UNIT_END + 0x00CD, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_7_CREATOR = UNIT_END + 0x00CE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_7_0 = UNIT_END + 0x00D0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_7_PROPERTIES = UNIT_END + 0x00DC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_7_PAD = UNIT_END + 0x00DD, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_8_CREATOR = UNIT_END + 0x00DE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_8_0 = UNIT_END + 0x00E0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_8_PROPERTIES = UNIT_END + 0x00EC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_8_PAD = UNIT_END + 0x00ED, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_9_CREATOR = UNIT_END + 0x00EE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_9_0 = UNIT_END + 0x00F0, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_9_PROPERTIES = UNIT_END + 0x00FC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_9_PAD = UNIT_END + 0x00FD, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_10_CREATOR = UNIT_END + 0x00FE, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_10_0 = UNIT_END + 0x0100, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_10_PROPERTIES = UNIT_END + 0x010C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_10_PAD = UNIT_END + 0x010D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_11_CREATOR = UNIT_END + 0x010E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_11_0 = UNIT_END + 0x0110, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_11_PROPERTIES = UNIT_END + 0x011C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_11_PAD = UNIT_END + 0x011D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_12_CREATOR = UNIT_END + 0x011E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_12_0 = UNIT_END + 0x0120, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_12_PROPERTIES = UNIT_END + 0x012C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_12_PAD = UNIT_END + 0x012D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_13_CREATOR = UNIT_END + 0x012E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_13_0 = UNIT_END + 0x0130, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_13_PROPERTIES = UNIT_END + 0x013C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_13_PAD = UNIT_END + 0x013D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_14_CREATOR = UNIT_END + 0x013E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_14_0 = UNIT_END + 0x0140, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_14_PROPERTIES = UNIT_END + 0x014C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_14_PAD = UNIT_END + 0x014D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_15_CREATOR = UNIT_END + 0x014E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_15_0 = UNIT_END + 0x0150, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_15_PROPERTIES = UNIT_END + 0x015C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_15_PAD = UNIT_END + 0x015D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_16_CREATOR = UNIT_END + 0x015E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_16_0 = UNIT_END + 0x0160, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_16_PROPERTIES = UNIT_END + 0x016C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_16_PAD = UNIT_END + 0x016D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_17_CREATOR = UNIT_END + 0x016E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_17_0 = UNIT_END + 0x0170, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_17_PROPERTIES = UNIT_END + 0x017C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_17_PAD = UNIT_END + 0x017D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_18_CREATOR = UNIT_END + 0x017E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_18_0 = UNIT_END + 0x0180, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_18_PROPERTIES = UNIT_END + 0x018C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_18_PAD = UNIT_END + 0x018D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_19_CREATOR = UNIT_END + 0x018E, // Size: 2, Type: LONG, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_19_0 = UNIT_END + 0x0190, // Size: 12, Type: INT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_19_PROPERTIES = UNIT_END + 0x019C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC
+ PLAYER_VISIBLE_ITEM_19_PAD = UNIT_END + 0x019D, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_CHOSEN_TITLE = UNIT_END + 0x019E, // Size: 1, Type: INT, Flags: PUBLIC
+ PLAYER_FIELD_PAD_0 = UNIT_END + 0x019F, // Size: 1, Type: INT, Flags: NONE
+ PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x01A0, // Size: 46, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x01CE, // Size: 32, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_BANK_SLOT_1 = UNIT_END + 0x01EE, // Size: 56, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_BANKBAG_SLOT_1 = UNIT_END + 0x0226, // Size: 14, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = UNIT_END + 0x0234, // Size: 24, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_KEYRING_SLOT_1 = UNIT_END + 0x024C, // Size: 64, Type: LONG, Flags: PRIVATE
+ PLAYER_FIELD_VANITYPET_SLOT_1 = UNIT_END + 0x028C, // Size: 36, Type: LONG, Flags: PRIVATE
+ PLAYER_FARSIGHT = UNIT_END + 0x02B0, // Size: 2, Type: LONG, Flags: PRIVATE
+ PLAYER__FIELD_KNOWN_TITLES = UNIT_END + 0x02B2, // Size: 2, Type: LONG, Flags: PRIVATE
+ PLAYER_XP = UNIT_END + 0x02B4, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_NEXT_LEVEL_XP = UNIT_END + 0x02B5, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_SKILL_INFO_1_1 = UNIT_END + 0x02B6, // Size: 384, Type: TWO_SHORT, Flags: PRIVATE
+ PLAYER_CHARACTER_POINTS1 = UNIT_END + 0x0436, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_CHARACTER_POINTS2 = UNIT_END + 0x0437, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_TRACK_CREATURES = UNIT_END + 0x0438, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_TRACK_RESOURCES = UNIT_END + 0x0439, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_BLOCK_PERCENTAGE = UNIT_END + 0x043A, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_DODGE_PERCENTAGE = UNIT_END + 0x043B, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_PARRY_PERCENTAGE = UNIT_END + 0x043C, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_EXPERTISE = UNIT_END + 0x043D, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_OFFHAND_EXPERTISE = UNIT_END + 0x043E, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_CRIT_PERCENTAGE = UNIT_END + 0x043F, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_RANGED_CRIT_PERCENTAGE = UNIT_END + 0x0440, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_OFFHAND_CRIT_PERCENTAGE = UNIT_END + 0x0441, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_SPELL_CRIT_PERCENTAGE1 = UNIT_END + 0x0442, // Size: 7, Type: FLOAT, Flags: PRIVATE
+ PLAYER_SHIELD_BLOCK = UNIT_END + 0x0449, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_EXPLORED_ZONES_1 = UNIT_END + 0x044A, // Size: 128, Type: BYTES, Flags: PRIVATE
+ PLAYER_REST_STATE_EXPERIENCE = UNIT_END + 0x04CA, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_COINAGE = UNIT_END + 0x04CB, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_DAMAGE_DONE_POS = UNIT_END + 0x04CC, // Size: 7, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = UNIT_END + 0x04D3, // Size: 7, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = UNIT_END + 0x04DA, // Size: 7, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_HEALING_DONE_POS = UNIT_END + 0x04E1, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_TARGET_RESISTANCE = UNIT_END + 0x04E2, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE = UNIT_END + 0x04E3, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_BYTES = UNIT_END + 0x04E4, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_AMMO_ID = UNIT_END + 0x04E5, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_SELF_RES_SPELL = UNIT_END + 0x04E6, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_PVP_MEDALS = UNIT_END + 0x04E7, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_BUYBACK_PRICE_1 = UNIT_END + 0x04E8, // Size: 12, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = UNIT_END + 0x04F4, // Size: 12, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_KILLS = UNIT_END + 0x0500, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE
+ PLAYER_FIELD_TODAY_CONTRIBUTION = UNIT_END + 0x0501, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_YESTERDAY_CONTRIBUTION = UNIT_END + 0x0502, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = UNIT_END + 0x0503, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_BYTES2 = UNIT_END + 0x0504, // Size: 1, Type: BYTES, Flags: PRIVATE
+ PLAYER_FIELD_WATCHED_FACTION_INDEX = UNIT_END + 0x0505, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_COMBAT_RATING_1 = UNIT_END + 0x0506, // Size: 24, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = UNIT_END + 0x051E, // Size: 18, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_HONOR_CURRENCY = UNIT_END + 0x0530, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_ARENA_CURRENCY = UNIT_END + 0x0531, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_MANA_REGEN = UNIT_END + 0x0532, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT = UNIT_END + 0x0533, // Size: 1, Type: FLOAT, Flags: PRIVATE
+ PLAYER_FIELD_MAX_LEVEL = UNIT_END + 0x0534, // Size: 1, Type: INT, Flags: PRIVATE
+ PLAYER_FIELD_DAILY_QUESTS_1 = UNIT_END + 0x0535, // Size: 25, Type: INT, Flags: PRIVATE
+ PLAYER_END = UNIT_END + 0x054E,
+};
+
+enum EGameObjectFields
+{
+ OBJECT_FIELD_CREATED_BY = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ GAMEOBJECT_DISPLAYID = OBJECT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_FLAGS = OBJECT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_ROTATION = OBJECT_END + 0x0004, // Size: 4, Type: FLOAT, Flags: PUBLIC
+ GAMEOBJECT_STATE = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_POS_X = OBJECT_END + 0x0009, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ GAMEOBJECT_POS_Y = OBJECT_END + 0x000A, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ GAMEOBJECT_POS_Z = OBJECT_END + 0x000B, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ GAMEOBJECT_FACING = OBJECT_END + 0x000C, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ GAMEOBJECT_DYN_FLAGS = OBJECT_END + 0x000D, // Size: 1, Type: INT, Flags: DYNAMIC
+ GAMEOBJECT_FACTION = OBJECT_END + 0x000E, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_TYPE_ID = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_LEVEL = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_ARTKIT = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC
+ GAMEOBJECT_ANIMPROGRESS = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: DYNAMIC
+ GAMEOBJECT_PADDING = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: NONE
+ GAMEOBJECT_END = OBJECT_END + 0x0014,
+};
+
+enum EDynamicObjectFields
+{
+ DYNAMICOBJECT_CASTER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ DYNAMICOBJECT_BYTES = OBJECT_END + 0x0002, // Size: 1, Type: BYTES, Flags: PUBLIC
+ DYNAMICOBJECT_SPELLID = OBJECT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC
+ DYNAMICOBJECT_RADIUS = OBJECT_END + 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ DYNAMICOBJECT_POS_X = OBJECT_END + 0x0005, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ DYNAMICOBJECT_POS_Y = OBJECT_END + 0x0006, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ DYNAMICOBJECT_POS_Z = OBJECT_END + 0x0007, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ DYNAMICOBJECT_FACING = OBJECT_END + 0x0008, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ DYNAMICOBJECT_CASTTIME = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: PUBLIC
+ DYNAMICOBJECT_END = OBJECT_END + 0x000A,
+};
+
+enum ECorpseFields
+{
+ CORPSE_FIELD_OWNER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
+ CORPSE_FIELD_PARTY = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC
+ CORPSE_FIELD_FACING = OBJECT_END + 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ CORPSE_FIELD_POS_X = OBJECT_END + 0x0005, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ CORPSE_FIELD_POS_Y = OBJECT_END + 0x0006, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ CORPSE_FIELD_POS_Z = OBJECT_END + 0x0007, // Size: 1, Type: FLOAT, Flags: PUBLIC
+ CORPSE_FIELD_DISPLAY_ID = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC
+ CORPSE_FIELD_ITEM = OBJECT_END + 0x0009, // Size: 19, Type: INT, Flags: PUBLIC
+ CORPSE_FIELD_BYTES_1 = OBJECT_END + 0x001C, // Size: 1, Type: BYTES, Flags: PUBLIC
+ CORPSE_FIELD_BYTES_2 = OBJECT_END + 0x001D, // Size: 1, Type: BYTES, Flags: PUBLIC
+ CORPSE_FIELD_GUILD = OBJECT_END + 0x001E, // Size: 1, Type: INT, Flags: PUBLIC
+ CORPSE_FIELD_FLAGS = OBJECT_END + 0x001F, // Size: 1, Type: INT, Flags: PUBLIC
+ CORPSE_FIELD_DYNAMIC_FLAGS = OBJECT_END + 0x0020, // Size: 1, Type: INT, Flags: DYNAMIC
+ CORPSE_FIELD_PAD = OBJECT_END + 0x0021, // Size: 1, Type: INT, Flags: NONE
+ CORPSE_END = OBJECT_END + 0x0022,
+};
+#endif
diff --git a/src/game/UpdateMask.h b/src/game/UpdateMask.h
new file mode 100644
index 00000000000..7b0b7bb0c6d
--- /dev/null
+++ b/src/game/UpdateMask.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef __UPDATEMASK_H
+#define __UPDATEMASK_H
+
+#include "UpdateFields.h"
+#include "Errors.h"
+
+class UpdateMask
+{
+ public:
+ UpdateMask( ) : mCount( 0 ), mBlocks( 0 ), mUpdateMask( 0 ) { }
+ UpdateMask( const UpdateMask& mask ) : mUpdateMask( 0 ) { *this = mask; }
+
+ ~UpdateMask( )
+ {
+ if(mUpdateMask)
+ delete [] mUpdateMask;
+ }
+
+ inline void SetBit (uint32 index)
+ {
+ ( (uint8 *)mUpdateMask )[ index >> 3 ] |= 1 << ( index & 0x7 );
+ }
+
+ inline void UnsetBit (uint32 index)
+ {
+ ( (uint8 *)mUpdateMask )[ index >> 3 ] &= (0xff ^ (1 << ( index & 0x7 ) ) );
+ }
+
+ inline bool GetBit (uint32 index)
+ {
+ return ( ( (uint8 *)mUpdateMask)[ index >> 3 ] & ( 1 << ( index & 0x7 ) )) != 0;
+ }
+
+ inline uint32 GetBlockCount() { return mBlocks; }
+ inline uint32 GetLength() { return mBlocks << 2; }
+ inline uint32 GetCount() { return mCount; }
+ inline uint8* GetMask() { return (uint8*)mUpdateMask; }
+
+ inline void SetCount (uint32 valuesCount)
+ {
+ if(mUpdateMask)
+ delete [] mUpdateMask;
+
+ mCount = valuesCount;
+ mBlocks = (valuesCount + 31) / 32;
+
+ mUpdateMask = new uint32[mBlocks];
+ memset(mUpdateMask, 0, mBlocks << 2);
+ }
+
+ inline void Clear()
+ {
+ if (mUpdateMask)
+ memset(mUpdateMask, 0, mBlocks << 2);
+ }
+
+ inline UpdateMask& operator = ( const UpdateMask& mask )
+ {
+ SetCount(mask.mCount);
+ memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2);
+
+ return *this;
+ }
+
+ inline void operator &= ( const UpdateMask& mask )
+ {
+ ASSERT(mask.mCount <= mCount);
+ for (uint32 i = 0; i < mBlocks; i++)
+ mUpdateMask[i] &= mask.mUpdateMask[i];
+ }
+
+ inline void operator |= ( const UpdateMask& mask )
+ {
+ ASSERT(mask.mCount <= mCount);
+ for (uint32 i = 0; i < mBlocks; i++)
+ mUpdateMask[i] |= mask.mUpdateMask[i];
+ }
+
+ inline UpdateMask operator & ( const UpdateMask& mask ) const
+ {
+ ASSERT(mask.mCount <= mCount);
+
+ UpdateMask newmask;
+ newmask = *this;
+ newmask &= mask;
+
+ return newmask;
+ }
+
+ inline UpdateMask operator | ( const UpdateMask& mask ) const
+ {
+ ASSERT(mask.mCount <= mCount);
+
+ UpdateMask newmask;
+ newmask = *this;
+ newmask |= mask;
+
+ return newmask;
+ }
+
+ private:
+ uint32 mCount;
+ uint32 mBlocks;
+ uint32 *mUpdateMask;
+};
+#endif
diff --git a/src/game/VoiceChatHandler.cpp b/src/game/VoiceChatHandler.cpp
new file mode 100644
index 00000000000..1368ddc4f85
--- /dev/null
+++ b/src/game/VoiceChatHandler.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Opcodes.h"
+#include "Log.h"
+
+void WorldSession::HandleVoiceSettingsOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_VOICE_SETTINGS");
+ // uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
+ recv_data.hexlike();
+}
+
+void WorldSession::HandleChannelEnableVoiceOpcode( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_CHANNEL_ENABLE_VOICE");
+ // Enable Voice button in channel context menu
+ recv_data.hexlike();
+}
+
+void WorldSession::HandleChannelVoiceChatQuery( WorldPacket & recv_data )
+{
+ sLog.outDebug("WORLD: CMSG_CHANNEL_VOICE_CHAT_QUERY");
+ // uint32, string
+ recv_data.hexlike();
+}
diff --git a/src/game/WaypointManager.cpp b/src/game/WaypointManager.cpp
new file mode 100644
index 00000000000..a4b809ddbef
--- /dev/null
+++ b/src/game/WaypointManager.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Database/DatabaseEnv.h"
+#include "GridDefines.h"
+#include "Policies/SingletonImp.h"
+#include "WaypointManager.h"
+#include "ProgressBar.h"
+#include "MapManager.h"
+
+INSTANTIATE_SINGLETON_1(WaypointManager);
+
+bool WaypointBehavior::isEmpty()
+{
+ return emote == 0 && spell == 0 && model1 == 0 && model2 == 0 && text[0].empty() &&
+ text[1].empty() && text[2].empty() && text[3].empty() && text[4].empty();
+}
+
+WaypointBehavior::WaypointBehavior(const WaypointBehavior &b)
+{
+ emote = b.emote; spell = b.spell; model1 = b.model1; model2 = b.model2;
+ text[0] = b.text[0]; text[1] = b.text[1]; text[2] = b.text[2];
+ text[3] = b.text[3]; text[4] = b.text[4];
+}
+
+void WaypointManager::Load()
+{
+ Cleanup();
+
+ uint32 total_paths = 0;
+ uint32 total_nodes = 0;
+ uint32 total_behaviors = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id");
+ if(result)
+ {
+ total_paths = result->GetRowCount();
+ barGoLink bar( total_paths );
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 id = fields[0].GetUInt32();
+ uint32 count = fields[1].GetUInt32();
+ m_pathMap[id].resize(count);
+
+ total_nodes += count;
+ bar.step();
+ } while( result->NextRow() );
+ delete result;
+ }
+
+ result = WorldDatabase.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id, point FROM creature_movement");
+ if(result)
+ {
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 point = fields[15].GetUInt32();
+ uint32 id = fields[14].GetUInt32();
+
+ WaypointPath &path = m_pathMap[id];
+ // the cleanup queries make sure the following is true
+ assert(point >= 1 && point <= path.size());
+ WaypointNode &node = path[point-1];
+
+ node.x = fields[0].GetFloat();
+ node.y = fields[1].GetFloat();
+ node.z = fields[2].GetFloat();
+ node.orientation = fields[3].GetFloat();
+ node.delay = fields[6].GetUInt16();
+
+ // prevent using invalid coordinates
+ if(!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation))
+ {
+ QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id);
+ if(result1) sLog.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y);
+ else sLog.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, point, node.x, node.y);
+
+ MaNGOS::NormalizeMapCoord(node.x);
+ MaNGOS::NormalizeMapCoord(node.y);
+ if(result1)
+ {
+ node.z = MapManager::Instance ().GetBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z);
+ delete result1;
+ }
+ WorldDatabase.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node.x, node.y, node.z, id, point);
+ }
+
+ WaypointBehavior be;
+ be.model1 = fields[4].GetUInt32();
+ be.model2 = fields[5].GetUInt32();
+ be.emote = fields[7].GetUInt32();
+ be.spell = fields[8].GetUInt32();
+ be.text[0] = fields[9].GetCppString();
+ be.text[1] = fields[10].GetCppString();
+ be.text[2] = fields[11].GetCppString();
+ be.text[3] = fields[12].GetCppString();
+ be.text[4] = fields[13].GetCppString();
+
+ // save memory by not storing empty behaviors
+ if(!be.isEmpty())
+ {
+ node.behavior = new WaypointBehavior(be);
+ ++total_behaviors;
+ }
+ else
+ node.behavior = NULL;
+ bar.step();
+ } while( result->NextRow() );
+ delete result;
+ }
+ sLog.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors);
+}
+
+void WaypointManager::Cleanup()
+{
+ // check if points need to be renumbered and do it
+ if(QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1"))
+ {
+ delete result;
+ WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement");
+ WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement");
+ WorldDatabase.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY");
+ WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)");
+ WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)");
+ WorldDatabase.DirectExecute("DROP TABLE temp");
+ assert(!(result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")));
+ }
+}
+
+void WaypointManager::Unload()
+{
+ for(WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr)
+ _clearPath(itr->second);
+ m_pathMap.clear();
+}
+
+void WaypointManager::_clearPath(WaypointPath &path)
+{
+ for(WaypointPath::iterator itr = path.begin(); itr != path.end(); ++itr)
+ if(itr->behavior)
+ delete itr->behavior;
+ path.clear();
+}
+
+/// - Insert after the last point
+void WaypointManager::AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid)
+{
+ _addNode(id, GetLastPoint(id, 0) + 1, x, y, z, o, delay, wpGuid);
+}
+
+/// - Insert after a certain point
+void WaypointManager::AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid)
+{
+ for(uint32 i = GetLastPoint(id, 0); i > point; i--)
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id, i);
+
+ _addNode(id, point + 1, x, y, z, o, delay, wpGuid);
+}
+
+/// - Insert without checking for collision
+void WaypointManager::_addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid)
+{
+ if(point == 0) return; // counted from 1 in the DB
+ WorldDatabase.PExecuteLog("INSERT INTO creature_movement (id,point,position_x,position_y,position_z,orientation,wpguid,waittime) VALUES ('%u','%u','%f', '%f', '%f', '%f', '%d', '%d')", id, point, x, y, z, o, wpGuid, delay);
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr == m_pathMap.end())
+ itr = m_pathMap.insert(WaypointPathMap::value_type(id, WaypointPath())).first;
+ itr->second.insert(itr->second.begin() + (point - 1), WaypointNode(x, y, z, o, delay, NULL));
+}
+
+uint32 WaypointManager::GetLastPoint(uint32 id, uint32 default_notfound)
+{
+ uint32 point = default_notfound;
+ /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id);
+ if( result )
+ {
+ point = (*result)[0].GetUInt32()+1;
+ delete result;
+ }*/
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr != m_pathMap.end() && itr->second.size() != 0)
+ point = itr->second.size();
+ return point;
+}
+
+void WaypointManager::DeleteNode(uint32 id, uint32 point)
+{
+ if(point == 0) return; // counted from 1 in the DB
+ WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id, point);
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id, point);
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr != m_pathMap.end() && point <= itr->second.size())
+ itr->second.erase(itr->second.begin() + (point-1));
+}
+
+void WaypointManager::DeletePath(uint32 id)
+{
+ WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id);
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr != m_pathMap.end())
+ _clearPath(itr->second);
+ // the path is not removed from the map, just cleared
+ // WMGs have pointers to the path, so deleting them would crash
+ // this wastes some memory, but these functions are
+ // only meant to be called by GM commands
+}
+
+void WaypointManager::SetNodePosition(uint32 id, uint32 point, float x, float y, float z)
+{
+ if(point == 0) return; // counted from 1 in the DB
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", x, y, z, id, point);
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr != m_pathMap.end() && point <= itr->second.size())
+ {
+ itr->second[point-1].x = x;
+ itr->second[point-1].y = y;
+ itr->second[point-1].z = z;
+ }
+}
+
+void WaypointManager::SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text)
+{
+ if(point == 0) return; // counted from 1 in the DB
+ if(!text_field) return;
+ std::string field = text_field;
+ WorldDatabase.escape_string(field);
+
+ if(!text)
+ {
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point);
+ }
+ else
+ {
+ std::string text2 = text;
+ WorldDatabase.escape_string(text2);
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field.c_str(), text2.c_str(), id, point);
+ }
+
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ if(itr != m_pathMap.end() && point <= itr->second.size())
+ {
+ WaypointNode &node = itr->second[point-1];
+ if(!node.behavior) node.behavior = new WaypointBehavior();
+
+ if(field == "text1") node.behavior->text[0] = text ? text : "";
+ if(field == "text2") node.behavior->text[1] = text ? text : "";
+ if(field == "text3") node.behavior->text[2] = text ? text : "";
+ if(field == "text4") node.behavior->text[3] = text ? text : "";
+ if(field == "text5") node.behavior->text[4] = text ? text : "";
+ if(field == "emote") node.behavior->emote = text ? atoi(text) : 0;
+ if(field == "spell") node.behavior->spell = text ? atoi(text) : 0;
+ if(field == "model1") node.behavior->model1 = text ? atoi(text) : 0;
+ if(field == "model2") node.behavior->model2 = text ? atoi(text) : 0;
+ }
+}
diff --git a/src/game/WaypointManager.h b/src/game/WaypointManager.h
new file mode 100644
index 00000000000..f0b66b66077
--- /dev/null
+++ b/src/game/WaypointManager.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_WAYPOINTMANAGER_H
+#define MANGOS_WAYPOINTMANAGER_H
+
+#include <vector>
+#include <string>
+#include "Utilities/HashMap.h"
+
+struct WaypointBehavior
+{
+ uint32 emote;
+ uint32 spell;
+ std::string text[5];
+ uint32 model1;
+ uint32 model2;
+
+ bool isEmpty();
+ WaypointBehavior() {}
+ WaypointBehavior(const WaypointBehavior &b);
+};
+
+struct WaypointNode
+{
+ float x;
+ float y;
+ float z;
+ float orientation;
+ uint32 delay;
+ WaypointBehavior * behavior;
+ WaypointNode() {}
+ WaypointNode(float _x, float _y, float _z, float _o, uint32 _delay, WaypointBehavior * _behavior)
+ : x(_x), y(_y), z(_z), orientation(_o), delay(_delay), behavior(_behavior) {}
+};
+
+typedef std::vector<WaypointNode> WaypointPath;
+
+class WaypointManager
+{
+ public:
+ WaypointManager() {}
+ ~WaypointManager() { Unload(); }
+
+ void Load();
+ void Unload();
+
+ void Cleanup();
+
+ WaypointPath *GetPath(uint32 id)
+ {
+ WaypointPathMap::iterator itr = m_pathMap.find(id);
+ return itr != m_pathMap.end() ? &itr->second : NULL;
+ }
+
+ void AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid);
+ void AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid);
+ uint32 GetLastPoint(uint32 id, uint32 default_notfound);
+ void DeleteNode(uint32 id, uint32 point);
+ void DeletePath(uint32 id);
+ void SetNodePosition(uint32 id, uint32 point, float x, float y, float z);
+ void SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text);
+
+ private:
+ void _addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid);
+ void _clearPath(WaypointPath &path);
+
+ typedef HM_NAMESPACE::hash_map<uint32, WaypointPath> WaypointPathMap;
+ WaypointPathMap m_pathMap;
+};
+
+#define WaypointMgr MaNGOS::Singleton<WaypointManager>::Instance()
+
+#endif
diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp
new file mode 100644
index 00000000000..0389e2ebc4b
--- /dev/null
+++ b/src/game/WaypointMovementGenerator.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/*
+creature_movement Table
+
+alter table creature_movement add `text1` varchar(255) default NULL;
+alter table creature_movement add `text2` varchar(255) default NULL;
+alter table creature_movement add `text3` varchar(255) default NULL;
+alter table creature_movement add `text4` varchar(255) default NULL;
+alter table creature_movement add `text5` varchar(255) default NULL;
+alter table creature_movement add `emote` int(10) unsigned default '0';
+alter table creature_movement add `spell` int(5) unsigned default '0';
+alter table creature_movement add `wpguid` int(11) default '0';
+
+*/
+
+#include <ctime>
+
+#include "WaypointMovementGenerator.h"
+#include "ObjectMgr.h"
+#include "Creature.h"
+#include "DestinationHolderImp.h"
+#include "CreatureAI.h"
+#include "WaypointManager.h"
+
+#include <cassert>
+
+//-----------------------------------------------//
+void
+WaypointMovementGenerator<Creature>::LoadPath(Creature &c)
+{
+ sLog.outDetail("LoadPath: loading waypoint path for creature %d,%d", c.GetGUIDLow(), c.GetDBTableGUIDLow());
+
+ i_path = WaypointMgr.GetPath(c.GetDBTableGUIDLow());
+ if(!i_path)
+ {
+ sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s(%d) doesn't have waypoint path", c.GetName(), c.GetDBTableGUIDLow());
+ return;
+ }
+
+ uint32 node_count = i_path->size();
+ i_hasDone.resize(node_count);
+ for(uint32 i = 0; i < node_count-1; i++)
+ i_hasDone[i] = false;
+
+ // to prevent a misbehaviour inside "update"
+ // update is always called with the next wp - but the wpSys needs the current
+ // so when the routine is called the first time, wpSys gets the last waypoint
+ // and this prevents the system from performing text/emote, etc
+ i_hasDone[node_count - 1] = true;
+}
+
+void
+WaypointMovementGenerator<Creature>::ClearWaypoints()
+{
+ i_path = NULL;
+}
+
+void
+WaypointMovementGenerator<Creature>::Initialize()
+{
+}
+
+bool
+WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ if(!&creature)
+ return true;
+
+ // Waypoint movement can be switched on/off
+ // This is quite handy for escort quests and other stuff
+ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ // prevent a crash at empty waypoint path.
+ if(!i_path || i_path->empty())
+ return true;
+
+ // i_path was modified by chat commands for example
+ if(i_path->size() != i_hasDone.size())
+ i_hasDone.resize(i_path->size());
+ if(i_currentNode >= i_path->size())
+ i_currentNode = 0;
+
+ CreatureTraveller traveller(creature);
+
+ i_nextMoveTime.Update(diff);
+ i_destinationHolder.UpdateTraveller(traveller, diff, false, true);
+
+ // creature has been stoped in middle of the waypoint segment
+ if (!i_destinationHolder.HasArrived() && creature.IsStopped())
+ {
+ if( i_nextMoveTime.Passed()) // Timer has elapsed, meaning this part controlled it
+ {
+ SetStopedByPlayer(false);
+ // Now we re-set destination to same node and start travel
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ const WaypointNode &node = i_path->at(i_currentNode);
+ i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ }
+ else // if( !i_nextMoveTime.Passed())
+ { // unexpected end of timer && creature stopped && not at end of segment
+ if (!IsStopedByPlayer())
+ { // Put 30 seconds delay
+ i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER);
+ i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER);
+ SetStopedByPlayer(true); // Mark we did it
+ }
+ }
+ return true; // Abort here this update
+ }
+
+ if( creature.IsStopped())
+ {
+ uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
+
+ if (!i_hasDone[idx])
+ {
+ if (i_path->at(idx).orientation !=100)
+ creature.SetOrientation(i_path->at(idx).orientation);
+
+ if(WaypointBehavior *behavior = i_path->at(idx).behavior)
+ {
+ if(behavior->emote != 0)
+ creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,behavior->emote);
+ if(behavior->spell != 0)
+ creature.CastSpell(&creature,behavior->spell, false);
+ if(behavior->model1 != 0)
+ creature.SetDisplayId(behavior->model1);
+ if(!behavior->text[0].empty())
+ {
+ // Only one text is set
+ if( !behavior->text[1].empty() )
+ {
+ // Select one from max 5 texts (0 and 1 laready checked)
+ int i = 2;
+ for( ; i < 5; ++i )
+ if( behavior->text[i].empty() )
+ break;
+
+ creature.Say(behavior->text[rand() % i].c_str(), 0, 0);
+
+ }
+ else
+ creature.Say(behavior->text[0].c_str(), 0, 0);
+ }
+
+ i_hasDone[idx] = true;
+ MovementInform(creature);
+ } // wpBehaviour found
+ } // HasDone == false
+ } // i_creature.IsStopped()
+
+ if( i_nextMoveTime.Passed() ) // This is at the end of waypoint segment or has been stopped by player
+ {
+ if( creature.IsStopped() ) // If stopped then begin a new move segment
+ {
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ const WaypointNode &node = i_path->at(i_currentNode);
+ i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
+
+ if (i_path->at(idx).orientation !=100)
+ creature.SetOrientation(i_path->at(idx).orientation);
+
+ if(WaypointBehavior *behavior = i_path->at(idx).behavior )
+ {
+ i_hasDone[idx] = false;
+ if(behavior->model2 != 0)
+ creature.SetDisplayId(behavior->model2);
+
+ creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
+ }
+ }
+ else // If not stopped then stop it and set the reset of TimeTracker to waittime
+ {
+ creature.StopMoving();
+ SetStopedByPlayer(false);
+ i_nextMoveTime.Reset(i_path->at(i_currentNode).delay);
+ ++i_currentNode;
+ if( i_currentNode >= i_path->size() )
+ i_currentNode = 0;
+ }
+ }
+ return true;
+}
+
+void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ if(unit.AI())
+ unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+}
+
+//----------------------------------------------------//
+void
+FlightPathMovementGenerator::LoadPath(Player &)
+{
+ objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds);
+}
+
+uint32
+FlightPathMovementGenerator::GetPathAtMapEnd() const
+{
+ if(i_currentNode >= i_mapIds.size())
+ return i_mapIds.size();
+
+ uint32 curMapId = i_mapIds[i_currentNode];
+ for(uint32 i = i_currentNode; i < i_mapIds.size(); ++i)
+ {
+ if(i_mapIds[i] != curMapId)
+ return i;
+ }
+
+ return i_mapIds.size();
+}
+
+void
+FlightPathMovementGenerator::Initialize(Player &player)
+{
+ player.getHostilRefManager().setOnlineOfflineState(false);
+ player.addUnitState(UNIT_STAT_IN_FLIGHT);
+ player.SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ LoadPath(player);
+ Traveller<Player> traveller(player);
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+
+ player.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(),MOVEMENTFLAG_WALK_MODE|MOVEMENTFLAG_ONTRANSPORT);
+}
+
+void FlightPathMovementGenerator::Finalize(Player & player)
+{
+
+ float x, y, z;
+ i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z);
+ player.SetPosition(x, y, z, player.GetOrientation());
+
+ player.clearUnitState(UNIT_STAT_IN_FLIGHT);
+ player.Unmount();
+ player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+
+ if(player.m_taxi.empty())
+ {
+ player.getHostilRefManager().setOnlineOfflineState(true);
+ if(player.pvpInfo.inHostileArea)
+ player.CastSpell(&player, 2479, true);
+
+ player.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
+ player.StopMoving();
+ }
+}
+
+bool
+FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
+{
+ if( MovementInProgress() )
+ {
+ Traveller<Player> traveller(player);
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false) )
+ {
+ i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE);
+ if( i_destinationHolder.HasArrived() )
+ {
+ uint32 curMap = i_mapIds[i_currentNode];
+ ++i_currentNode;
+ if( MovementInProgress() )
+ {
+ DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName());
+ if(i_mapIds[i_currentNode]==curMap)
+ {
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+ }
+ return true;
+ }
+ //else HasArrived()
+ }
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+
+ // we have arrived at the end of the path
+ return false;
+}
+
+void
+FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
+{
+ if(i_mapIds.empty())
+ return;
+
+ uint32 map0 = i_mapIds[0];
+ for(int i = 1; i < i_mapIds.size(); ++i)
+ {
+ if(i_mapIds[i]!=map0)
+ {
+ i_currentNode = i;
+ return;
+ }
+ }
+}
+
+//
+// Unique1's ASTAR Pathfinding Code... For future use & reference...
+//
+
+#ifdef __PATHFINDING__
+
+int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
+
+int ShortenASTARRoute(short int *pathlist, int number)
+{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
+ short int temppathlist[MAX_PATHLIST_NODES];
+ int count = 0;
+ // int count2 = 0;
+ int temp, temp2;
+ int link;
+ int upto = 0;
+
+ for (temp = number; temp >= 0; temp--)
+ {
+ qboolean shortened = qfalse;
+
+ for (temp2 = 0; temp2 < temp; temp2++)
+ {
+ for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
+ {
+ if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
+ continue;
+
+ //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
+ // continue;
+
+ //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
+ // continue;
+
+ if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
+ { // Found a shorter route...
+ //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
+ {
+ temppathlist[count] = pathlist[temp2];
+ temp = temp2;
+ ++count;
+ shortened = qtrue;
+ }
+ }
+ }
+ }
+
+ if (!shortened)
+ {
+ temppathlist[count] = pathlist[temp];
+ ++count;
+ }
+ }
+
+ upto = count;
+
+ for (temp = 0; temp < count; temp++)
+ {
+ pathlist[temp] = temppathlist[upto];
+ --upto;
+ }
+
+ G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
+ return count;
+}
+
+/*
+===========================================================================
+CreatePathAStar
+This function uses the A* pathfinding algorithm to determine the
+shortest path between any two nodes.
+It's fairly complex, so I'm not really going to explain it much.
+Look up A* and binary heaps for more info.
+pathlist stores the ideal path between the nodes, in reverse order,
+and the return value is the number of nodes in that path
+===========================================================================
+*/
+int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
+{
+ //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
+ //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
+ short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
+ float gcost[MAX_NODES];
+ int fcost[MAX_NODES];
+ char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
+ short int parent[MAX_NODES];
+
+ short int numOpen = 0;
+ short int atNode, temp, newnode=-1;
+ qboolean found = qfalse;
+ int count = -1;
+ float gc;
+ int i, u, v, m;
+ vec3_t vec;
+
+ //clear out all the arrays
+ memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
+ memset(fcost, 0, sizeof(int)*MAX_NODES);
+ memset(list, 0, sizeof(char)*MAX_NODES);
+ memset(parent, 0, sizeof(short int)*MAX_NODES);
+ memset(gcost, -1, sizeof(float)*MAX_NODES);
+
+ //make sure we have valid data before calculating everything
+ if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
+ return -1;
+
+ openlist[1] = from; //add the starting node to the open list
+ ++numOpen;
+ gcost[from] = 0; //its f and g costs are obviously 0
+ fcost[from] = 0;
+
+ while (1)
+ {
+ if (numOpen != 0) //if there are still items in the open list
+ {
+ //pop the top item off of the list
+ atNode = openlist[1];
+ list[atNode] = 2; //put the node on the closed list so we don't check it again
+ --numOpen;
+
+ openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
+ v = 1;
+
+ //this while loop reorders the list so that the new lowest fcost is at the top again
+ while (1)
+ {
+ u = v;
+ if ((2*u+1) < numOpen) //if both children exist
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
+ v = 2*u+1;
+ }
+ else
+ {
+ if ((2*u) < numOpen) //if only one child exists
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ }
+ }
+
+ if (u != v) //if they're out of order, swap this item with its parent
+ {
+ temp = openlist[u];
+ openlist[u] = openlist[v];
+ openlist[v] = temp;
+ }
+ else
+ break;
+ }
+
+ for (i = 0; i < nodes[atNode].enodenum; i++) //loop through all the links for this node
+ {
+ newnode = nodes[atNode].links[i].targetNode;
+
+ //if this path is blocked, skip it
+ if (nodes[atNode].links[i].flags & PATH_BLOCKED)
+ continue;
+ //if this path is blocked, skip it
+ if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
+ continue;
+ //skip any unreachable nodes
+ if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
+ continue;
+ if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
+ continue;
+
+ if (list[newnode] == 2) //if this node is on the closed list, skip it
+ continue;
+
+ if (list[newnode] != 1) //if this node is not already on the open list
+ {
+ openlist[++numOpen] = newnode; //add the new node to the open list
+ list[newnode] = 1;
+ parent[newnode] = atNode; //record the node's parent
+
+ if (newnode == to) //if we've found the goal, don't keep computing paths!
+ break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
+
+ //store it's f cost value
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //this loop re-orders the heap so that the lowest fcost is at the top
+ m = numOpen;
+ while (m != 1) //while this item isn't at the top of the heap already
+ {
+ //if it has a lower fcost than its parent
+ if (fcost[openlist[m]] <= fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ }
+ else //if this node is already on the open list
+ {
+ gc = gcost[atNode];
+ VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
+ gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
+
+ if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
+ {
+ parent[newnode] = atNode; //set the new parent for this node
+ gcost[newnode] = gc; //and the new g cost
+
+ for (i = 1; i < numOpen; i++) //loop through all the items on the open list
+ {
+ if (openlist[i] == newnode) //find this node in the list
+ {
+ //calculate the new fcost and store it
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //reorder the list again, with the lowest fcost item on top
+ m = i;
+ while (m != 1)
+ {
+ //if the item has a lower fcost than it's parent
+ if (fcost[openlist[m]] < fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ break; //exit the 'for' loop because we already changed this node
+ } //if
+ } //for
+ } //if (gc < gcost[newnode])
+ } //if (list[newnode] != 1) --> else
+ } //for (loop through links)
+ } //if (numOpen != 0)
+ else
+ {
+ found = qfalse; //there is no path between these nodes
+ break;
+ }
+
+ if (list[to] == 1) //if the destination node is on the open list, we're done
+ {
+ found = qtrue;
+ break;
+ }
+ } //while (1)
+
+ if (found == qtrue) //if we found a path
+ {
+ //G_Printf("%s - path found!n", bot->client->pers.netname);
+ count = 0;
+
+ temp = to; //start at the end point
+ while (temp != from) //travel along the path (backwards) until we reach the starting point
+ {
+ pathlist[count++] = temp; //add the node to the pathlist and increment the count
+ temp = parent[temp]; //move to the parent of this node to continue the path
+ }
+
+ pathlist[count++] = from; //add the beginning node to the end of the pathlist
+
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ }
+ else
+ {
+ //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
+ count = CreateDumbRoute(from, to, pathlist);
+
+ if (count > 0)
+ {
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ return count;
+ }
+ }
+
+ return count; //return the number of nodes in the path, -1 if not found
+}
+
+/*
+===========================================================================
+GetFCost
+Utility function used by A* pathfinding to calculate the
+cost to move between nodes towards a goal. Using the A*
+algorithm F = G + H, G here is the distance along the node
+paths the bot must travel, and H is the straight-line distance
+to the goal node.
+Returned as an int because more precision is unnecessary and it
+will slightly speed up heap access
+===========================================================================
+*/
+int GetFCost(int to, int num, int parentNum, float *gcost)
+{
+ float gc = 0;
+ float hc = 0;
+ vec3_t v;
+
+ if (gcost[num] == -1)
+ {
+ if (parentNum != -1)
+ {
+ gc = gcost[parentNum];
+ VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
+ gc += VectorLength(v);
+ }
+ gcost[num] = gc;
+ }
+ else
+ gc = gcost[num];
+
+ VectorSubtract(nodes[to].origin, nodes[num].origin, v);
+ hc = VectorLength(v);
+
+ return (int)(gc + hc);
+}
+#endif //__PATHFINDING__
diff --git a/src/game/WaypointMovementGenerator.h b/src/game/WaypointMovementGenerator.h
new file mode 100644
index 00000000000..94762a961b6
--- /dev/null
+++ b/src/game/WaypointMovementGenerator.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_WAYPOINTMOVEMENTGENERATOR_H
+#define MANGOS_WAYPOINTMOVEMENTGENERATOR_H
+
+/** @page PathMovementGenerator is used to generate movements
+ * of waypoints and flight paths. Each serves the purpose
+ * of generate activities so that it generates updated
+ * packets for the players.
+ */
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "WaypointManager.h"
+#include "Path.h"
+#include "Traveller.h"
+
+#include "Player.h"
+
+#include <vector>
+#include <set>
+
+#define FLIGHT_TRAVEL_UPDATE 100
+#define STOP_TIME_FOR_PLAYER 3 * 60 * 1000 // 3 Minutes
+
+template<class T, class P = Path>
+class MANGOS_DLL_SPEC PathMovementBase
+{
+ public:
+ PathMovementBase() : i_currentNode(0) {}
+ virtual ~PathMovementBase() {};
+
+ inline bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); }
+
+ // template pattern, not defined .. override required
+ void LoadPath(T &);
+ void ReloadPath(T &);
+ uint32 GetCurrentNode() const { return i_currentNode; }
+
+ bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; }
+ protected:
+ uint32 i_currentNode;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ P i_path;
+};
+
+/** WaypointMovementGenerator loads a series of way points
+ * from the DB and apply it to the creature's movement generator.
+ * Hence, the creature will move according to its predefined way points.
+ */
+
+template<class T>
+class MANGOS_DLL_SPEC WaypointMovementGenerator;
+
+template<>
+class MANGOS_DLL_SPEC WaypointMovementGenerator<Creature>
+: public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >,
+public PathMovementBase<Creature, WaypointPath*>
+{
+ TimeTrackerSmall i_nextMoveTime;
+ std::vector<bool> i_hasDone;
+ public:
+ WaypointMovementGenerator(Creature &) : i_nextMoveTime(0) {}
+ ~WaypointMovementGenerator() { ClearWaypoints(); }
+ void Initialize(Creature &u)
+ {
+ i_nextMoveTime.Reset(0); // TODO: check the lower bound (0 is probably too small)
+ u.StopMoving();
+ LoadPath(u);
+ }
+ void Finalize(Creature &) {}
+ void Reset(Creature &u) { ReloadPath(u); }
+ bool Update(Creature &u, const uint32 &diff);
+
+ void MovementInform(Creature &);
+
+ MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
+
+ // now path movement implmementation
+ void LoadPath(Creature &c);
+ void ReloadPath(Creature &c) { ClearWaypoints(); LoadPath(c); }
+
+ // Player stoping creature
+ bool IsStopedByPlayer() { return b_StopedByPlayer; }
+ void SetStopedByPlayer(bool val) { b_StopedByPlayer = val; }
+
+ // statics
+ static void Initialize(void);
+ private:
+ void ClearWaypoints();
+ bool b_StopedByPlayer;
+};
+
+/** FlightPathMovementGenerator generates movement of the player for the paths
+ * and hence generates ground and activities for the player.
+ */
+class MANGOS_DLL_SPEC FlightPathMovementGenerator
+: public MovementGeneratorMedium< Player, FlightPathMovementGenerator >,
+public PathMovementBase<Player>
+{
+ uint32 i_pathId;
+ std::vector<uint32> i_mapIds;
+ public:
+ explicit FlightPathMovementGenerator(uint32 id, uint32 startNode = 0) : i_pathId(id) { i_currentNode = startNode; }
+ void Initialize(Player &);
+ void Finalize(Player &);
+ void Reset(Player &) {}
+ bool Update(Player &, const uint32 &);
+ MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
+
+ void LoadPath(Player &);
+ void ReloadPath(Player &) { /* don't reload flight path */ }
+
+ Path& GetPath() { return i_path; }
+ uint32 GetPathAtMapEnd() const;
+ inline bool HasArrived() const { return (i_currentNode >= i_path.Size()); }
+ void SetCurrentNodeAfterTeleport();
+ void SkipCurrentNode() { ++i_currentNode; }
+};
+#endif
diff --git a/src/game/Weather.cpp b/src/game/Weather.cpp
new file mode 100644
index 00000000000..77e81f0b16d
--- /dev/null
+++ b/src/game/Weather.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup world
+*/
+
+#include "Weather.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "World.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Util.h"
+
+/// Create the Weather object
+Weather::Weather(uint32 zone, WeatherZoneChances const* weatherChances) : m_zone(zone), m_weatherChances(weatherChances)
+{
+ m_timer.SetInterval(sWorld.getConfig(CONFIG_INTERVAL_CHANGEWEATHER));
+ m_type = WEATHER_TYPE_FINE;
+ m_grade = 0;
+
+ sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (1000*MINUTE)) );
+}
+
+/// Launch a weather update
+bool Weather::Update(time_t diff)
+{
+ if (m_timer.GetCurrent()>=0)
+ m_timer.Update(diff);
+ else m_timer.SetCurrent(0);
+
+ ///- If the timer has passed, ReGenerate the weather
+ if(m_timer.Passed())
+ {
+ m_timer.Reset();
+ // update only if Regenerate has changed the weather
+ if(ReGenerate())
+ {
+ ///- Weather will be removed if not updated (no players in zone anymore)
+ if(!UpdateWeather())
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Calculate the new weather
+bool Weather::ReGenerate()
+{
+ if (!m_weatherChances)
+ {
+ m_type = WEATHER_TYPE_FINE;
+ m_grade = 0.0f;
+ return false;
+ }
+
+ /// Weather statistics:
+ ///- 30% - no change
+ ///- 30% - weather gets better (if not fine) or change weather type
+ ///- 30% - weather worsens (if not fine)
+ ///- 10% - radical change (if not fine)
+ uint32 u = urand(0, 99);
+
+ if (u < 30)
+ return false;
+
+ // remember old values
+ WeatherType old_type = m_type;
+ float old_grade = m_grade;
+
+ //78 days between January 1st and March 20nd; 365/4=91 days by season
+ // season source http://aa.usno.navy.mil/data/docs/EarthSeasons.html
+ time_t gtime = sWorld.GetGameTime();
+ struct tm * ltime = localtime(&gtime);
+ uint32 season = ((ltime->tm_yday - 78 + 365)/91)%4;
+
+ static char const* seasonName[WEATHER_SEASONS] = { "spring", "summer", "fall", "winter" };
+
+ sLog.outDebug("Generating a change in %s weather for zone %u.", seasonName[season], m_zone);
+
+ if ((u < 60) && (m_grade < 0.33333334f)) // Get fair
+ {
+ m_type = WEATHER_TYPE_FINE;
+ m_grade = 0.0f;
+ }
+
+ if ((u < 60) && (m_type != WEATHER_TYPE_FINE)) // Get better
+ {
+ m_grade -= 0.33333334f;
+ return true;
+ }
+
+ if ((u < 90) && (m_type != WEATHER_TYPE_FINE)) // Get worse
+ {
+ m_grade += 0.33333334f;
+ return true;
+ }
+
+ if (m_type != WEATHER_TYPE_FINE)
+ {
+ /// Radical change:
+ ///- if light -> heavy
+ ///- if medium -> change weather type
+ ///- if heavy -> 50% light, 50% change weather type
+
+ if (m_grade < 0.33333334f)
+ {
+ m_grade = 0.9999f; // go nuts
+ return true;
+ }
+ else
+ {
+ if (m_grade > 0.6666667f)
+ {
+ // Severe change, but how severe?
+ uint32 rnd = urand(0,99);
+ if (rnd < 50)
+ {
+ m_grade -= 0.6666667f;
+ return true;
+ }
+ }
+ m_type = WEATHER_TYPE_FINE; // clear up
+ m_grade = 0;
+ }
+ }
+
+ // At this point, only weather that isn't doing anything remains but that have weather data
+ uint32 chance1 = m_weatherChances->data[season].rainChance;
+ uint32 chance2 = chance1+ m_weatherChances->data[season].snowChance;
+ uint32 chance3 = chance2+ m_weatherChances->data[season].stormChance;
+
+ uint32 rnd = urand(0, 99);
+ if(rnd <= chance1)
+ m_type = WEATHER_TYPE_RAIN;
+ else if(rnd <= chance2)
+ m_type = WEATHER_TYPE_SNOW;
+ else if(rnd <= chance3)
+ m_type = WEATHER_TYPE_STORM;
+ else
+ m_type = WEATHER_TYPE_FINE;
+
+ /// New weather statistics (if not fine):
+ ///- 85% light
+ ///- 7% medium
+ ///- 7% heavy
+ /// If fine 100% sun (no fog)
+
+ if (m_type == WEATHER_TYPE_FINE)
+ {
+ m_grade = 0.0f;
+ }
+ else if (u < 90)
+ {
+ m_grade = rand_norm() * 0.3333f;
+ }
+ else
+ {
+ // Severe change, but how severe?
+ rnd = urand(0, 99);
+ if (rnd < 50)
+ m_grade = rand_norm() * 0.3333f + 0.3334f;
+ else
+ m_grade = rand_norm() * 0.3333f + 0.6667f;
+ }
+
+ // return true only in case weather changes
+ return m_type != old_type || m_grade != old_grade;
+}
+
+void Weather::SendWeatherUpdateToPlayer(Player *player)
+{
+ WorldPacket data( SMSG_WEATHER, (4+4+4) );
+
+ data << uint32(GetWeatherState()) << (float)m_grade << uint8(0);
+ player->GetSession()->SendPacket( &data );
+}
+
+void Weather::SendFineWeatherUpdateToPlayer(Player *player)
+{
+ WorldPacket data( SMSG_WEATHER, (4+4+4) );
+
+ data << (uint32)WEATHER_STATE_FINE << (float)0.0f << uint8(0);
+ player->GetSession()->SendPacket( &data );
+}
+
+/// Send the new weather to all players in the zone
+bool Weather::UpdateWeather()
+{
+ Player* player = sWorld.FindPlayerInZone(m_zone);
+ if(!player)
+ return false;
+
+ ///- Send the weather packet to all players in this zone
+ if (m_grade >= 1)
+ m_grade = 0.9999f;
+ else if (m_grade < 0)
+ m_grade = 0.0001f;
+
+ WeatherState state = GetWeatherState();
+
+ WorldPacket data( SMSG_WEATHER, (4+4+4) );
+ data << uint32(state) << (float)m_grade << uint8(0);
+ player->SendMessageToSet( &data, true );
+
+ ///- Log the event
+ char const* wthstr;
+ switch(state)
+ {
+ case WEATHER_STATE_LIGHT_RAIN:
+ wthstr = "light rain";
+ break;
+ case WEATHER_STATE_MEDIUM_RAIN:
+ wthstr = "medium rain";
+ break;
+ case WEATHER_STATE_HEAVY_RAIN:
+ wthstr = "heavy rain";
+ break;
+ case WEATHER_STATE_LIGHT_SNOW:
+ wthstr = "light snow";
+ break;
+ case WEATHER_STATE_MEDIUM_SNOW:
+ wthstr = "medium snow";
+ break;
+ case WEATHER_STATE_HEAVY_SNOW:
+ wthstr = "heavy snow";
+ break;
+ case WEATHER_STATE_LIGHT_SANDSTORM:
+ wthstr = "light sandstorm";
+ break;
+ case WEATHER_STATE_MEDIUM_SANDSTORM:
+ wthstr = "medium sandstorm";
+ break;
+ case WEATHER_STATE_HEAVY_SANDSTORM:
+ wthstr = "heavy sandstorm";
+ break;
+ case WEATHER_STATE_THUNDERS:
+ wthstr = "thunders";
+ break;
+ case WEATHER_STATE_BLACKRAIN:
+ wthstr = "blackrain";
+ break;
+ case WEATHER_STATE_FINE:
+ default:
+ wthstr = "fine";
+ break;
+ }
+ sLog.outDetail("Change the weather of zone %u to %s.", m_zone, wthstr);
+
+ return true;
+}
+
+/// Set the weather
+void Weather::SetWeather(WeatherType type, float grade)
+{
+ if(m_type == type && m_grade == grade)
+ return;
+
+ m_type = type;
+ m_grade = grade;
+ UpdateWeather();
+}
+
+/// Get the sound number associated with the current weather
+WeatherState Weather::GetWeatherState() const
+{
+ if (m_grade<0.27f)
+ return WEATHER_STATE_FINE;
+
+ switch(m_type)
+ {
+ case WEATHER_TYPE_RAIN:
+ if(m_grade<0.40f)
+ return WEATHER_STATE_LIGHT_RAIN;
+ else if(m_grade<0.70f)
+ return WEATHER_STATE_MEDIUM_RAIN;
+ else
+ return WEATHER_STATE_HEAVY_RAIN;
+ case WEATHER_TYPE_SNOW:
+ if(m_grade<0.40f)
+ return WEATHER_STATE_LIGHT_SNOW;
+ else if(m_grade<0.70f)
+ return WEATHER_STATE_MEDIUM_SNOW;
+ else
+ return WEATHER_STATE_HEAVY_SNOW;
+ case WEATHER_TYPE_STORM:
+ if(m_grade<0.40f)
+ return WEATHER_STATE_LIGHT_SANDSTORM;
+ else if(m_grade<0.70f)
+ return WEATHER_STATE_MEDIUM_SANDSTORM;
+ else
+ return WEATHER_STATE_HEAVY_SANDSTORM;
+ case WEATHER_TYPE_BLACKRAIN:
+ return WEATHER_STATE_BLACKRAIN;
+ case WEATHER_TYPE_THUNDERS:
+ return WEATHER_STATE_THUNDERS;
+ case WEATHER_TYPE_FINE:
+ default:
+ return WEATHER_STATE_FINE;
+ }
+}
diff --git a/src/game/Weather.h b/src/game/Weather.h
new file mode 100644
index 00000000000..e6eaa49a3e7
--- /dev/null
+++ b/src/game/Weather.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup world
+/// @{
+/// \file
+
+#ifndef __WEATHER_H
+#define __WEATHER_H
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Timer.h"
+
+class Player;
+
+enum WeatherState
+{
+ WEATHER_STATE_FINE = 0,
+ WEATHER_STATE_LIGHT_RAIN = 3,
+ WEATHER_STATE_MEDIUM_RAIN = 4,
+ WEATHER_STATE_HEAVY_RAIN = 5,
+ WEATHER_STATE_LIGHT_SNOW = 6,
+ WEATHER_STATE_MEDIUM_SNOW = 7,
+ WEATHER_STATE_HEAVY_SNOW = 8,
+ WEATHER_STATE_LIGHT_SANDSTORM = 22,
+ WEATHER_STATE_MEDIUM_SANDSTORM = 41,
+ WEATHER_STATE_HEAVY_SANDSTORM = 42,
+ WEATHER_STATE_THUNDERS = 86,
+ WEATHER_STATE_BLACKRAIN = 90
+};
+
+struct WeatherZoneChances;
+
+/// Weather for one zone
+class Weather
+{
+ public:
+ Weather(uint32 zone, WeatherZoneChances const* weatherChances);
+ ~Weather() { };
+ bool ReGenerate();
+ bool UpdateWeather();
+ void SendWeatherUpdateToPlayer(Player *player);
+ static void SendFineWeatherUpdateToPlayer(Player *player);
+ void SetWeather(WeatherType type, float grade);
+ /// For which zone is this weather?
+ uint32 GetZone() { return m_zone; };
+ bool Update(time_t diff);
+ private:
+ WeatherState GetWeatherState() const;
+ uint32 m_zone;
+ WeatherType m_type;
+ float m_grade;
+ IntervalTimer m_timer;
+ WeatherZoneChances const* m_weatherChances;
+};
+#endif
diff --git a/src/game/World.cpp b/src/game/World.cpp
new file mode 100644
index 00000000000..8546778ae21
--- /dev/null
+++ b/src/game/World.cpp
@@ -0,0 +1,2460 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup world
+*/
+
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Config/ConfigEnv.h"
+#include "SystemConfig.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "Weather.h"
+#include "Player.h"
+#include "SkillExtraItems.h"
+#include "SkillDiscovery.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Chat.h"
+#include "Database/DBCStores.h"
+#include "LootMgr.h"
+#include "ItemEnchantmentMgr.h"
+#include "MapManager.h"
+#include "ScriptCalls.h"
+#include "CreatureAIRegistry.h"
+#include "Policies/SingletonImp.h"
+#include "BattleGroundMgr.h"
+#include "TemporarySummon.h"
+#include "WaypointMovementGenerator.h"
+#include "VMapFactory.h"
+#include "GlobalEvents.h"
+#include "GameEvent.h"
+#include "Database/DatabaseImpl.h"
+#include "WorldSocket.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "InstanceSaveMgr.h"
+#include "WaypointManager.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1( World );
+
+volatile bool World::m_stopEvent = false;
+volatile uint32 World::m_worldLoopCounter = 0;
+
+float World::m_MaxVisibleDistanceForCreature = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceForPlayer = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceForObject = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_VisibleUnitGreyDistance = 0;
+float World::m_VisibleObjectGreyDistance = 0;
+
+// ServerMessages.dbc
+enum ServerMessageType
+{
+ SERVER_MSG_SHUTDOWN_TIME = 1,
+ SERVER_MSG_RESTART_TIME = 2,
+ SERVER_MSG_STRING = 3,
+ SERVER_MSG_SHUTDOWN_CANCELLED = 4,
+ SERVER_MSG_RESTART_CANCELLED = 5
+};
+
+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()
+{
+ m_playerLimit = 0;
+ m_allowMovement = true;
+ m_ShutdownMask = 0;
+ m_ShutdownTimer = 0;
+ m_gameTime=time(NULL);
+ m_startTime=m_gameTime;
+ m_maxActiveSessionCount = 0;
+ m_maxQueuedSessionCount = 0;
+ m_resultQueue = NULL;
+ m_NextDailyQuestReset = 0;
+
+ m_defaultDbcLocale = LOCALE_enUS;
+ m_availableDbcLocaleMask = 0;
+}
+
+/// World destructor
+World::~World()
+{
+ ///- Empty the kicked session set
+ for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
+ delete *itr;
+
+ m_kicked_sessions.clear();
+
+ ///- Empty the WeatherMap
+ for (WeatherMap::iterator itr = m_weathers.begin(); itr != m_weathers.end(); ++itr)
+ delete itr->second;
+
+ m_weathers.clear();
+
+ VMAP::VMapFactory::clear();
+
+ if(m_resultQueue) delete m_resultQueue;
+}
+
+/// Find a player in a specified zone
+Player* World::FindPlayerInZone(uint32 zone)
+{
+ ///- circle through active sessions and return the first player found in the zone
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second)
+ continue;
+ Player *player = itr->second->GetPlayer();
+ if(!player)
+ continue;
+ if( player->IsInWorld() && player->GetZoneId() == zone )
+ {
+ // Used by the weather system. We return the player to broadcast the change weather message to him and all players in the zone.
+ return player;
+ }
+ }
+ return NULL;
+}
+
+/// Find a session by its id
+WorldSession* World::FindSession(uint32 id) const
+{
+ SessionMap::const_iterator itr = m_sessions.find(id);
+
+ if(itr != m_sessions.end())
+ return itr->second; // also can return NULL for kicked session
+ else
+ return NULL;
+}
+
+/// Remove a given session
+bool World::RemoveSession(uint32 id)
+{
+ ///- Find the session, kick the user, but we can't delete session at this moment to prevent iterator invalidation
+ SessionMap::iterator itr = m_sessions.find(id);
+
+ if(itr != m_sessions.end() && itr->second)
+ {
+ if (itr->second->PlayerLoading())
+ return false;
+ itr->second->KickPlayer();
+ }
+
+ return true;
+}
+
+/// Add a session to the session list
+void World::AddSession(WorldSession* s)
+{
+ ASSERT(s);
+
+ WorldSession* old = m_sessions[s->GetAccountId()];
+ m_sessions[s->GetAccountId()] = s;
+
+ // if session already exist, prepare to it deleting at next world update
+ if(old)
+ m_kicked_sessions.insert(old);
+}
+
+int32 World::GetQueuePos(WorldSocket* socket)
+{
+ uint32 position = 1;
+
+ for(Queue::iterator iter = m_QueuedPlayer.begin(); iter != m_QueuedPlayer.end(); ++iter, ++position)
+ if((*iter) == socket)
+ return position;
+
+ return 0;
+}
+
+void World::AddQueuedPlayer(WorldSocket* socket)
+{
+ m_QueuedPlayer.push_back(socket);
+}
+
+void World::RemoveQueuedPlayer(WorldSocket* socket)
+{
+ // sessions count including queued to remove (if removed_session set)
+ uint32 sessions = GetActiveSessionCount();
+
+ uint32 position = 1;
+ Queue::iterator iter = m_QueuedPlayer.begin();
+
+ // if session not queued then we need decrease sessions count (Remove socked callet before session removing from session list)
+ bool decrease_session = true;
+
+ // search socket to remove and count skipped positions
+ for(;iter != m_QueuedPlayer.end(); ++iter, ++position)
+ {
+ if(*iter==socket)
+ {
+ Queue::iterator iter2 = iter;
+ ++iter;
+ m_QueuedPlayer.erase(iter2);
+ decrease_session = false; // removing queued session
+ break;
+ }
+ }
+
+ // iter point to next socked after removed or end()
+ // position store position of removed socket and then new position next socket after removed
+
+ // decrease for case session queued for removing
+ if(decrease_session && sessions)
+ --sessions;
+
+ // accept first in queue
+ if( (!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty() )
+ {
+ WorldSocket * socket = m_QueuedPlayer.front();
+ socket->SendAuthWaitQue(0);
+ m_QueuedPlayer.pop_front();
+
+ // update iter to point first queued socket or end() if queue is empty now
+ iter = m_QueuedPlayer.begin();
+ position = 1;
+ }
+
+ // update position from iter to end()
+ // iter point to first not updated socket, position store new position
+ for(; iter != m_QueuedPlayer.end(); ++iter, ++position)
+ (*iter)->SendAuthWaitQue(position);
+}
+
+/// Find a Weather object by the given zoneid
+Weather* World::FindWeather(uint32 id) const
+{
+ WeatherMap::const_iterator itr = m_weathers.find(id);
+
+ if(itr != m_weathers.end())
+ return itr->second;
+ else
+ return 0;
+}
+
+/// Remove a Weather object for the given zoneid
+void World::RemoveWeather(uint32 id)
+{
+ // not called at the moment. Kept for completeness
+ WeatherMap::iterator itr = m_weathers.find(id);
+
+ if(itr != m_weathers.end())
+ {
+ delete itr->second;
+ m_weathers.erase(itr);
+ }
+}
+
+/// Add a Weather object to the list
+Weather* World::AddWeather(uint32 zone_id)
+{
+ WeatherZoneChances const* weatherChances = objmgr.GetWeatherChances(zone_id);
+
+ // zone not have weather, ignore
+ if(!weatherChances)
+ return NULL;
+
+ Weather* w = new Weather(zone_id,weatherChances);
+ m_weathers[w->GetZone()] = w;
+ w->ReGenerate();
+ w->UpdateWeather();
+ return w;
+}
+
+/// Initialize config values
+void World::LoadConfigSettings(bool reload)
+{
+ if(reload)
+ {
+ if(!sConfig.Reload())
+ {
+ sLog.outError("World settings reload fail: can't read settings from %s.",sConfig.GetFilename().c_str());
+ return;
+ }
+ //TODO Check if config is outdated
+ }
+
+ ///- Read the player limit and the Message of the day from the config file
+ SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT), true );
+ SetMotd( sConfig.GetStringDefault("Motd", "Welcome to the Massive Network Game Object Server." ) );
+
+ ///- Read all rates from the config file
+ rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1);
+ if(rate_values[RATE_HEALTH] < 0)
+ {
+ sLog.outError("Rate.Health (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_HEALTH]);
+ rate_values[RATE_HEALTH] = 1;
+ }
+ rate_values[RATE_POWER_MANA] = sConfig.GetFloatDefault("Rate.Mana", 1);
+ if(rate_values[RATE_POWER_MANA] < 0)
+ {
+ sLog.outError("Rate.Mana (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]);
+ rate_values[RATE_POWER_MANA] = 1;
+ }
+ rate_values[RATE_POWER_RAGE_INCOME] = sConfig.GetFloatDefault("Rate.Rage.Income", 1);
+ rate_values[RATE_POWER_RAGE_LOSS] = sConfig.GetFloatDefault("Rate.Rage.Loss", 1);
+ if(rate_values[RATE_POWER_RAGE_LOSS] < 0)
+ {
+ sLog.outError("Rate.Rage.Loss (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]);
+ rate_values[RATE_POWER_RAGE_LOSS] = 1;
+ }
+ rate_values[RATE_POWER_FOCUS] = sConfig.GetFloatDefault("Rate.Focus", 1.0f);
+ rate_values[RATE_LOYALTY] = sConfig.GetFloatDefault("Rate.Loyalty", 1.0f);
+ rate_values[RATE_SKILL_DISCOVERY] = sConfig.GetFloatDefault("Rate.Skill.Discovery", 1.0f);
+ rate_values[RATE_DROP_ITEM_POOR] = sConfig.GetFloatDefault("Rate.Drop.Item.Poor", 1.0f);
+ rate_values[RATE_DROP_ITEM_NORMAL] = sConfig.GetFloatDefault("Rate.Drop.Item.Normal", 1.0f);
+ rate_values[RATE_DROP_ITEM_UNCOMMON] = sConfig.GetFloatDefault("Rate.Drop.Item.Uncommon", 1.0f);
+ rate_values[RATE_DROP_ITEM_RARE] = sConfig.GetFloatDefault("Rate.Drop.Item.Rare", 1.0f);
+ rate_values[RATE_DROP_ITEM_EPIC] = sConfig.GetFloatDefault("Rate.Drop.Item.Epic", 1.0f);
+ rate_values[RATE_DROP_ITEM_LEGENDARY] = sConfig.GetFloatDefault("Rate.Drop.Item.Legendary", 1.0f);
+ rate_values[RATE_DROP_ITEM_ARTIFACT] = sConfig.GetFloatDefault("Rate.Drop.Item.Artifact", 1.0f);
+ rate_values[RATE_DROP_ITEM_REFERENCED] = sConfig.GetFloatDefault("Rate.Drop.Item.Referenced", 1.0f);
+ rate_values[RATE_DROP_MONEY] = sConfig.GetFloatDefault("Rate.Drop.Money", 1.0f);
+ 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_XP_PAST_70] = sConfig.GetFloatDefault("Rate.XP.PastLevel70", 1.0f);
+ rate_values[RATE_REPUTATION_GAIN] = sConfig.GetFloatDefault("Rate.Reputation.Gain", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.Damage", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_HP] = sConfig.GetFloatDefault("Rate.Creature.Normal.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.HP", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_AGGRO] = sConfig.GetFloatDefault("Rate.Creature.Aggro", 1.0f);
+ rate_values[RATE_REST_INGAME] = sConfig.GetFloatDefault("Rate.Rest.InGame", 1.0f);
+ rate_values[RATE_REST_OFFLINE_IN_TAVERN_OR_CITY] = sConfig.GetFloatDefault("Rate.Rest.Offline.InTavernOrCity", 1.0f);
+ rate_values[RATE_REST_OFFLINE_IN_WILDERNESS] = sConfig.GetFloatDefault("Rate.Rest.Offline.InWilderness", 1.0f);
+ rate_values[RATE_DAMAGE_FALL] = sConfig.GetFloatDefault("Rate.Damage.Fall", 1.0f);
+ rate_values[RATE_AUCTION_TIME] = sConfig.GetFloatDefault("Rate.Auction.Time", 1.0f);
+ rate_values[RATE_AUCTION_DEPOSIT] = sConfig.GetFloatDefault("Rate.Auction.Deposit", 1.0f);
+ rate_values[RATE_AUCTION_CUT] = sConfig.GetFloatDefault("Rate.Auction.Cut", 1.0f);
+ rate_values[RATE_HONOR] = sConfig.GetFloatDefault("Rate.Honor",1.0f);
+ rate_values[RATE_MINING_AMOUNT] = sConfig.GetFloatDefault("Rate.Mining.Amount",1.0f);
+ rate_values[RATE_MINING_NEXT] = sConfig.GetFloatDefault("Rate.Mining.Next",1.0f);
+ rate_values[RATE_INSTANCE_RESET_TIME] = sConfig.GetFloatDefault("Rate.InstanceResetTime",1.0f);
+ rate_values[RATE_TALENT] = sConfig.GetFloatDefault("Rate.Talent",1.0f);
+ if(rate_values[RATE_TALENT] < 0.0f)
+ {
+ sLog.outError("Rate.Talent (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_TALENT]);
+ rate_values[RATE_TALENT] = 1.0f;
+ }
+ rate_values[RATE_CORPSE_DECAY_LOOTED] = sConfig.GetFloatDefault("Rate.Corpse.Decay.Looted",0.1f);
+
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = sConfig.GetFloatDefault("TargetPosRecalculateRange",1.5f);
+ if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] < CONTACT_DISTANCE)
+ {
+ sLog.outError("TargetPosRecalculateRange (%f) must be >= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],CONTACT_DISTANCE,CONTACT_DISTANCE);
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = CONTACT_DISTANCE;
+ }
+ else if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] > ATTACK_DISTANCE)
+ {
+ sLog.outError("TargetPosRecalculateRange (%f) must be <= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],ATTACK_DISTANCE,ATTACK_DISTANCE);
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = ATTACK_DISTANCE;
+ }
+
+ rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfig.GetFloatDefault("DurabilityLossChance.Damage",0.5f);
+ if(rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Damage (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_DAMAGE]);
+ rate_values[RATE_DURABILITY_LOSS_DAMAGE] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_ABSORB] = sConfig.GetFloatDefault("DurabilityLossChance.Absorb",0.5f);
+ if(rate_values[RATE_DURABILITY_LOSS_ABSORB] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Absorb (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_ABSORB]);
+ rate_values[RATE_DURABILITY_LOSS_ABSORB] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_PARRY] = sConfig.GetFloatDefault("DurabilityLossChance.Parry",0.05f);
+ if(rate_values[RATE_DURABILITY_LOSS_PARRY] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Parry (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_PARRY]);
+ rate_values[RATE_DURABILITY_LOSS_PARRY] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_BLOCK] = sConfig.GetFloatDefault("DurabilityLossChance.Block",0.05f);
+ if(rate_values[RATE_DURABILITY_LOSS_BLOCK] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Block (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_BLOCK]);
+ rate_values[RATE_DURABILITY_LOSS_BLOCK] = 0.0f;
+ }
+
+ ///- Read other configuration items from the config file
+
+ m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1);
+ if(m_configs[CONFIG_COMPRESSION] < 1 || m_configs[CONFIG_COMPRESSION] > 9)
+ {
+ sLog.outError("Compression level (%i) must be in range 1..9. Using default compression level (1).",m_configs[CONFIG_COMPRESSION]);
+ m_configs[CONFIG_COMPRESSION] = 1;
+ }
+ m_configs[CONFIG_ADDON_CHANNEL] = sConfig.GetBoolDefault("AddonChannel", true);
+ m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true);
+ m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 900000);
+
+ m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 300000);
+ if(m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY)
+ {
+ sLog.outError("GridCleanUpDelay (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_GRIDCLEAN],MIN_GRID_DELAY);
+ m_configs[CONFIG_INTERVAL_GRIDCLEAN] = MIN_GRID_DELAY;
+ }
+ if(reload)
+ MapManager::Instance().SetGridCleanUpDelay(m_configs[CONFIG_INTERVAL_GRIDCLEAN]);
+
+ m_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfig.GetIntDefault("MapUpdateInterval", 100);
+ if(m_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY)
+ {
+ sLog.outError("MapUpdateInterval (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_MAPUPDATE],MIN_MAP_UPDATE_DELAY);
+ m_configs[CONFIG_INTERVAL_MAPUPDATE] = MIN_MAP_UPDATE_DELAY;
+ }
+ if(reload)
+ MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]);
+
+ m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 600000);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
+ if(val!=m_configs[CONFIG_PORT_WORLD])
+ sLog.outError("WorldServerPort option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_PORT_WORLD]);
+ }
+ else
+ m_configs[CONFIG_PORT_WORLD] = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
+ if(val!=m_configs[CONFIG_SOCKET_SELECTTIME])
+ sLog.outError("SocketSelectTime option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[DEFAULT_SOCKET_SELECT_TIME]);
+ }
+ else
+ m_configs[CONFIG_SOCKET_SELECTTIME] = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
+
+
+ m_configs[CONFIG_TCP_NO_DELAY] = sConfig.GetBoolDefault("TcpNoDelay", false);
+ m_configs[CONFIG_GROUP_XP_DISTANCE] = sConfig.GetIntDefault("MaxGroupXPDistance", 74);
+ /// \todo Add MonsterSight and GuarderSight (with meaning) in mangosd.conf or put them as define
+ m_configs[CONFIG_SIGHT_MONSTER] = sConfig.GetIntDefault("MonsterSight", 50);
+ m_configs[CONFIG_SIGHT_GUARDER] = sConfig.GetIntDefault("GuarderSight", 50);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("GameType", 0);
+ if(val!=m_configs[CONFIG_GAME_TYPE])
+ sLog.outError("GameType option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_GAME_TYPE]);
+ }
+ else
+ m_configs[CONFIG_GAME_TYPE] = sConfig.GetIntDefault("GameType", 0);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
+ if(val!=m_configs[CONFIG_REALM_ZONE])
+ sLog.outError("RealmZone option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_REALM_ZONE]);
+ }
+ else
+ m_configs[CONFIG_REALM_ZONE] = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
+
+ m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetBoolDefault("AllowTwoSide.Accounts", false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Chat",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Channel",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Group",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Guild",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Auction",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Mail",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfig.GetBoolDefault("AllowTwoSide.WhoList", false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfig.GetBoolDefault("AllowTwoSide.AddFriend", false);
+ m_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfig.GetIntDefault("StrictPlayerNames", 0);
+ m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault("StrictCharterNames", 0);
+ m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault("StrictPetNames", 0);
+
+ m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault("CharactersCreatingDisabled", 0);
+
+ m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10);
+ if(m_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_configs[CONFIG_CHARACTERS_PER_REALM] > 10)
+ {
+ sLog.outError("CharactersPerRealm (%i) must be in range 1..10. Set to 10.",m_configs[CONFIG_CHARACTERS_PER_REALM]);
+ m_configs[CONFIG_CHARACTERS_PER_REALM] = 10;
+ }
+
+ // must be after CONFIG_CHARACTERS_PER_REALM
+ m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = sConfig.GetIntDefault("CharactersPerAccount", 50);
+ if(m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] < m_configs[CONFIG_CHARACTERS_PER_REALM])
+ {
+ sLog.outError("CharactersPerAccount (%i) can't be less than CharactersPerRealm (%i).",m_configs[CONFIG_CHARACTERS_PER_ACCOUNT],m_configs[CONFIG_CHARACTERS_PER_REALM]);
+ m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = m_configs[CONFIG_CHARACTERS_PER_REALM];
+ }
+
+ m_configs[CONFIG_SKIP_CINEMATICS] = sConfig.GetIntDefault("SkipCinematics", 0);
+ if(m_configs[CONFIG_SKIP_CINEMATICS] < 0 || m_configs[CONFIG_SKIP_CINEMATICS] > 2)
+ {
+ sLog.outError("SkipCinematics (%i) must be in range 0..2. Set to 0.",m_configs[CONFIG_SKIP_CINEMATICS]);
+ m_configs[CONFIG_SKIP_CINEMATICS] = 0;
+ }
+
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 60);
+ if(val!=m_configs[CONFIG_MAX_PLAYER_LEVEL])
+ sLog.outError("MaxPlayerLevel option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ }
+ else
+ m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 60);
+ if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > 255)
+ {
+ sLog.outError("MaxPlayerLevel (%i) must be in range 1..255. Set to 255.",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_MAX_PLAYER_LEVEL] = 255;
+ }
+
+ m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
+ if(m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
+ {
+ sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to 1.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_START_PLAYER_LEVEL] = 1;
+ }
+ else if(m_configs[CONFIG_START_PLAYER_LEVEL] > m_configs[CONFIG_MAX_PLAYER_LEVEL])
+ {
+ sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to %u.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_START_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL];
+ }
+ m_configs[CONFIG_MAX_HONOR_POINTS] = sConfig.GetIntDefault("MaxHonorPoints", 75000);
+ m_configs[CONFIG_MAX_ARENA_POINTS] = sConfig.GetIntDefault("MaxArenaPoints", 5000);
+
+ m_configs[CONFIG_INSTANCE_IGNORE_LEVEL] = sConfig.GetBoolDefault("Instance.IgnoreLevel", false);
+ m_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfig.GetBoolDefault("Instance.IgnoreRaid", false);
+
+ m_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true);
+ m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.Enable", true);
+ m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false);
+
+ m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true);
+ m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4);
+ m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 1800000);
+
+ m_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfig.GetIntDefault("MaxPrimaryTradeSkill", 2);
+ m_configs[CONFIG_MIN_PETITION_SIGNS] = sConfig.GetIntDefault("MinPetitionSigns", 9);
+ if(m_configs[CONFIG_MIN_PETITION_SIGNS] > 9)
+ {
+ sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.",m_configs[CONFIG_MIN_PETITION_SIGNS]);
+ m_configs[CONFIG_MIN_PETITION_SIGNS] = 9;
+ }
+
+ m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetBoolDefault("GM.WhisperingTo",false);
+ 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_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState",2);
+ m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false);
+
+ m_configs[CONFIG_GROUP_VISIBILITY] = sConfig.GetIntDefault("Visibility.GroupMode",0);
+
+ m_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfig.GetIntDefault("MailDeliveryDelay",HOUR);
+
+ m_configs[CONFIG_UPTIME_UPDATE] = sConfig.GetIntDefault("UpdateUptimeInterval", 10);
+ if(m_configs[CONFIG_UPTIME_UPDATE]<=0)
+ {
+ sLog.outError("UpdateUptimeInterval (%i) must be > 0, set to default 10.",m_configs[CONFIG_UPTIME_UPDATE]);
+ m_configs[CONFIG_UPTIME_UPDATE] = 10;
+ }
+ if(reload)
+ {
+ m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
+ m_timers[WUPDATE_UPTIME].Reset();
+ }
+
+ m_configs[CONFIG_SKILL_CHANCE_ORANGE] = sConfig.GetIntDefault("SkillChance.Orange",100);
+ m_configs[CONFIG_SKILL_CHANCE_YELLOW] = sConfig.GetIntDefault("SkillChance.Yellow",75);
+ m_configs[CONFIG_SKILL_CHANCE_GREEN] = sConfig.GetIntDefault("SkillChance.Green",25);
+ m_configs[CONFIG_SKILL_CHANCE_GREY] = sConfig.GetIntDefault("SkillChance.Grey",0);
+
+ m_configs[CONFIG_SKILL_CHANCE_MINING_STEPS] = sConfig.GetIntDefault("SkillChance.MiningSteps",75);
+ m_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfig.GetIntDefault("SkillChance.SkinningSteps",75);
+
+ m_configs[CONFIG_SKILL_PROSPECTING] = sConfig.GetBoolDefault("SkillChance.Prospecting",false);
+
+ m_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfig.GetIntDefault("SkillGain.Crafting", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_CRAFTING] < 0)
+ {
+ sLog.outError("SkillGain.Crafting (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_CRAFTING]);
+ m_configs[CONFIG_SKILL_GAIN_CRAFTING] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_DEFENSE] = sConfig.GetIntDefault("SkillGain.Defense", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_DEFENSE] < 0)
+ {
+ sLog.outError("SkillGain.Defense (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_DEFENSE]);
+ m_configs[CONFIG_SKILL_GAIN_DEFENSE] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_GATHERING] = sConfig.GetIntDefault("SkillGain.Gathering", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_GATHERING] < 0)
+ {
+ sLog.outError("SkillGain.Gathering (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_GATHERING]);
+ m_configs[CONFIG_SKILL_GAIN_GATHERING] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_WEAPON] = sConfig.GetIntDefault("SkillGain.Weapon", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_WEAPON] < 0)
+ {
+ sLog.outError("SkillGain.Weapon (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_WEAPON]);
+ m_configs[CONFIG_SKILL_GAIN_WEAPON] = 1;
+ }
+
+ m_configs[CONFIG_MAX_OVERSPEED_PINGS] = sConfig.GetIntDefault("MaxOverspeedPings",2);
+ if(m_configs[CONFIG_MAX_OVERSPEED_PINGS] != 0 && m_configs[CONFIG_MAX_OVERSPEED_PINGS] < 2)
+ {
+ sLog.outError("MaxOverspeedPings (%i) must be in range 2..infinity (or 0 to disable check. Set to 2.",m_configs[CONFIG_MAX_OVERSPEED_PINGS]);
+ m_configs[CONFIG_MAX_OVERSPEED_PINGS] = 2;
+ }
+
+ m_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY] = sConfig.GetBoolDefault("SaveRespawnTimeImmediately",true);
+ m_configs[CONFIG_WEATHER] = sConfig.GetBoolDefault("ActivateWeather",true);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("Expansion",1);
+ if(val!=m_configs[CONFIG_EXPANSION])
+ sLog.outError("Expansion option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_EXPANSION]);
+ }
+ else
+ m_configs[CONFIG_EXPANSION] = sConfig.GetIntDefault("Expansion",1);
+
+ m_configs[CONFIG_CHATFLOOD_MESSAGE_COUNT] = sConfig.GetIntDefault("ChatFlood.MessageCount",10);
+ m_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = sConfig.GetIntDefault("ChatFlood.MessageDelay",1);
+ m_configs[CONFIG_CHATFLOOD_MUTE_TIME] = sConfig.GetIntDefault("ChatFlood.MuteTime",10);
+
+ m_configs[CONFIG_EVENT_ANNOUNCE] = sConfig.GetIntDefault("Event.Announce",0);
+
+ m_configs[CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS] = sConfig.GetIntDefault("CreatureFamilyAssistenceRadius",10);
+
+ m_configs[CONFIG_WORLD_BOSS_LEVEL_DIFF] = sConfig.GetIntDefault("WorldBossLevelDiff",3);
+
+ // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level (255)
+ m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.LowLevelHideDiff",4);
+ if(m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > 255)
+ m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = 255;
+ m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.HighLevelHideDiff",7);
+ if(m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] > 255)
+ m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = 255;
+
+ m_configs[CONFIG_DETECT_POS_COLLISION] = sConfig.GetBoolDefault("DetectPosCollision", true);
+
+ m_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfig.GetBoolDefault("Channel.RestrictedLfg", true);
+ m_configs[CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL] = sConfig.GetBoolDefault("Channel.SilentlyGMJoin", false);
+
+ m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true);
+ m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false);
+
+ m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60);
+ m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfig.GetIntDefault("Corpse.Decay.ELITE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_RAREELITE] = sConfig.GetIntDefault("Corpse.Decay.RAREELITE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_WORLDBOSS] = sConfig.GetIntDefault("Corpse.Decay.WORLDBOSS", 3600);
+
+ m_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfig.GetIntDefault("Death.SicknessLevel", 11);
+ m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvP", true);
+ m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvE", true);
+
+ m_configs[CONFIG_THREAT_RADIUS] = sConfig.GetIntDefault("ThreatRadius", 100);
+
+ // always use declined names in the russian client
+ m_configs[CONFIG_DECLINED_NAMES_USED] =
+ (m_configs[CONFIG_REALM_ZONE] == REALM_ZONE_RUSSIAN) ? true : sConfig.GetBoolDefault("DeclinedNames", false);
+
+ m_configs[CONFIG_LISTEN_RANGE_SAY] = sConfig.GetIntDefault("ListenRange.Say", 25);
+ m_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfig.GetIntDefault("ListenRange.TextEmote", 25);
+ m_configs[CONFIG_LISTEN_RANGE_YELL] = sConfig.GetIntDefault("ListenRange.Yell", 300);
+
+
+ m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
+ if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Grey.Unit can't be greater %f",MAX_VISIBILITY_DISTANCE);
+ m_VisibleUnitGreyDistance = MAX_VISIBILITY_DISTANCE;
+ }
+ m_VisibleObjectGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Object", 10);
+ if(m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Grey.Object can't be greater %f",MAX_VISIBILITY_DISTANCE);
+ m_VisibleObjectGreyDistance = MAX_VISIBILITY_DISTANCE;
+ }
+
+ m_MaxVisibleDistanceForCreature = sConfig.GetFloatDefault("Visibility.Distance.Creature", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForCreature < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
+ {
+ sLog.outError("Visibility.Distance.Creature can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
+ m_MaxVisibleDistanceForCreature = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
+ }
+ else if(m_MaxVisibleDistanceForCreature + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility. Distance .Creature can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
+ m_MaxVisibleDistanceForCreature = MAX_VISIBILITY_DISTANCE-m_VisibleUnitGreyDistance;
+ }
+ m_MaxVisibleDistanceForPlayer = sConfig.GetFloatDefault("Visibility.Distance.Player", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForPlayer < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
+ {
+ sLog.outError("Visibility.Distance.Player can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
+ m_MaxVisibleDistanceForPlayer = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
+ }
+ else if(m_MaxVisibleDistanceForPlayer + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Player can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
+ m_MaxVisibleDistanceForPlayer = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
+ }
+ m_MaxVisibleDistanceForObject = sConfig.GetFloatDefault("Visibility.Distance.Gameobject", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForObject < INTERACTION_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Object can't be less max aggro radius %f",float(INTERACTION_DISTANCE));
+ m_MaxVisibleDistanceForObject = INTERACTION_DISTANCE;
+ }
+ else if(m_MaxVisibleDistanceForObject + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Object can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
+ m_MaxVisibleDistanceForObject = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
+ }
+ m_MaxVisibleDistanceInFlight = sConfig.GetFloatDefault("Visibility.Distance.InFlight", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceInFlight + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.InFlight can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
+ m_MaxVisibleDistanceInFlight = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
+ }
+
+ ///- Read the "Data" directory from the config file
+ std::string dataPath = sConfig.GetStringDefault("DataDir","./");
+ if( dataPath.at(dataPath.length()-1)!='/' && dataPath.at(dataPath.length()-1)!='\\' )
+ dataPath.append("/");
+
+ if(reload)
+ {
+ if(dataPath!=m_dataPath)
+ sLog.outError("DataDir option can't be changed at mangosd.conf reload, using current value (%s).",m_dataPath.c_str());
+ }
+ else
+ {
+ m_dataPath = dataPath;
+ sLog.outString("Using DataDir %s",m_dataPath.c_str());
+ }
+
+ bool enableLOS = sConfig.GetBoolDefault("vmap.enableLOS", false);
+ bool enableHeight = sConfig.GetBoolDefault("vmap.enableHeight", false);
+ std::string ignoreMapIds = sConfig.GetStringDefault("vmap.ignoreMapIds", "");
+ std::string ignoreSpellIds = sConfig.GetStringDefault("vmap.ignoreSpellIds", "");
+ VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
+ VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
+ VMAP::VMapFactory::createOrGetVMapManager()->preventMapsFromBeingUsed(ignoreMapIds.c_str());
+ VMAP::VMapFactory::preventSpellsFromBeingTestedForLoS(ignoreSpellIds.c_str());
+ sLog.outString( "WORLD: VMap support included. LineOfSight:%i, getHeight:%i",enableLOS, enableHeight);
+ sLog.outString( "WORLD: VMap data directory is: %svmaps",m_dataPath.c_str());
+ sLog.outString( "WORLD: VMap config keys are: vmap.enableLOS, vmap.enableHeight, vmap.ignoreMapIds, vmap.ignoreSpellIds");
+}
+
+/// Initialize the World
+void World::SetInitialWorldSettings()
+{
+ ///- Initialize the random number generator
+ srand((unsigned int)time(NULL));
+
+ ///- Initialize config settings
+ LoadConfigSettings();
+
+ ///- Init highest guids before any table loading to prevent using not initialized guids in some code.
+ objmgr.SetHighestGuids();
+
+ ///- Check the existence of the map files for all races' startup areas.
+ if( !MapManager::ExistMapAndVMap(0,-6240.32f, 331.033f)
+ ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
+ ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
+ ||!MapManager::ExistMapAndVMap(1,-618.518f,-4251.67f)
+ ||!MapManager::ExistMapAndVMap(0, 1676.35f, 1677.45f)
+ ||!MapManager::ExistMapAndVMap(1, 10311.3f, 832.463f)
+ ||!MapManager::ExistMapAndVMap(1,-2917.58f,-257.98f)
+ ||m_configs[CONFIG_EXPANSION] && (
+ !MapManager::ExistMapAndVMap(530,10349.6f,-6357.29f) || !MapManager::ExistMapAndVMap(530,-3961.64f,-13931.2f) ) )
+ {
+ sLog.outError("Correct *.map files not found in path '%smaps' or *.vmap/*vmdir files in '%svmaps'. Please place *.map/*.vmap/*.vmdir files in appropriate directories or correct the DataDir value in the mangosd.conf file.",m_dataPath.c_str(),m_dataPath.c_str());
+ exit(1);
+ }
+
+ ///- Loading strings. Getting no records means core load has to be canceled because no error message can be output.
+ sLog.outString( "" );
+ sLog.outString( "Loading MaNGOS strings..." );
+ if (!objmgr.LoadMangosStrings())
+ exit(1); // Error message displayed in function already
+
+ ///- Update the realm entry in the database with the realm type from the config file
+ //No SQL injection as values are treated as integers
+
+ // 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);
+
+ ///- Remove the bones after a restart
+ CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0'");
+
+ ///- Load the DBC files
+ sLog.outString("Initialize data stores...");
+ LoadDBCStores(m_dataPath);
+ DetectDBCLang();
+
+ sLog.outString( "Loading InstanceTemplate" );
+ objmgr.LoadInstanceTemplate();
+
+ sLog.outString( "Loading SkillLineAbilityMultiMap Data..." );
+ spellmgr.LoadSkillLineAbilityMap();
+
+ ///- Clean up and pack instances
+ sLog.outString( "Cleaning up instances..." );
+ sInstanceSaveManager.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables
+
+ sLog.outString( "Packing instances..." );
+ sInstanceSaveManager.PackInstances();
+
+ sLog.outString( "Loading Localization strings..." );
+ objmgr.LoadCreatureLocales();
+ objmgr.LoadGameObjectLocales();
+ objmgr.LoadItemLocales();
+ objmgr.LoadQuestLocales();
+ objmgr.LoadNpcTextLocales();
+ objmgr.LoadPageTextLocales();
+ objmgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
+
+ sLog.outString( "Loading Page Texts..." );
+ objmgr.LoadPageTexts();
+
+ sLog.outString( "Loading Game Object Templates..." ); // must be after LoadPageTexts
+ objmgr.LoadGameobjectInfo();
+
+ sLog.outString( "Loading Spell Chain Data..." );
+ spellmgr.LoadSpellChains();
+
+ sLog.outString( "Loading Spell Elixir types..." );
+ spellmgr.LoadSpellElixirs();
+
+ sLog.outString( "Loading Spell Learn Skills..." );
+ spellmgr.LoadSpellLearnSkills(); // must be after LoadSpellChains
+
+ sLog.outString( "Loading Spell Learn Spells..." );
+ spellmgr.LoadSpellLearnSpells();
+
+ sLog.outString( "Loading Spell Proc Event conditions..." );
+ spellmgr.LoadSpellProcEvents();
+
+ sLog.outString( "Loading Aggro Spells Definitions...");
+ spellmgr.LoadSpellThreats();
+
+ sLog.outString( "Loading NPC Texts..." );
+ objmgr.LoadGossipText();
+
+ sLog.outString( "Loading Item Random Enchantments Table..." );
+ LoadRandomEnchantmentsTable();
+
+ sLog.outString( "Loading Items..." ); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
+ objmgr.LoadItemPrototypes();
+
+ sLog.outString( "Loading Item Texts..." );
+ objmgr.LoadItemTexts();
+
+ sLog.outString( "Loading Creature Model Based Info Data..." );
+ objmgr.LoadCreatureModelInfo();
+
+ sLog.outString( "Loading Equipment templates...");
+ objmgr.LoadEquipmentTemplates();
+
+ sLog.outString( "Loading Creature templates..." );
+ objmgr.LoadCreatureTemplates();
+
+ sLog.outString( "Loading SpellsScriptTarget...");
+ spellmgr.LoadSpellScriptTarget(); // must be after LoadCreatureTemplates and LoadGameobjectInfo
+
+ sLog.outString( "Loading Creature Reputation OnKill Data..." );
+ objmgr.LoadReputationOnKill();
+
+ sLog.outString( "Loading Pet Create Spells..." );
+ objmgr.LoadPetCreateSpells();
+
+ sLog.outString( "Loading Creature Data..." );
+ objmgr.LoadCreatures();
+
+ sLog.outString( "Loading Creature Addon Data..." );
+ objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
+
+ sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances()
+ objmgr.LoadCreatureRespawnTimes();
+
+ sLog.outString( "Loading Gameobject Data..." );
+ objmgr.LoadGameobjects();
+
+ sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances()
+ objmgr.LoadGameobjectRespawnTimes();
+
+ sLog.outString( "Loading Game Event Data...");
+ gameeventmgr.LoadFromDB();
+
+ sLog.outString( "Loading Weather Data..." );
+ objmgr.LoadWeatherZoneChances();
+
+ sLog.outString( "Loading Quests..." );
+ objmgr.LoadQuests(); // must be loaded after DBCs, creature_template, item_template, gameobject tables
+
+ sLog.outString( "Loading Quests Relations..." );
+ objmgr.LoadQuestRelations(); // must be after quest load
+
+ sLog.outString( "Loading AreaTrigger definitions..." );
+ objmgr.LoadAreaTriggerTeleports(); // must be after item template load
+
+ sLog.outString( "Loading Quest Area Triggers..." );
+ objmgr.LoadQuestAreaTriggers(); // must be after LoadQuests
+
+ sLog.outString( "Loading Tavern Area Triggers..." );
+ objmgr.LoadTavernAreaTriggers();
+
+ sLog.outString( "Loading AreaTrigger script names..." );
+ objmgr.LoadAreaTriggerScripts();
+
+
+ sLog.outString( "Loading Graveyard-zone links...");
+ objmgr.LoadGraveyardZones();
+
+ sLog.outString( "Loading Spell target coordinates..." );
+ spellmgr.LoadSpellTargetPositions();
+
+ sLog.outString( "Loading SpellAffect definitions..." );
+ spellmgr.LoadSpellAffects();
+
+ sLog.outString( "Loading spell pet auras..." );
+ spellmgr.LoadSpellPetAuras();
+
+ sLog.outString( "Loading player Create Info & Level Stats..." );
+ objmgr.LoadPlayerInfo();
+
+ sLog.outString( "Loading Exploration BaseXP Data..." );
+ objmgr.LoadExplorationBaseXP();
+
+ sLog.outString( "Loading Pet Name Parts..." );
+ objmgr.LoadPetNames();
+
+ sLog.outString( "Loading the max pet number..." );
+ objmgr.LoadPetNumber();
+
+ sLog.outString( "Loading pet level stats..." );
+ objmgr.LoadPetLevelInfo();
+
+ sLog.outString( "Loading Player Corpses..." );
+ objmgr.LoadCorpses();
+
+ sLog.outString( "Loading Loot Tables..." );
+ LoadLootTables();
+
+ sLog.outString( "Loading Skill Discovery Table..." );
+ LoadSkillDiscoveryTable();
+
+ sLog.outString( "Loading Skill Extra Item Table..." );
+ LoadSkillExtraItemTable();
+
+ sLog.outString( "Loading Skill Fishing base level requirements..." );
+ objmgr.LoadFishingBaseSkillLevel();
+
+ ///- Load dynamic data tables from the database
+ sLog.outString( "Loading Auctions..." );
+ objmgr.LoadAuctionItems();
+ objmgr.LoadAuctions();
+
+ sLog.outString( "Loading Guilds..." );
+ objmgr.LoadGuilds();
+
+ sLog.outString( "Loading ArenaTeams..." );
+ objmgr.LoadArenaTeams();
+
+ sLog.outString( "Loading Groups..." );
+ objmgr.LoadGroups();
+
+ sLog.outString( "Loading ReservedNames..." );
+ objmgr.LoadReservedPlayersNames();
+
+ sLog.outString( "Loading GameObject for quests..." );
+ objmgr.LoadGameObjectForQuests();
+
+ sLog.outString( "Loading BattleMasters..." );
+ objmgr.LoadBattleMastersEntry();
+
+ sLog.outString( "Loading Waypoints..." );
+ WaypointMgr.Load();
+
+ ///- Handle outdated emails (delete/return)
+ sLog.outString( "Returning old mails..." );
+ objmgr.ReturnOrDeleteOldMails(false);
+
+ ///- Load and initialize scripts
+ sLog.outString( "Loading Scripts..." );
+ objmgr.LoadQuestStartScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
+ objmgr.LoadQuestEndScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
+ objmgr.LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data)
+ objmgr.LoadGameObjectScripts(); // must be after load Creature/Gameobject(Template/Data)
+ objmgr.LoadEventScripts(); // must be after load Creature/Gameobject(Template/Data)
+
+ sLog.outString( "Initializing Scripts..." );
+ if(!LoadScriptingModule())
+ exit(1);
+
+ ///- Initialize game time and timers
+ sLog.outString( "DEBUG:: Initialize game time and timers" );
+ m_gameTime = time(NULL);
+ m_startTime=m_gameTime;
+
+ tm local;
+ time_t curr;
+ time(&curr);
+ local=*(localtime(&curr)); // dereference and assign
+ char isoDate[128];
+ 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);
+
+ WorldDatabase.PExecute("INSERT INTO uptime (startstring, starttime, uptime) VALUES('%s', %ld, 0)", isoDate, m_startTime );
+
+ m_timers[WUPDATE_OBJECTS].SetInterval(0);
+ m_timers[WUPDATE_SESSIONS].SetInterval(0);
+ m_timers[WUPDATE_WEATHERS].SetInterval(1000);
+ m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*1000); //set auction update interval to 1 minute
+ m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
+ //Update "uptime" table based on configuration entry in minutes.
+ m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*1000); //erase corpses every 20 minutes
+
+ //to set mailtimer to return mails every day between 4 and 5 am
+ //mailtimer is increased when updating auctions
+ //one second is 1000 -(tested on win system)
+ mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * 1000) / m_timers[WUPDATE_AUCTIONS].GetInterval() );
+ //1440
+ mail_timer_expires = ( (DAY * 1000) / (m_timers[WUPDATE_AUCTIONS].GetInterval()));
+ sLog.outDebug("Mail timer set to: %u, mail return is called every %u minutes", mail_timer, mail_timer_expires);
+
+ ///- Initilize static helper structures
+ AIRegistry::Initialize();
+ WaypointMovementGenerator<Creature>::Initialize();
+ Player::InitVisibleBits();
+
+ ///- Initialize MapManager
+ sLog.outString( "Starting Map System" );
+ MapManager::Instance().Initialize();
+
+ ///- Initialize Battlegrounds
+ sLog.outString( "Starting BattleGround System" );
+ sBattleGroundMgr.CreateInitialBattleGrounds();
+
+ //Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager
+ sLog.outString( "Loading Transports..." );
+ MapManager::Instance().LoadTransports();
+
+ sLog.outString("Deleting expired bans..." );
+ loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
+ sLog.outString("Calculate next daily quest reset time..." );
+ InitDailyQuestResetTime();
+
+ sLog.outString("Starting Game Event system..." );
+ uint32 nextGameEvent = gameeventmgr.Initialize();
+ m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
+
+ sLog.outString( "WORLD: World initialized" );
+}
+void World::DetectDBCLang()
+{
+ uint32 m_lang_confid = sConfig.GetIntDefault("DBC.Locale", 255);
+
+ if(m_lang_confid != 255 && m_lang_confid >= MAX_LOCALE)
+ {
+ sLog.outError("Incorrect DBC.Locale! Must be >= 0 and < %d (set to 0)",MAX_LOCALE);
+ m_lang_confid = LOCALE_enUS;
+ }
+
+ ChrRacesEntry const* race = sChrRacesStore.LookupEntry(1);
+
+ std::string availableLocalsStr;
+
+ int default_locale = MAX_LOCALE;
+ for (int i = MAX_LOCALE-1; i >= 0; --i)
+ {
+ if ( strlen(race->name[i]) > 0) // check by race names
+ {
+ default_locale = i;
+ m_availableDbcLocaleMask |= (1 << i);
+ availableLocalsStr += localeNames[i];
+ availableLocalsStr += " ";
+ }
+ }
+
+ if( default_locale != m_lang_confid && m_lang_confid < MAX_LOCALE &&
+ (m_availableDbcLocaleMask & (1 << m_lang_confid)) )
+ {
+ default_locale = m_lang_confid;
+ }
+
+ if(default_locale >= MAX_LOCALE)
+ {
+ sLog.outError("Unable to determine your DBC Locale! (corrupt DBC?)");
+ exit(1);
+ }
+
+ m_defaultDbcLocale = LocaleConstant(default_locale);
+
+ sLog.outString("Using %s DBC Locale as default. All available DBC locales: %s",localeNames[m_defaultDbcLocale],availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str());
+}
+
+/// Update the World !
+void World::Update(time_t diff)
+{
+ ///- Update the different timers
+ for(int i = 0; i < WUPDATE_COUNT; i++)
+ if(m_timers[i].GetCurrent()>=0)
+ m_timers[i].Update(diff);
+ else m_timers[i].SetCurrent(0);
+
+ ///- Update the game time and check for shutdown time
+ _UpdateGameTime();
+
+ /// Handle daily quests reset time
+ if(m_gameTime > m_NextDailyQuestReset)
+ {
+ ResetDailyQuests();
+ m_NextDailyQuestReset += DAY;
+ }
+
+ /// <ul><li> Handle auctions when the timer has passed
+ if (m_timers[WUPDATE_AUCTIONS].Passed())
+ {
+ m_timers[WUPDATE_AUCTIONS].Reset();
+
+ ///- Update mails (return old mails with item, or delete them)
+ //(tested... works on win)
+ if (++mail_timer > mail_timer_expires)
+ {
+ mail_timer = 0;
+ objmgr.ReturnOrDeleteOldMails(true);
+ }
+
+ AuctionHouseObject* AuctionMap;
+ for (int i = 0; i < 3; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ AuctionMap = objmgr.GetAuctionsMap( 6 );//horde
+ break;
+ case 1:
+ AuctionMap = objmgr.GetAuctionsMap( 2 );//alliance
+ break;
+ case 2:
+ AuctionMap = objmgr.GetAuctionsMap( 7 );//neutral
+ break;
+ }
+
+ ///- Handle expired auctions
+ AuctionHouseObject::AuctionEntryMap::iterator itr,next;
+ for (itr = AuctionMap->GetAuctionsBegin(); itr != AuctionMap->GetAuctionsEnd();itr = next)
+ {
+ next = itr;
+ ++next;
+ if (m_gameTime > (itr->second->time))
+ {
+ ///- Either cancel the auction if there was no bidder
+ if (itr->second->bidder == 0)
+ {
+ objmgr.SendAuctionExpiredMail( itr->second );
+ }
+ ///- Or perform the transaction
+ else
+ {
+ //we should send an "item sold" message if the seller is online
+ //we send the item to the winner
+ //we send the money to the seller
+ objmgr.SendAuctionSuccessfulMail( itr->second );
+ objmgr.SendAuctionWonMail( itr->second );
+ }
+
+ ///- In any case clear the auction
+ //No SQL injection (Id is integer)
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",itr->second->Id);
+ objmgr.RemoveAItem(itr->second->item_guidlow);
+ delete itr->second;
+ AuctionMap->RemoveAuction(itr->first);
+ }
+ }
+ }
+ }
+
+ /// <li> Handle session updates when the timer has passed
+ if (m_timers[WUPDATE_SESSIONS].Passed())
+ {
+ m_timers[WUPDATE_SESSIONS].Reset();
+
+ UpdateSessions(diff);
+ }
+
+ /// <li> Handle weather updates when the timer has passed
+ if (m_timers[WUPDATE_WEATHERS].Passed())
+ {
+ m_timers[WUPDATE_WEATHERS].Reset();
+
+ ///- Send an update signal to Weather objects
+ WeatherMap::iterator itr, next;
+ for (itr = m_weathers.begin(); itr != m_weathers.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ ///- and remove Weather objects for zones with no player
+ //As interval > WorldTick
+ if(!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))
+ {
+ delete itr->second;
+ m_weathers.erase(itr);
+ }
+ }
+ }
+ /// <li> Update uptime table
+ if (m_timers[WUPDATE_UPTIME].Passed())
+ {
+ uint32 tmpDiff = (m_gameTime - m_startTime);
+ uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount();
+
+ m_timers[WUPDATE_UPTIME].Reset();
+ WorldDatabase.PExecute("UPDATE uptime SET uptime = %d, maxplayers = %d WHERE starttime = " I64FMTD, tmpDiff, maxClientsNum, uint64(m_startTime));
+ }
+
+ /// <li> Handle all other objects
+ if (m_timers[WUPDATE_OBJECTS].Passed())
+ {
+ m_timers[WUPDATE_OBJECTS].Reset();
+ ///- Update objects when the timer has passed (maps, transport, creatures,...)
+ MapManager::Instance().Update(diff); // As interval = 0
+
+ ///- Process necessary scripts
+ if (!m_scriptSchedule.empty())
+ ScriptsProcess();
+
+ sBattleGroundMgr.Update(diff);
+ }
+
+ // execute callbacks from sql queries that were queued recently
+ UpdateResultQueue();
+
+ ///- Erase corpses once every 20 minutes
+ if (m_timers[WUPDATE_CORPSES].Passed())
+ {
+ m_timers[WUPDATE_CORPSES].Reset();
+
+ CorpsesErase();
+ }
+
+ ///- Process Game events when necessary
+ if (m_timers[WUPDATE_EVENTS].Passed())
+ {
+ m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed
+ uint32 nextGameEvent = gameeventmgr.Update();
+ m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
+ m_timers[WUPDATE_EVENTS].Reset();
+ }
+
+ /// </ul>
+ ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
+ MapManager::Instance().DoDelayedMovesAndRemoves();
+
+ // update the instance reset times
+ sInstanceSaveManager.Update();
+
+ // And last, but not least handle the issued cli commands
+ ProcessCliCommands();
+}
+
+/// Put scripts in the execution queue
+void World::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->GetGUID();
+ 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 (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->GetGUID();
+ 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_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;
+ default:
+ sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID));
+ break;
+ }
+ }
+
+ 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_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;
+ }
+ }
+
+ 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->datatext.c_str(), 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->datatext.c_str(),unit_target);
+ break;
+ case 2: // Yell
+ ((Creature *)source)->Yell(step.script->datatext.c_str(), LANG_UNIVERSAL, unit_target);
+ break;
+ case 3: // Emote text
+ ((Creature *)source)->TextEmote(step.script->datatext.c_str(), 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, ((Unit *)source)->GetUnitMovementFlags(), step.script->datalong2 );
+ MapManager::Instance().GetMap(((Unit *)source)->GetMapId(), ((Unit *)source))->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(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, *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_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
+
+ MapManager::Instance().GetMap(go->GetMapId(), go)->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(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, *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() )
+ 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(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, *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() )
+ 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;
+ }
+
+ if(!source->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ Object* cmdTarget = step.script->datalong2 ? source : target;
+
+ if(!cmdTarget)
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 ? "source" : "target");
+ break;
+ }
+
+ if(!cmdTarget->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId());
+ break;
+ }
+
+ Unit* spellTarget = (Unit*)cmdTarget;
+
+ //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast
+ ((Unit*)source)->CastSpell(spellTarget,step.script->datalong,false);
+
+ 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)
+{
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
+ {
+ if (itr->second &&
+ itr->second->GetPlayer() &&
+ itr->second->GetPlayer()->IsInWorld() &&
+ itr->second != self &&
+ (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
+ {
+ itr->second->SendPacket(packet);
+ }
+ }
+}
+
+/// Send a System Message to all players (except self if mentioned)
+void World::SendWorldText(int32 string_id, ...)
+{
+ std::vector<std::vector<WorldPacket*> > data_cache; // 0 = default, i => i-1 locale index
+
+ for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() )
+ continue;
+
+ uint32 loc_idx = itr->second->GetSessionDbLocaleIndex();
+ uint32 cache_idx = loc_idx+1;
+
+ std::vector<WorldPacket*>* data_list;
+
+ // create if not cached yet
+ if(data_cache.size() < cache_idx+1 || data_cache[cache_idx].empty())
+ {
+ if(data_cache.size() < cache_idx+1)
+ data_cache.resize(cache_idx+1);
+
+ data_list = &data_cache[cache_idx];
+
+ char const* text = objmgr.GetMangosString(string_id,loc_idx);
+
+ char buf[1000];
+
+ va_list argptr;
+ va_start( argptr, string_id );
+ vsnprintf( buf,1000, text, argptr );
+ va_end( argptr );
+
+ char* pos = &buf[0];
+
+ while(char* line = ChatHandler::LineFromMessage(pos))
+ {
+ WorldPacket* data = new WorldPacket();
+ ChatHandler::FillMessageData(data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL);
+ data_list->push_back(data);
+ }
+ }
+ else
+ data_list = &data_cache[cache_idx];
+
+ for(int i = 0; i < data_list->size(); ++i)
+ itr->second->SendPacket((*data_list)[i]);
+ }
+
+ // free memory
+ for(int i = 0; i < data_cache.size(); ++i)
+ for(int j = 0; j < data_cache[i].size(); ++j)
+ delete data_cache[i][j];
+}
+
+/// Send a packet to all players (or players selected team) in the zone (except self if mentioned)
+void World::SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self, uint32 team)
+{
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
+ {
+ if (itr->second &&
+ itr->second->GetPlayer() &&
+ itr->second->GetPlayer()->IsInWorld() &&
+ itr->second->GetPlayer()->GetZoneId() == zone &&
+ itr->second != self &&
+ (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
+ {
+ itr->second->SendPacket(packet);
+ }
+ }
+}
+
+/// Send a System Message to all players in the zone (except self if mentioned)
+void World::SendZoneText(uint32 zone, const char* text, WorldSession *self, uint32 team)
+{
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, text, NULL);
+ SendZoneMessage(zone, &data, self,team);
+}
+
+/// Kick (and save) all players
+void World::KickAll()
+{
+ // session not removed at kick and will removed in next update tick
+ for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ itr->second->KickPlayer();
+}
+
+/// Kick (and save) all players with security level less `sec`
+void World::KickAllLess(AccountTypes sec)
+{
+ // session not removed at kick and will removed in next update tick
+ for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ if(itr->second->GetSecurity() < sec)
+ itr->second->KickPlayer();
+}
+
+/// Kick all queued players
+void World::KickAllQueued()
+{
+ // session not removed at kick and will removed in next update tick
+ for (Queue::iterator itr = m_QueuedPlayer.begin(); itr != m_QueuedPlayer.end(); ++itr)
+ if(WorldSession* session = (*itr)->GetSession())
+ session->KickPlayer();
+
+ m_QueuedPlayer.empty();
+}
+
+/// Kick (and save) the designated player
+bool World::KickPlayer(std::string playerName)
+{
+ SessionMap::iterator itr;
+
+ // session not removed at kick and will removed in next update tick
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second)
+ continue;
+ Player *player = itr->second->GetPlayer();
+ if(!player)
+ continue;
+ if( player->IsInWorld() )
+ {
+ if (playerName == player->GetName())
+ {
+ itr->second->KickPlayer();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
+uint8 World::BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author)
+{
+ loginDatabase.escape_string(nameOrIP);
+ loginDatabase.escape_string(reason);
+ std::string safe_author=author;
+ loginDatabase.escape_string(safe_author);
+
+ if(type != "ip" && !normalizePlayerName(nameOrIP))
+ return BAN_NOTFOUND; // Nobody to ban
+
+ uint32 duration_secs = TimeStringToSecs(duration);
+ QueryResult *resultAccounts = NULL; //used for kicking
+
+ ///- Update the database with ban information
+
+ if(type=="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());
+ }
+ else if(type=="account")
+ {
+ //No SQL injection as string is escaped
+ resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
+ }
+ else if(type=="character")
+ {
+ //No SQL injection as string is escaped
+ resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
+ }
+ else
+ return BAN_SYNTAX_ERROR; //Syntax problem
+
+ if(!resultAccounts)
+ if(type=="ip")
+ return BAN_SUCCESS; // ip correctly banned but nobody affected (yet)
+ else
+ return BAN_NOTFOUND; // Nobody to ban
+
+ ///- Disconnect all affected players (for IP it can be several)
+ do
+ {
+ Field* fieldsAccount = resultAccounts->Fetch();
+ uint32 account = fieldsAccount->GetUInt32();
+
+ if(type != "ip")
+ //No SQL injection as strings are escaped
+ 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());
+
+ WorldSession* sess = FindSession(account);
+ if( sess )
+ if(std::string(sess->GetPlayerName()) != author)
+ sess->KickPlayer();
+ }
+ while( resultAccounts->NextRow() );
+
+ delete resultAccounts;
+ return BAN_SUCCESS;
+}
+
+/// Remove a ban from an account or IP address
+bool World::RemoveBanAccount(std::string type, std::string nameOrIP)
+{
+ if(type == "ip")
+ {
+ loginDatabase.escape_string(nameOrIP);
+ loginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str());
+ }
+ else
+ {
+ uint32 account=0;
+ if(type == "account")
+ {
+ //NO SQL injection as name is escaped
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
+ if(!resultAccounts)
+ return false;
+ Field* fieldsAccount = resultAccounts->Fetch();
+ account = fieldsAccount->GetUInt32();
+
+ delete resultAccounts;
+ }
+ else if(type == "character")
+ {
+ if(!normalizePlayerName(nameOrIP))
+ return false;
+
+ //NO SQL injection as name is escaped
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
+ if(!resultAccounts)
+ return false;
+ Field* fieldsAccount = resultAccounts->Fetch();
+ account = fieldsAccount->GetUInt32();
+
+ delete resultAccounts;
+ }
+ if(!account)
+ return false;
+ //NO SQL injection as account is uint32
+ loginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account);
+ }
+ return true;
+}
+
+/// Update the game time
+void World::_UpdateGameTime()
+{
+ ///- update the time
+ time_t thisTime = time(NULL);
+ uint32 elapsed = uint32(thisTime - m_gameTime);
+ m_gameTime = thisTime;
+
+ ///- if there is a shutdown timer
+ if(m_ShutdownTimer > 0 && elapsed > 0)
+ {
+ ///- ... and it is overdue, stop the world (set m_stopEvent)
+ if( m_ShutdownTimer <= elapsed )
+ {
+ if(!(m_ShutdownMask & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
+ m_stopEvent = true;
+ else
+ m_ShutdownTimer = 1; // minimum timer value to wait idle state
+ }
+ ///- ... else decrease it and if necessary display a shutdown countdown to the users
+ else
+ {
+ m_ShutdownTimer -= elapsed;
+
+ ShutdownMsg();
+ }
+ }
+}
+
+/// Shutdown the server
+void World::ShutdownServ(uint32 time, uint32 options)
+{
+ m_ShutdownMask = options;
+
+ ///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
+ if(time==0)
+ {
+ if(!(options & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
+ m_stopEvent = true;
+ else
+ m_ShutdownTimer = 1; //So that the session count is re-evaluated at next world tick
+ }
+ ///- Else set the shutdown timer and warn users
+ else
+ {
+ m_ShutdownTimer = time;
+ ShutdownMsg(true);
+ }
+}
+
+/// Display a shutdown message to the user(s)
+void World::ShutdownMsg(bool show, Player* player)
+{
+ // not show messages for idle shutdown mode
+ if(m_ShutdownMask & SHUTDOWN_MASK_IDLE)
+ return;
+
+ ///- Display a message every 12 hours, hours, 5 minutes, minute, 5 seconds and finally seconds
+ if ( show ||
+ (m_ShutdownTimer < 10) ||
+ // < 30 sec; every 5 sec
+ (m_ShutdownTimer<30 && (m_ShutdownTimer % 5 )==0) ||
+ // < 5 min ; every 1 min
+ (m_ShutdownTimer<5*MINUTE && (m_ShutdownTimer % MINUTE )==0) ||
+ // < 30 min ; every 5 min
+ (m_ShutdownTimer<30*MINUTE && (m_ShutdownTimer % (5*MINUTE))==0) ||
+ // < 12 h ; every 1 h
+ (m_ShutdownTimer<12*HOUR && (m_ShutdownTimer % HOUR )==0) ||
+ // > 12 h ; every 12 h
+ (m_ShutdownTimer>12*HOUR && (m_ShutdownTimer % (12*HOUR) )==0))
+ {
+ std::string str = secsToTimeString(m_ShutdownTimer);
+
+ uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_TIME : SERVER_MSG_SHUTDOWN_TIME;
+
+ SendServerMessage(msgid,str.c_str(),player);
+ DEBUG_LOG("Server is %s in %s",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"),str.c_str());
+ }
+}
+
+/// Cancel a planned server shutdown
+void World::ShutdownCancel()
+{
+ if(!m_ShutdownTimer)
+ return;
+
+ uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
+
+ m_ShutdownMask = 0;
+ m_ShutdownTimer = 0;
+ SendServerMessage(msgid);
+
+ DEBUG_LOG("Server %s cancelled.",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"));
+}
+
+/// Send a server message to the user(s)
+void World::SendServerMessage(uint32 type, const char *text, Player* player)
+{
+ WorldPacket data(SMSG_SERVER_MESSAGE, 50); // guess size
+ data << uint32(type);
+ if(type <= SERVER_MSG_STRING)
+ data << text;
+
+ if(player)
+ player->GetSession()->SendPacket(&data);
+ else
+ SendGlobalMessage( &data );
+}
+
+void World::UpdateSessions( time_t diff )
+{
+ ///- Delete kicked sessions at add new session
+ for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
+ delete *itr;
+ m_kicked_sessions.clear();
+
+ ///- Then send an update signal to remaining ones
+ for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(!itr->second)
+ continue;
+
+ ///- and remove not active sessions from the list
+ if(!itr->second->Update(diff)) // As interval = 0
+ {
+ delete itr->second;
+ m_sessions.erase(itr);
+ }
+ }
+}
+
+// This handles the issued and queued CLI commands
+void World::ProcessCliCommands()
+{
+ if (cliCmdQueue.empty()) return;
+
+ CliCommandHolder *command;
+ pPrintf p_zprintf;
+ while (!cliCmdQueue.empty())
+ {
+ sLog.outDebug("CLI command under processing...");
+ command = cliCmdQueue.next();
+ command->Execute();
+ p_zprintf=command->GetOutputMethod();
+ delete command;
+ }
+ // print the console message here so it looks right
+ p_zprintf("mangos>");
+}
+
+void World::InitResultQueue()
+{
+ m_resultQueue = new SqlResultQueue;
+ CharacterDatabase.SetResultQueue(m_resultQueue);
+}
+
+void World::UpdateResultQueue()
+{
+ m_resultQueue->Update();
+}
+
+void World::UpdateRealmCharCount(uint32 accountId)
+{
+ CharacterDatabase.AsyncPQuery(this, &World::_UpdateRealmCharCount, accountId,
+ "SELECT COUNT(guid) FROM characters WHERE account = '%u'", accountId);
+}
+
+void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId)
+{
+ if (resultCharCount)
+ {
+ 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);
+ }
+}
+
+void World::InitDailyQuestResetTime()
+{
+ time_t mostRecentQuestTime;
+
+ QueryResult* result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily");
+ if(result)
+ {
+ Field *fields = result->Fetch();
+
+ mostRecentQuestTime = (time_t)fields[0].GetUInt64();
+ delete result;
+ }
+ else
+ mostRecentQuestTime = 0;
+
+ // client built-in time for reset is 6:00 AM
+ // FIX ME: client not show day start time
+ time_t curTime = time(NULL);
+ tm localTm = *localtime(&curTime);
+ localTm.tm_hour = 6;
+ localTm.tm_min = 0;
+ localTm.tm_sec = 0;
+
+ // current day reset time
+ time_t curDayResetTime = mktime(&localTm);
+
+ // last reset time before current moment
+ time_t resetTime = (curTime < curDayResetTime) ? curDayResetTime - DAY : curDayResetTime;
+
+ // need reset (if we have quest time before last reset time (not processed by some reason)
+ if(mostRecentQuestTime && mostRecentQuestTime <= resetTime)
+ m_NextDailyQuestReset = mostRecentQuestTime;
+ else
+ {
+ // plan next reset time
+ m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime;
+ }
+}
+
+void World::ResetDailyQuests()
+{
+ sLog.outDetail("Daily quests reset for all characters.");
+ CharacterDatabase.Execute("DELETE FROM character_queststatus_daily");
+ for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ if(itr->second->GetPlayer())
+ itr->second->GetPlayer()->ResetDailyQuestStatus();
+}
+
+void World::SetPlayerLimit( int32 limit, bool needUpdate )
+{
+ if(limit < -SEC_ADMINISTRATOR)
+ limit = -SEC_ADMINISTRATOR;
+
+ // lock update need
+ bool db_update_need = needUpdate || (limit < 0) != (m_playerLimit < 0) || (limit < 0 && m_playerLimit < 0 && limit != m_playerLimit);
+
+ m_playerLimit = limit;
+
+ if(db_update_need)
+ loginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID);
+}
+
+void World::UpdateMaxSessionCounters()
+{
+ m_maxActiveSessionCount = std::max(m_maxActiveSessionCount,uint32(m_sessions.size()-m_QueuedPlayer.size()));
+ m_maxQueuedSessionCount = std::max(m_maxQueuedSessionCount,uint32(m_QueuedPlayer.size()));
+}
diff --git a/src/game/World.h b/src/game/World.h
new file mode 100644
index 00000000000..56450832ab9
--- /dev/null
+++ b/src/game/World.h
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup world The World
+/// @{
+/// \file
+
+#ifndef __WORLD_H
+#define __WORLD_H
+
+#include "Common.h"
+#include "Timer.h"
+#include "Policies/Singleton.h"
+
+#include <map>
+#include <set>
+#include <list>
+
+class Object;
+class WorldPacket;
+class WorldSession;
+class Player;
+class Weather;
+struct ScriptAction;
+struct ScriptInfo;
+class CliCommandHolder;
+class SqlResultQueue;
+class QueryResult;
+class WorldSocket;
+
+enum ShutdownMask
+{
+ SHUTDOWN_MASK_RESTART = 1,
+ SHUTDOWN_MASK_IDLE = 2,
+};
+
+/// Timers for different object refresh rates
+enum WorldTimers
+{
+ WUPDATE_OBJECTS = 0,
+ WUPDATE_SESSIONS = 1,
+ WUPDATE_AUCTIONS = 2,
+ WUPDATE_WEATHERS = 3,
+ WUPDATE_UPTIME = 4,
+ WUPDATE_CORPSES = 5,
+ WUPDATE_EVENTS = 6,
+ WUPDATE_COUNT = 7
+};
+
+/// Configuration elements
+enum WorldConfigs
+{
+ CONFIG_COMPRESSION = 0,
+ CONFIG_GRID_UNLOAD,
+ CONFIG_INTERVAL_SAVE,
+ CONFIG_INTERVAL_GRIDCLEAN,
+ CONFIG_INTERVAL_MAPUPDATE,
+ CONFIG_INTERVAL_CHANGEWEATHER,
+ CONFIG_PORT_WORLD,
+ CONFIG_SOCKET_SELECTTIME,
+ CONFIG_GROUP_XP_DISTANCE,
+ CONFIG_SIGHT_MONSTER,
+ CONFIG_SIGHT_GUARDER,
+ CONFIG_GAME_TYPE,
+ CONFIG_REALM_ZONE,
+ CONFIG_ALLOW_TWO_SIDE_ACCOUNTS,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION,
+ CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL,
+ CONFIG_ALLOW_TWO_SIDE_WHO_LIST,
+ CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND,
+ CONFIG_STRICT_PLAYER_NAMES,
+ CONFIG_STRICT_CHARTER_NAMES,
+ CONFIG_STRICT_PET_NAMES,
+ CONFIG_CHARACTERS_CREATING_DISABLED,
+ CONFIG_CHARACTERS_PER_ACCOUNT,
+ CONFIG_CHARACTERS_PER_REALM,
+ CONFIG_SKIP_CINEMATICS,
+ CONFIG_MAX_PLAYER_LEVEL,
+ CONFIG_START_PLAYER_LEVEL,
+ CONFIG_MAX_HONOR_POINTS,
+ CONFIG_MAX_ARENA_POINTS,
+ CONFIG_INSTANCE_IGNORE_LEVEL,
+ CONFIG_INSTANCE_IGNORE_RAID,
+ CONFIG_BATTLEGROUND_CAST_DESERTER,
+ CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE,
+ CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY,
+ CONFIG_INSTANCE_RESET_TIME_HOUR,
+ CONFIG_INSTANCE_UNLOAD_DELAY,
+ CONFIG_CAST_UNSTUCK,
+ CONFIG_MAX_PRIMARY_TRADE_SKILL,
+ CONFIG_MIN_PETITION_SIGNS,
+ CONFIG_GM_WISPERING_TO,
+ CONFIG_GM_IN_GM_LIST,
+ CONFIG_GM_IN_WHO_LIST,
+ CONFIG_GM_LOGIN_STATE,
+ CONFIG_GM_LOG_TRADE,
+ CONFIG_GROUP_VISIBILITY,
+ CONFIG_MAIL_DELIVERY_DELAY,
+ CONFIG_UPTIME_UPDATE,
+ CONFIG_SKILL_CHANCE_ORANGE,
+ CONFIG_SKILL_CHANCE_YELLOW,
+ CONFIG_SKILL_CHANCE_GREEN,
+ CONFIG_SKILL_CHANCE_GREY,
+ CONFIG_SKILL_CHANCE_MINING_STEPS,
+ CONFIG_SKILL_CHANCE_SKINNING_STEPS,
+ CONFIG_SKILL_PROSPECTING,
+ CONFIG_SKILL_GAIN_CRAFTING,
+ CONFIG_SKILL_GAIN_DEFENSE,
+ CONFIG_SKILL_GAIN_GATHERING,
+ CONFIG_SKILL_GAIN_WEAPON,
+ CONFIG_MAX_OVERSPEED_PINGS,
+ CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY,
+ CONFIG_WEATHER,
+ CONFIG_EXPANSION,
+ CONFIG_CHATFLOOD_MESSAGE_COUNT,
+ CONFIG_CHATFLOOD_MESSAGE_DELAY,
+ CONFIG_CHATFLOOD_MUTE_TIME,
+ CONFIG_EVENT_ANNOUNCE,
+ CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS,
+ CONFIG_WORLD_BOSS_LEVEL_DIFF,
+ CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF,
+ CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF,
+ CONFIG_DETECT_POS_COLLISION,
+ CONFIG_RESTRICTED_LFG_CHANNEL,
+ CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL,
+ CONFIG_TALENTS_INSPECTING,
+ CONFIG_CHAT_FAKE_MESSAGE_PREVENTING,
+ CONFIG_TCP_NO_DELAY,
+ CONFIG_CORPSE_DECAY_NORMAL,
+ CONFIG_CORPSE_DECAY_RARE,
+ CONFIG_CORPSE_DECAY_ELITE,
+ CONFIG_CORPSE_DECAY_RAREELITE,
+ CONFIG_CORPSE_DECAY_WORLDBOSS,
+ CONFIG_ADDON_CHANNEL,
+ CONFIG_DEATH_SICKNESS_LEVEL,
+ CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP,
+ CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE,
+ CONFIG_THREAT_RADIUS,
+ CONFIG_DECLINED_NAMES_USED,
+ CONFIG_LISTEN_RANGE_SAY,
+ CONFIG_LISTEN_RANGE_TEXTEMOTE,
+ CONFIG_LISTEN_RANGE_YELL,
+ CONFIG_VALUE_COUNT
+};
+
+/// Server rates
+enum Rates
+{
+ RATE_HEALTH=0,
+ RATE_POWER_MANA,
+ RATE_POWER_RAGE_INCOME,
+ RATE_POWER_RAGE_LOSS,
+ RATE_POWER_FOCUS,
+ RATE_SKILL_DISCOVERY,
+ RATE_DROP_ITEM_POOR,
+ RATE_DROP_ITEM_NORMAL,
+ RATE_DROP_ITEM_UNCOMMON,
+ RATE_DROP_ITEM_RARE,
+ RATE_DROP_ITEM_EPIC,
+ RATE_DROP_ITEM_LEGENDARY,
+ RATE_DROP_ITEM_ARTIFACT,
+ RATE_DROP_ITEM_REFERENCED,
+ RATE_DROP_MONEY,
+ RATE_XP_KILL,
+ RATE_XP_QUEST,
+ RATE_XP_EXPLORE,
+ RATE_XP_PAST_70,
+ RATE_REPUTATION_GAIN,
+ RATE_CREATURE_NORMAL_HP,
+ RATE_CREATURE_ELITE_ELITE_HP,
+ RATE_CREATURE_ELITE_RAREELITE_HP,
+ RATE_CREATURE_ELITE_WORLDBOSS_HP,
+ RATE_CREATURE_ELITE_RARE_HP,
+ RATE_CREATURE_NORMAL_DAMAGE,
+ RATE_CREATURE_ELITE_ELITE_DAMAGE,
+ RATE_CREATURE_ELITE_RAREELITE_DAMAGE,
+ RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE,
+ RATE_CREATURE_ELITE_RARE_DAMAGE,
+ RATE_CREATURE_NORMAL_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE,
+ RATE_CREATURE_ELITE_RARE_SPELLDAMAGE,
+ RATE_CREATURE_AGGRO,
+ RATE_REST_INGAME,
+ RATE_REST_OFFLINE_IN_TAVERN_OR_CITY,
+ RATE_REST_OFFLINE_IN_WILDERNESS,
+ RATE_DAMAGE_FALL,
+ RATE_AUCTION_TIME,
+ RATE_AUCTION_DEPOSIT,
+ RATE_AUCTION_CUT,
+ RATE_HONOR,
+ RATE_MINING_AMOUNT,
+ RATE_MINING_NEXT,
+ RATE_TALENT,
+ RATE_LOYALTY,
+ RATE_CORPSE_DECAY_LOOTED,
+ RATE_INSTANCE_RESET_TIME,
+ RATE_TARGET_POS_RECALCULATION_RANGE,
+ RATE_DURABILITY_LOSS_DAMAGE,
+ RATE_DURABILITY_LOSS_PARRY,
+ RATE_DURABILITY_LOSS_ABSORB,
+ RATE_DURABILITY_LOSS_BLOCK,
+ MAX_RATES
+};
+
+/// Type of server
+enum RealmType
+{
+ REALM_TYPE_NORMAL = 0,
+ REALM_TYPE_PVP = 1,
+ REALM_TYPE_NORMAL2 = 4,
+ REALM_TYPE_RP = 6,
+ REALM_TYPE_RPPVP = 8,
+ REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
+ // replaced by REALM_PVP in realm list
+};
+
+enum RealmZone
+{
+ REALM_ZONE_UNKNOWN = 0, // any language
+ REALM_ZONE_DEVELOPMENT = 1, // any language
+ REALM_ZONE_UNITED_STATES = 2, // extended-Latin
+ REALM_ZONE_OCEANIC = 3, // extended-Latin
+ REALM_ZONE_LATIN_AMERICA = 4, // extended-Latin
+ REALM_ZONE_TOURNAMENT_5 = 5, // basic-Latin at create, any at login
+ REALM_ZONE_KOREA = 6, // East-Asian
+ REALM_ZONE_TOURNAMENT_7 = 7, // basic-Latin at create, any at login
+ REALM_ZONE_ENGLISH = 8, // extended-Latin
+ REALM_ZONE_GERMAN = 9, // extended-Latin
+ REALM_ZONE_FRENCH = 10, // extended-Latin
+ REALM_ZONE_SPANISH = 11, // extended-Latin
+ REALM_ZONE_RUSSIAN = 12, // Cyrillic
+ REALM_ZONE_TOURNAMENT_13 = 13, // basic-Latin at create, any at login
+ REALM_ZONE_TAIWAN = 14, // East-Asian
+ REALM_ZONE_TOURNAMENT_15 = 15, // basic-Latin at create, any at login
+ REALM_ZONE_CHINA = 16, // East-Asian
+ REALM_ZONE_CN1 = 17, // basic-Latin at create, any at login
+ REALM_ZONE_CN2 = 18, // basic-Latin at create, any at login
+ REALM_ZONE_CN3 = 19, // basic-Latin at create, any at login
+ REALM_ZONE_CN4 = 20, // basic-Latin at create, any at login
+ REALM_ZONE_CN5 = 21, // basic-Latin at create, any at login
+ REALM_ZONE_CN6 = 22, // basic-Latin at create, any at login
+ REALM_ZONE_CN7 = 23, // basic-Latin at create, any at login
+ REALM_ZONE_CN8 = 24, // basic-Latin at create, any at login
+ REALM_ZONE_TOURNAMENT_25 = 25, // basic-Latin at create, any at login
+ REALM_ZONE_TEST_SERVER = 26, // any language
+ REALM_ZONE_TOURNAMENT_27 = 27, // basic-Latin at create, any at login
+ REALM_ZONE_QA_SERVER = 28, // any language
+ REALM_ZONE_CN9 = 29 // basic-Latin at create, any at login
+};
+
+/// Ban function return codes
+enum BanReturn
+{
+ BAN_SUCCESS,
+ BAN_SYNTAX_ERROR,
+ BAN_NOTFOUND
+};
+
+// DB scripting commands
+#define SCRIPT_COMMAND_TALK 0 // source = unit, target=any, datalong ( 0=say, 1=whisper, 2=yell, 3=emote text)
+#define SCRIPT_COMMAND_EMOTE 1 // source = unit, datalong = anim_id
+#define SCRIPT_COMMAND_FIELD_SET 2 // source = any, datalong = field_id, datalog2 = value
+#define SCRIPT_COMMAND_MOVE_TO 3 // source = Creature, datalog2 = time, x/y/z
+#define SCRIPT_COMMAND_FLAG_SET 4 // source = any, datalong = field_id, datalog2 = bitmask
+#define SCRIPT_COMMAND_FLAG_REMOVE 5 // source = any, datalong = field_id, datalog2 = bitmask
+#define SCRIPT_COMMAND_TELEPORT_TO 6 // source or target with Player, datalong = map_id, x/y/z
+#define SCRIPT_COMMAND_QUEST_EXPLORED 7 // one from source or target must be Player, another GO/Creature, datalong=quest_id, datalong2=distance or 0
+#define SCRIPT_COMMAND_RESPAWN_GAMEOBJECT 9 // source = any (summoner), datalong=db_guid, datalong2=despawn_delay
+#define SCRIPT_COMMAND_TEMP_SUMMON_CREATURE 10 // source = any (summoner), datalong=creature entry, datalong2=despawn_delay
+#define SCRIPT_COMMAND_OPEN_DOOR 11 // source = unit, datalong=db_guid, datalong2=reset_delay
+#define SCRIPT_COMMAND_CLOSE_DOOR 12 // source = unit, datalong=db_guid, datalong2=reset_delay
+#define SCRIPT_COMMAND_ACTIVATE_OBJECT 13 // source = unit, target=GO
+#define SCRIPT_COMMAND_REMOVE_AURA 14 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id
+#define SCRIPT_COMMAND_CAST_SPELL 15 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id
+
+/// CLI related stuff, define here to prevent cyclic dependancies
+
+typedef int(* pPrintf)(const char*,...);
+typedef void(* pCliFunc)(char *,pPrintf);
+
+/// Command Template class
+struct CliCommand
+{
+ char const * cmd;
+ pCliFunc Func;
+ char const * description;
+};
+
+/// Storage class for commands issued for delayed execution
+class CliCommandHolder
+{
+ private:
+ const CliCommand *cmd;
+ char *args;
+ pPrintf m_zprintf;
+ public:
+ CliCommandHolder(const CliCommand *command, const char *arguments, pPrintf p_zprintf)
+ : cmd(command), m_zprintf(p_zprintf)
+ {
+ size_t len = strlen(arguments)+1;
+ args = new char[len];
+ memcpy(args, arguments, len);
+ }
+ ~CliCommandHolder() { delete[] args; }
+ void Execute() const { cmd->Func(args, m_zprintf); }
+ pPrintf GetOutputMethod() const {return (m_zprintf);}
+};
+
+/// The World
+class World
+{
+ public:
+ static volatile bool m_stopEvent;
+ static volatile uint32 m_worldLoopCounter;
+
+ World();
+ ~World();
+
+ WorldSession* FindSession(uint32 id) const;
+ void AddSession(WorldSession *s);
+ bool RemoveSession(uint32 id);
+ /// Get the number of current active sessions
+ void UpdateMaxSessionCounters();
+ uint32 GetActiveAndQueuedSessionCount() const { return m_sessions.size(); }
+ uint32 GetActiveSessionCount() const { return m_sessions.size() - m_QueuedPlayer.size(); }
+ uint32 GetQueuedSessionCount() const { return m_QueuedPlayer.size(); }
+ /// 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);
+
+ Weather* FindWeather(uint32 id) const;
+ Weather* AddWeather(uint32 zone_id);
+ void RemoveWeather(uint32 zone_id);
+
+ /// Get the active session server limit (or security level limitations)
+ uint32 GetPlayerAmountLimit() const { return m_playerLimit >= 0 ? m_playerLimit : 0; }
+ AccountTypes GetPlayerSecurityLimit() const { return m_playerLimit <= 0 ? AccountTypes(-m_playerLimit) : SEC_PLAYER; }
+
+ /// Set the active session server limit (or security level limitation)
+ void SetPlayerLimit(int32 limit, bool needUpdate = false);
+
+ //player Queue
+ typedef std::list<WorldSocket*> Queue;
+ void AddQueuedPlayer(WorldSocket* Socket);
+ void RemoveQueuedPlayer(WorldSocket* Socket);
+ int32 GetQueuePos(WorldSocket* Socket);
+ uint32 GetQueueSize() const { return m_QueuedPlayer.size(); }
+
+ /// \todo Actions on m_allowMovement still to be implemented
+ /// Is movement allowed?
+ bool getAllowMovement() const { return m_allowMovement; }
+ /// Allow/Disallow object movements
+ void SetAllowMovement(bool allow) { m_allowMovement = allow; }
+
+ /// Set a new Message of the Day
+ void SetMotd(std::string motd) { m_motd = motd; }
+ /// Get the current Message of the Day
+ const char* GetMotd() const { return m_motd.c_str(); }
+
+ uint32 GetDefaultDbcLocale() const { return m_defaultDbcLocale; }
+
+ /// Get the path where data (dbc, maps) are stored on disk
+ std::string GetDataPath() const { return m_dataPath; }
+
+ /// When server started?
+ time_t const& GetStartTime() const { return m_startTime; }
+ /// What time is it?
+ time_t const& GetGameTime() const { return m_gameTime; }
+ /// Uptime (in secs)
+ uint32 GetUptime() const { return uint32(m_gameTime - m_startTime); }
+
+ /// Get the maximum skill level a player can reach
+ uint16 GetConfigMaxSkillValue() const
+ {
+ uint32 lvl = getConfig(CONFIG_MAX_PLAYER_LEVEL);
+ return lvl > 60 ? 300 + ((lvl - 60) * 75) / 10 : lvl*5;
+ }
+
+ void SetInitialWorldSettings();
+ void LoadConfigSettings(bool reload = false);
+
+ void SendWorldText(int32 string_id, ...);
+ void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0);
+ void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0);
+ void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0);
+ void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL);
+
+ /// Are we in the middle of a shutdown?
+ uint32 GetShutdownMask() const { return m_ShutdownMask; }
+ bool IsShutdowning() const { return m_ShutdownTimer > 0; }
+ void ShutdownServ(uint32 time, uint32 options = 0);
+ void ShutdownCancel();
+ void ShutdownMsg(bool show = false, Player* player = NULL);
+
+ void Update(time_t diff);
+
+ void UpdateSessions( time_t diff );
+ /// Set a server rate (see #Rates)
+ void setRate(Rates rate,float value) { rate_values[rate]=value; }
+ /// Get a server rate (see #Rates)
+ float getRate(Rates rate) const { return rate_values[rate]; }
+
+ /// Set a server configuration element (see #WorldConfigs)
+ void setConfig(uint32 index,uint32 value)
+ {
+ if(index<CONFIG_VALUE_COUNT)
+ m_configs[index]=value;
+ }
+
+ /// Get a server configuration element (see #WorldConfigs)
+ uint32 getConfig(uint32 index) const
+ {
+ if(index<CONFIG_VALUE_COUNT)
+ return m_configs[index];
+ else
+ return 0;
+ }
+
+ /// Are we on a "Player versus Player" server?
+ bool IsPvPRealm() { return (getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP); }
+ bool IsFFAPvPRealm() { return getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; }
+
+ bool KickPlayer(std::string playerName);
+ void KickAll();
+ void KickAllLess(AccountTypes sec);
+ void KickAllQueued();
+ uint8 BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author);
+ bool RemoveBanAccount(std::string type, std::string nameOrIP);
+
+ 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);
+ bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); }
+
+ // for max speed access
+ static float GetMaxVisibleDistanceForCreature() { return m_MaxVisibleDistanceForCreature; }
+ static float GetMaxVisibleDistanceForPlayer() { return m_MaxVisibleDistanceForPlayer; }
+ static float GetMaxVisibleDistanceForObject() { return m_MaxVisibleDistanceForObject; }
+ static float GetMaxVisibleDistanceInFlight() { return m_MaxVisibleDistanceInFlight; }
+ static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; }
+ static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; }
+
+ void ProcessCliCommands();
+ void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); }
+
+ void UpdateResultQueue();
+ void InitResultQueue();
+
+ void UpdateRealmCharCount(uint32 accid);
+
+ LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const { if(m_availableDbcLocaleMask & (1 << locale)) return locale; else return m_defaultDbcLocale; }
+ protected:
+ void _UpdateGameTime();
+ void ScriptsProcess();
+ // callback for UpdateRealmCharacters
+ void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId);
+
+ void InitDailyQuestResetTime();
+ void ResetDailyQuests();
+ private:
+ time_t m_startTime;
+ time_t m_gameTime;
+ IntervalTimer m_timers[WUPDATE_COUNT];
+ uint32 mail_timer;
+ uint32 mail_timer_expires;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Weather*> WeatherMap;
+ WeatherMap m_weathers;
+ typedef HM_NAMESPACE::hash_map<uint32, WorldSession*> SessionMap;
+ SessionMap m_sessions;
+ std::set<WorldSession*> m_kicked_sessions;
+ uint32 m_maxActiveSessionCount;
+ uint32 m_maxQueuedSessionCount;
+
+ std::multimap<time_t, ScriptAction> m_scriptSchedule;
+
+ float rate_values[MAX_RATES];
+ uint32 m_configs[CONFIG_VALUE_COUNT];
+ int32 m_playerLimit;
+ LocaleConstant m_defaultDbcLocale; // from config for one from loaded DBC locales
+ uint32 m_availableDbcLocaleMask; // by loaded DBC
+ void DetectDBCLang();
+ bool m_allowMovement;
+ std::string m_motd;
+ std::string m_dataPath;
+
+ uint32 m_ShutdownTimer;
+ uint32 m_ShutdownMask;
+
+ // for max speed access
+ static float m_MaxVisibleDistanceForCreature;
+ static float m_MaxVisibleDistanceForPlayer;
+ static float m_MaxVisibleDistanceForObject;
+ static float m_MaxVisibleDistanceInFlight;
+ static float m_VisibleUnitGreyDistance;
+ static float m_VisibleObjectGreyDistance;
+
+ // CLI command holder to be thread safe
+ ZThread::LockedQueue<CliCommandHolder*, ZThread::FastMutex> cliCmdQueue;
+ SqlResultQueue *m_resultQueue;
+
+ // next daily quests reset time
+ time_t m_NextDailyQuestReset;
+
+ //Player Queue
+ Queue m_QueuedPlayer;
+};
+
+extern uint32 realmID;
+
+#define sWorld MaNGOS::Singleton<World>::Instance()
+#endif
+/// @}
diff --git a/src/game/WorldLog.cpp b/src/game/WorldLog.cpp
new file mode 100644
index 00000000000..21fc26da41b
--- /dev/null
+++ b/src/game/WorldLog.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "WorldLog.h"
+#include "Policies/SingletonImp.h"
+#include "Config/ConfigEnv.h"
+
+#define CLASS_LOCK MaNGOS::ClassLevelLockable<WorldLog, ZThread::FastMutex>
+INSTANTIATE_SINGLETON_2(WorldLog, CLASS_LOCK);
+INSTANTIATE_CLASS_MUTEX(WorldLog, ZThread::FastMutex);
+
+#define WORLD_LOG_FILE_STRING "world.log"
+
+/// Open the log file (if specified so in the configuration file)
+void WorldLog::Initialize()
+{
+ std::string logsDir = sConfig.GetStringDefault("LogsDir","");
+
+ if(!logsDir.empty())
+ {
+ if((logsDir.at(logsDir.length()-1)!='/') && (logsDir.at(logsDir.length()-1)!='\\'))
+ logsDir.append("/");
+ }
+
+ std::string logname = sConfig.GetStringDefault("WorldLogFile", "");
+ if(!logname.empty())
+ {
+ i_file = fopen((logsDir+logname).c_str(), "w");
+ }
+}
+
+#define sWorldLog WorldLog::Instance()
diff --git a/src/game/WorldLog.h b/src/game/WorldLog.h
new file mode 100644
index 00000000000..5047161b63e
--- /dev/null
+++ b/src/game/WorldLog.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w
+/// @{
+/// \file
+
+#ifndef MANGOS_WORLDLOG_H
+#define MANGOS_WORLDLOG_H
+
+#include "Common.h"
+#include "Policies/Singleton.h"
+#include "Errors.h"
+
+#include <stdarg.h>
+
+/// %Log packets to a file
+class MANGOS_DLL_DECL WorldLog : public MaNGOS::Singleton<WorldLog, MaNGOS::ClassLevelLockable<WorldLog, ZThread::FastMutex> >
+{
+ friend class MaNGOS::OperatorNew<WorldLog>;
+ WorldLog() : i_file(NULL) { Initialize(); }
+ WorldLog(const WorldLog &);
+ WorldLog& operator=(const WorldLog &);
+ typedef MaNGOS::ClassLevelLockable<WorldLog, ZThread::FastMutex>::Lock Guard;
+
+ /// Close the file in destructor
+ ~WorldLog()
+ {
+ if( i_file != NULL )
+ fclose(i_file);
+ i_file = NULL;
+ }
+
+ public:
+ void Initialize();
+ /// Is the world logger active?
+ inline bool LogWorld(void) const { return (i_file != NULL); }
+ /// %Log to the file
+ inline void Log(char const *fmt, ...)
+ {
+ if( LogWorld() )
+ {
+ Guard guard(*this);
+ ASSERT(i_file);
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(i_file, fmt, args);
+ va_end(args);
+
+ fflush(i_file);
+ }
+ }
+
+ private:
+ FILE *i_file;
+};
+
+#define sWorldLog WorldLog::Instance()
+#endif
+/// @}
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp
new file mode 100644
index 00000000000..eaba12b4a10
--- /dev/null
+++ b/src/game/WorldSession.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldSocket.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "Group.h"
+#include "Guild.h"
+#include "World.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "BattleGroundMgr.h"
+#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler
+#include "Chat.h"
+#include "SocialMgr.h"
+
+/// WorldSession constructor
+WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale) :
+LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
+_player(NULL), _socket(sock),_security(sec), _accountId(id), m_isTBC(tbc),
+m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
+_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0)
+{
+}
+
+/// WorldSession destructor
+WorldSession::~WorldSession()
+{
+ ///- unload player if not unloaded
+ if(_player)
+ LogoutPlayer(true);
+
+ /// - If have unclosed socket, close it
+ if(_socket)
+ _socket->CloseSocket();
+
+ _socket = NULL;
+
+ ///- empty incoming packet queue
+ while(!_recvQueue.empty())
+ {
+ WorldPacket *packet = _recvQueue.next();
+ delete packet;
+ }
+}
+
+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",
+ GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
+}
+
+/// Get the player name
+char const* WorldSession::GetPlayerName() const
+{
+ return GetPlayer() ? GetPlayer()->GetName() : "<none>";
+}
+
+/// Set the WorldSocket associated with this session
+void WorldSession::SetSocket(WorldSocket *sock)
+{
+ _socket = sock;
+}
+
+/// Send a packet to the client
+void WorldSession::SendPacket(WorldPacket const* packet)
+{
+ if (!_socket)
+ return;
+ #ifdef MANGOS_DEBUG
+ // Code for network use statistic
+ static uint64 sendPacketCount = 0;
+ static uint64 sendPacketBytes = 0;
+
+ static time_t firstTime = time(NULL);
+ static time_t lastTime = firstTime; // next 60 secs start time
+
+ static uint64 sendLastPacketCount = 0;
+ static uint64 sendLastPacketBytes = 0;
+
+ time_t cur_time = time(NULL);
+
+ if((cur_time - lastTime) < 60)
+ {
+ sendPacketCount+=1;
+ sendPacketBytes+=packet->size();
+
+ sendLastPacketCount+=1;
+ sendLastPacketBytes+=packet->size();
+ }
+ else
+ {
+ uint64 minTime = uint64(cur_time - lastTime);
+ uint64 fullTime = uint64(lastTime - firstTime);
+ sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
+ sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
+
+ lastTime = cur_time;
+ sendLastPacketCount = 1;
+ sendLastPacketBytes = packet->wpos(); // wpos is real written size
+ }
+ #endif // !MANGOS_DEBUG
+
+ _socket->SendPacket(packet);
+}
+
+/// Add an incoming packet to the queue
+void WorldSession::QueuePacket(WorldPacket& packet)
+{
+ WorldPacket *pck = new WorldPacket(packet);
+ _recvQueue.add(pck);
+}
+
+/// Logging helper for unexpected opcodes
+void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
+{
+ sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode(),
+ reason);
+}
+
+/// Update the WorldSession (triggered by World update)
+bool WorldSession::Update(uint32 /*diff*/)
+{
+ WorldPacket *packet;
+
+ ///- Retrieve packets from the receive queue and call the appropriate handlers
+ /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list?
+ /// answer : there is a way, but this is better, because it would use redundant RAM
+ while (!_recvQueue.empty())
+ {
+ packet = _recvQueue.next();
+
+ /*#if 1
+ sLog.outError( "MOEP: %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ #endif*/
+
+ if(packet->GetOpcode() >= NUM_MSG_TYPES)
+ {
+ sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ }
+ else
+ {
+ OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
+ switch (opHandle.status)
+ {
+ case STATUS_LOGGEDIN:
+ if(!_player)
+ {
+ // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
+ if(!m_playerRecentlyLogout)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ }
+ else if(_player->IsInWorld())
+ (this->*opHandle.handler)(*packet);
+ // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
+ break;
+ case STATUS_TRANSFER_PENDING:
+ if(!_player)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ else if(_player->IsInWorld())
+ logUnexpectedOpcode(packet, "the player is still in world");
+ else
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_AUTHED:
+ m_playerRecentlyLogout = false;
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_NEVER:
+ sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ break;
+ }
+ }
+
+ delete packet;
+ }
+
+ ///- If necessary, log the player out
+ time_t currTime = time(NULL);
+ if (!_socket || (ShouldLogOut(currTime) && !m_playerLoading))
+ LogoutPlayer(true);
+
+ if (!_socket)
+ return false; //Will remove this session from the world session map
+
+ return true;
+}
+
+/// %Log the player out
+void WorldSession::LogoutPlayer(bool Save)
+{
+ // finish pending transfers before starting the logout
+ while(_player && _player->IsBeingTeleported())
+ HandleMoveWorldportAckOpcode();
+
+ m_playerLogout = true;
+
+ if (_player)
+ {
+ ///- If the player just died before logging out, make him appear as a ghost
+ //FIXME: logout must be delayed in case lost connection with client in time of combat
+ if (_player->GetDeathTimer())
+ {
+ _player->getHostilRefManager().deleteReferences();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+ else if (!_player->getAttackers().empty())
+ {
+ _player->CombatStop();
+ _player->getHostilRefManager().setOnlineOfflineState(false);
+ _player->RemoveAllAurasOnDeath();
+
+ // build set of player who attack _player or who have pet attacking of _player
+ std::set<Player*> aset;
+ for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
+ {
+ Unit* owner = (*itr)->GetOwner(); // including player controlled case
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)owner);
+ }
+ else
+ if((*itr)->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)(*itr));
+ }
+
+ _player->SetPvPDeath(!aset.empty());
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+
+ // give honor to all attackers from set like group case
+ for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
+ (*itr)->RewardHonor(_player,aset.size());
+
+ // give bg rewards and update counters like kill by first from attackers
+ // this can't be called for all attackers.
+ if(!aset.empty())
+ if(BattleGround *bg = _player->GetBattleGround())
+ bg->HandleKillPlayer(_player,*aset.begin());
+ }
+ else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
+ {
+ // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
+ //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+
+ ///- Remove player from battleground (teleport to entrance)
+ if(_player->InBattleGround())
+ _player->LeaveBattleground();
+
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if(int32 bgTypeId = _player->GetBattleGroundQueueId(i))
+ {
+ _player->RemoveBattleGroundQueueId(bgTypeId);
+ sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true);
+ }
+ }
+
+ ///- 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());
+
+ ///- 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());
+ if(guild)
+ {
+ guild->LoadPlayerStatsByGuid(_player->GetGUID());
+ guild->UpdateLogoutTime(_player->GetGUID());
+
+ WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
+ data<<(uint8)GE_SIGNED_OFF;
+ data<<(uint8)1;
+ data<<_player->GetName();
+ data<<_player->GetGUID();
+ guild->BroadcastPacket(&data);
+ }
+
+ ///- Remove pet
+ _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
+
+ ///- empty buyback items and save the player in the database
+ // some save parts only correctly work in case player present in map/player_lists (pets, etc)
+ if(Save)
+ {
+ uint32 eslot;
+ for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
+ {
+ eslot = j - BUYBACK_SLOT_START;
+ _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0);
+ }
+ _player->SaveToDB();
+ }
+
+ ///- Leave all channels before player delete...
+ _player->CleanupChannels();
+
+ ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
+ _player->UninviteFromGroup();
+
+ // remove player from the group if he is:
+ // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
+ if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && _socket)
+ _player->RemoveFromGroup();
+
+ ///- 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()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false);
+ // RemoveFromWorld does cleanup that requires the player to be in the accessor
+ ObjectAccessor::Instance().RemoveObject(_player);
+
+ ///- Send update to group
+ if(_player->GetGroup())
+ _player->GetGroup()->SendUpdate();
+
+ ///- Broadcast a logout message to the player's friends
+ sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), "", true);
+
+ ///- Delete the player object
+ _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
+
+ delete _player;
+ _player = NULL;
+
+ ///- Send the 'logout complete' packet to the client
+ WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
+ SendPacket( &data );
+
+ ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
+ //No SQL injection as AccountId is uint32
+ CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId());
+ sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
+ }
+
+ m_playerLogout = false;
+ m_playerRecentlyLogout = true;
+ LogoutRequest(0);
+}
+
+/// Kick a player out of the World
+void WorldSession::KickPlayer()
+{
+ if(!_socket)
+ return;
+
+ // player will be logout and session will removed in next update tick
+ _socket->CloseSocket();
+ _socket = NULL;
+}
+
+/// Cancel channeling handler
+
+void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
+{
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+
+ va_start(ap, Text);
+ vsnprintf( szStr, 1024, Text, ap );
+ va_end(ap);
+
+ uint32 length = strlen(szStr)+1;
+ WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
+ data << length;
+ data << szStr;
+ SendPacket(&data);
+}
+
+void WorldSession::SendNotification(const char *format,...)
+{
+ if(format)
+ {
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+ va_start(ap, format);
+ vsnprintf( szStr, 1024, format, ap );
+ va_end(ap);
+
+ WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
+ data << szStr;
+ SendPacket(&data);
+ }
+}
+
+const char * WorldSession::GetMangosString( int32 entry )
+{
+ return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
+}
+
+void WorldSession::Handle_NULL( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_Depricated( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h
new file mode 100644
index 00000000000..05df0afe3a8
--- /dev/null
+++ b/src/game/WorldSession.h
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w
+/// @{
+/// \file
+
+#ifndef __WORLDSESSION_H
+#define __WORLDSESSION_H
+
+#include "Common.h"
+
+class MailItemsInfo;
+struct ItemPrototype;
+struct AuctionEntry;
+
+class Creature;
+class Item;
+class Object;
+class Player;
+class Unit;
+class WorldPacket;
+class WorldSocket;
+class WorldSession;
+class QueryResult;
+class LoginQueryHolder;
+class CharacterHandler;
+
+#define CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S));
+
+enum PartyOperation
+{
+ PARTY_OP_INVITE = 0,
+ PARTY_OP_LEAVE = 2
+};
+
+enum PartyResult
+{
+ PARTY_RESULT_OK = 0,
+ PARTY_RESULT_CANT_FIND_TARGET = 1,
+ PARTY_RESULT_NOT_IN_YOUR_PARTY = 2,
+ PARTY_RESULT_NOT_IN_YOUR_INSTANCE = 3,
+ PARTY_RESULT_PARTY_FULL = 4,
+ PARTY_RESULT_ALREADY_IN_GROUP = 5,
+ PARTY_RESULT_YOU_NOT_IN_GROUP = 6,
+ PARTY_RESULT_YOU_NOT_LEADER = 7,
+ PARTY_RESULT_TARGET_UNFRIENDLY = 8,
+ PARTY_RESULT_TARGET_IGNORE_YOU = 9,
+ PARTY_RESULT_INVITE_RESTRICTED = 13
+};
+
+/// Player session in the World
+class MANGOS_DLL_SPEC WorldSession
+{
+ friend class CharacterHandler;
+ public:
+ WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale);
+ ~WorldSession();
+
+ bool PlayerLoading() const { return m_playerLoading; }
+ bool PlayerLogout() const { return m_playerLogout; }
+
+ void SizeError(WorldPacket const& packet, uint32 size) const;
+
+ void SendPacket(WorldPacket const* packet);
+ void SendNotification(const char *format,...) ATTR_PRINTF(2,3);
+ void SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type);
+ void SendPartyResult(PartyOperation operation, std::string member, PartyResult res);
+ void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3);
+
+ uint32 GetSecurity() const { return _security; }
+ uint32 GetAccountId() const { return _accountId; }
+ Player* GetPlayer() const { return _player; }
+ char const* GetPlayerName() const;
+ void SetSecurity(uint32 security) { _security = security; }
+ void SetSocket(WorldSocket *sock);
+ void SetPlayer(Player *plr) { _player = plr; }
+ bool IsTBC() const { return m_isTBC; }
+
+ /// Is the user engaged in a log out process?
+ bool isLogingOut() const { return _logoutTime || m_playerLogout; }
+
+ /// Engage the logout process for the user
+ void LogoutRequest(time_t requestTime)
+ {
+ _logoutTime = requestTime;
+ }
+
+ /// Is logout cooldown expired?
+ bool ShouldLogOut(time_t currTime) const
+ {
+ return (_logoutTime > 0 && currTime >= _logoutTime + 20);
+ }
+
+ void LogoutPlayer(bool Save);
+ void KickPlayer();
+
+ void QueuePacket(WorldPacket& packet);
+ bool Update(uint32 diff);
+
+ //void SendTestCreatureQueryOpcode( uint32 entry, uint64 guid, uint32 testvalue );
+ void SendNameQueryOpcode(Player* p);
+ void SendNameQueryOpcodeFromDB(uint64 guid);
+ static void SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId);
+
+ void SendTrainerList( uint64 guid );
+ void SendTrainerList( uint64 guid,std::string strTitle );
+ void SendListInventory( uint64 guid );
+ void SendShowBank( uint64 guid );
+ void SendTabardVendorActivate( uint64 guid );
+ void SendSpiritResurrect();
+ void SendBindPoint(Creature* npc);
+ void SendGMTicketGetTicket(uint32 status, char const* text);
+
+ void SendAttackStop(Unit const* enemy);
+
+ void SendBattlegGroundList( uint64 guid, uint32 bgTypeId );
+
+ void SendTradeStatus(uint32 status);
+ void SendCancelTrade();
+
+ void SendStablePet(uint64 guid );
+ void SendPetitionQueryOpcode( uint64 petitionguid);
+ void SendUpdateTrade();
+
+ //pet
+ void SendPetNameQuery(uint64 guid, uint32 petnumber);
+
+ //mail
+ //used with item_page table
+ bool SendItemInfo( uint32 itemid, WorldPacket data );
+ static void SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, std::string subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint32 COD, uint16 mailTemplateId = 0);
+ static void SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 received_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay = 0, uint16 mailTemplateId = 0);
+
+ //auction
+ void SendAuctionHello( uint64 guid, Creature * unit );
+ void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0);
+ void SendAuctionBidderNotification( uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template);
+ void SendAuctionOwnerNotification( AuctionEntry * auction );
+ bool SendAuctionInfo(WorldPacket & data, AuctionEntry* auction);
+ void SendAuctionOutbiddedMail( AuctionEntry * auction, uint32 newPrice );
+ void SendAuctionCancelledToBidderMail( AuctionEntry* auction );
+
+ //Item Enchantment
+ void SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID);
+ void SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration);
+
+ //Taxi
+ void SendTaxiStatus( uint64 guid );
+ void SendTaxiMenu( Creature* unit );
+ void SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode = 0 );
+ bool SendLearnNewTaxiNode( Creature* unit );
+
+ // Guild/Arena Team
+ void SendGuildCommandResult(uint32 typecmd,std::string str,uint32 cmdresult);
+ void SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3);
+ void BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3);
+ void SendNotInArenaTeamPacket(uint8 type);
+ void SendPetitionShowList( uint64 guid );
+ void SendSaveGuildEmblem( uint32 msg );
+
+ // Looking For Group
+ // TRUE values set by client sending CMSG_LFG_SET_AUTOJOIN and CMSG_LFM_CLEAR_AUTOFILL before player login
+ bool LookingForGroup_auto_join;
+ bool LookingForGroup_auto_add;
+
+ void BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data);
+
+ void DoLootRelease( uint64 lguid );
+
+ // Account mute time
+ time_t m_muteTime;
+
+ // Locales
+ LocaleConstant GetSessionDbcLocale() { return m_sessionDbcLocale; }
+ int GetSessionDbLocaleIndex() { return m_sessionDbLocaleIndex; }
+ const char *GetMangosString(int32 entry);
+
+ uint32 GetLatency() const { return m_latency; }
+ void SetLatency(uint32 latency) { m_latency = latency; }
+ uint32 getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus);
+
+ public: // opcodes handlers
+
+ void Handle_NULL(WorldPacket& recvPacket); // not used
+ void Handle_EarlyProccess( WorldPacket& recvPacket);// just mark packets processed in WorldSocket::OnRead
+ void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client
+ void Handle_Depricated(WorldPacket& recvPacket); // never used anymore by client
+
+ void HandleCharEnumOpcode(WorldPacket& recvPacket);
+ void HandleCharDeleteOpcode(WorldPacket& recvPacket);
+ void HandleCharCreateOpcode(WorldPacket& recvPacket);
+ void HandlePlayerLoginOpcode(WorldPacket& recvPacket);
+ void HandleCharEnum(QueryResult * result);
+ void HandlePlayerLogin(LoginQueryHolder * holder);
+
+ // played time
+ void HandlePlayedTime(WorldPacket& recvPacket);
+
+ // new
+ void HandleMoveUnRootAck(WorldPacket& recvPacket);
+ void HandleMoveRootAck(WorldPacket& recvPacket);
+ void HandleLookingForGroup(WorldPacket& recvPacket);
+
+ // new inspect
+ void HandleInspectOpcode(WorldPacket& recvPacket);
+
+ // new party stats
+ void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket);
+
+ void HandleMoveWaterWalkAck(WorldPacket& recvPacket);
+ void HandleFeatherFallAck(WorldPacket &recv_data);
+
+ void HandleMoveHoverAck( WorldPacket & recv_data );
+
+ void HandleMountSpecialAnimOpcode(WorldPacket &recvdata);
+
+ // character view
+ void HandleToggleHelmOpcode(WorldPacket& recv_data);
+ void HandleToggleCloakOpcode(WorldPacket& recv_data);
+
+ // repair
+ void HandleRepairItemOpcode(WorldPacket& recvPacket);
+
+ // Knockback
+ void HandleMoveKnockBackAck(WorldPacket& recvPacket);
+
+ void HandleMoveTeleportAck(WorldPacket& recvPacket);
+ void HandleForceSpeedChangeAck( WorldPacket & recv_data );
+
+ void HandlePingOpcode(WorldPacket& recvPacket);
+ void HandleAuthSessionOpcode(WorldPacket& recvPacket);
+ void HandleRepopRequestOpcode(WorldPacket& recvPacket);
+ void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket);
+ void HandleLootMoneyOpcode(WorldPacket& recvPacket);
+ void HandleLootOpcode(WorldPacket& recvPacket);
+ void HandleLootReleaseOpcode(WorldPacket& recvPacket);
+ void HandleLootMasterGiveOpcode(WorldPacket& recvPacket);
+ void HandleWhoOpcode(WorldPacket& recvPacket);
+ void HandleLogoutRequestOpcode(WorldPacket& recvPacket);
+ void HandlePlayerLogoutOpcode(WorldPacket& recvPacket);
+ void HandleLogoutCancelOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketCreateOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket);
+
+ void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketUpdateTextOpcode(WorldPacket& recvPacket);
+
+ void HandleGMSurveySubmit(WorldPacket& recvPacket);
+
+ void HandleTogglePvP(WorldPacket& recvPacket);
+
+ void HandleZoneUpdateOpcode(WorldPacket& recvPacket);
+ void HandleSetTargetOpcode(WorldPacket& recvPacket);
+ void HandleSetSelectionOpcode(WorldPacket& recvPacket);
+ void HandleStandStateChangeOpcode(WorldPacket& recvPacket);
+ void HandleEmoteOpcode(WorldPacket& recvPacket);
+ void HandleFriendListOpcode(WorldPacket& recvPacket);
+ void HandleAddFriendOpcode(WorldPacket& recvPacket);
+ void HandleDelFriendOpcode(WorldPacket& recvPacket);
+ void HandleAddIgnoreOpcode(WorldPacket& recvPacket);
+ void HandleDelIgnoreOpcode(WorldPacket& recvPacket);
+ void HandleSetFriendNoteOpcode(WorldPacket& recvPacket);
+ void HandleBugOpcode(WorldPacket& recvPacket);
+ void HandleSetAmmoOpcode(WorldPacket& recvPacket);
+ void HandleItemNameQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleAreaTriggerOpcode(WorldPacket& recvPacket);
+
+ void HandleSetFactionAtWar( WorldPacket & recv_data );
+ void HandleSetFactionCheat( WorldPacket & recv_data );
+ void HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data);
+ void HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data);
+
+ void HandleUpdateAccountData(WorldPacket& recvPacket);
+ void HandleRequestAccountData(WorldPacket& recvPacket);
+ void HandleSetActionButtonOpcode(WorldPacket& recvPacket);
+
+ void HandleGameObjectUseOpcode(WorldPacket& recPacket);
+ void HandleMeetingStoneInfo(WorldPacket& recPacket);
+
+ void HandleNameQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleQueryTimeOpcode(WorldPacket& recvPacket);
+
+ void HandleCreatureQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleGameObjectQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket);
+ void HandleMoveWorldportAckOpcode(); // for server-side calls
+
+ void HandleMovementOpcodes(WorldPacket& recvPacket);
+ void HandleSetActiveMoverOpcode(WorldPacket &recv_data);
+ void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data);
+
+ void HandleRequestRaidInfoOpcode( WorldPacket & recv_data );
+
+ void HandleBattlefieldStatusOpcode(WorldPacket &recv_data);
+ void HandleBattleMasterHelloOpcode(WorldPacket &recv_data);
+
+ void HandleGroupInviteOpcode(WorldPacket& recvPacket);
+ //void HandleGroupCancelOpcode(WorldPacket& recvPacket);
+ void HandleGroupAcceptOpcode(WorldPacket& recvPacket);
+ void HandleGroupDeclineOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninviteNameOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninviteGuidOpcode(WorldPacket& recvPacket);
+ void HandleGroupUninvite(uint64 guid, std::string name);
+ void HandleGroupSetLeaderOpcode(WorldPacket& recvPacket);
+ void HandleGroupLeaveOpcode(WorldPacket& recvPacket);
+ void HandleGroupPassOnLootOpcode( WorldPacket &recv_data );
+ void HandleLootMethodOpcode(WorldPacket& recvPacket);
+ void HandleLootRoll( WorldPacket &recv_data );
+ void HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data );
+ void HandleRaidIconTargetOpcode( WorldPacket & recv_data );
+ void HandleRaidReadyCheckOpcode( WorldPacket & recv_data );
+ void HandleRaidReadyCheckFinishOpcode( WorldPacket & recv_data );
+ void HandleRaidConvertOpcode( WorldPacket & recv_data );
+ void HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data );
+ void HandleGroupAssistantOpcode( WorldPacket & recv_data );
+ void HandleGroupPromoteOpcode( WorldPacket & recv_data );
+
+ void HandlePetitionBuyOpcode(WorldPacket& recv_data);
+ void HandlePetitionShowSignOpcode(WorldPacket& recv_data);
+ void HandlePetitionQueryOpcode(WorldPacket& recv_data);
+ void HandlePetitionRenameOpcode(WorldPacket& recv_data);
+ void HandlePetitionSignOpcode(WorldPacket& recv_data);
+ void HandlePetitionDeclineOpcode(WorldPacket& recv_data);
+ void HandleOfferPetitionOpcode(WorldPacket& recv_data);
+ void HandleTurnInPetitionOpcode(WorldPacket& recv_data);
+
+ void HandleGuildQueryOpcode(WorldPacket& recvPacket);
+ void HandleGuildCreateOpcode(WorldPacket& recvPacket);
+ void HandleGuildInviteOpcode(WorldPacket& recvPacket);
+ void HandleGuildRemoveOpcode(WorldPacket& recvPacket);
+ void HandleGuildAcceptOpcode(WorldPacket& recvPacket);
+ void HandleGuildDeclineOpcode(WorldPacket& recvPacket);
+ void HandleGuildInfoOpcode(WorldPacket& recvPacket);
+ void HandleGuildEventLogOpcode(WorldPacket& recvPacket);
+ void HandleGuildRosterOpcode(WorldPacket& recvPacket);
+ void HandleGuildPromoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildDemoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildLeaveOpcode(WorldPacket& recvPacket);
+ void HandleGuildDisbandOpcode(WorldPacket& recvPacket);
+ void HandleGuildLeaderOpcode(WorldPacket& recvPacket);
+ void HandleGuildMOTDOpcode(WorldPacket& recvPacket);
+ void HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket);
+ void HandleGuildRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildAddRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildDelRankOpcode(WorldPacket& recvPacket);
+ void HandleGuildChangeInfoOpcode(WorldPacket& recvPacket);
+ void HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket);
+
+ void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket);
+ void HandleTaxiQueryAvailableNodesOpcode(WorldPacket& recvPacket);
+ void HandleActivateTaxiOpcode(WorldPacket& recvPacket);
+ void HandleActivateTaxiFarOpcode(WorldPacket& recvPacket);
+ void HandleTaxiNextDestinationOpcode(WorldPacket& recvPacket);
+
+ void HandleTabardVendorActivateOpcode(WorldPacket& recvPacket);
+ void HandleBankerActivateOpcode(WorldPacket& recvPacket);
+ void HandleBuyBankSlotOpcode(WorldPacket& recvPacket);
+ void HandleTrainerListOpcode(WorldPacket& recvPacket);
+ void HandleTrainerBuySpellOpcode(WorldPacket& recvPacket);
+ void HandlePetitionShowListOpcode(WorldPacket& recvPacket);
+ void HandleGossipHelloOpcode(WorldPacket& recvPacket);
+ void HandleGossipSelectOptionOpcode(WorldPacket& recvPacket);
+ void HandleSpiritHealerActivateOpcode(WorldPacket& recvPacket);
+ void HandleNpcTextQueryOpcode(WorldPacket& recvPacket);
+ void HandleBinderActivateOpcode(WorldPacket& recvPacket);
+ void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
+ void HandleStablePet(WorldPacket& recvPacket);
+ void HandleUnstablePet(WorldPacket& recvPacket);
+ void HandleBuyStableSlot(WorldPacket& recvPacket);
+ void HandleStableRevivePet(WorldPacket& recvPacket);
+ void HandleStableSwapPet(WorldPacket& recvPacket);
+
+ void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
+ void HandleDuelCancelledOpcode(WorldPacket& recvPacket);
+
+ void HandleAcceptTradeOpcode(WorldPacket& recvPacket);
+ void HandleBeginTradeOpcode(WorldPacket& recvPacket);
+ void HandleBusyTradeOpcode(WorldPacket& recvPacket);
+ void HandleCancelTradeOpcode(WorldPacket& recvPacket);
+ void HandleClearTradeItemOpcode(WorldPacket& recvPacket);
+ void HandleIgnoreTradeOpcode(WorldPacket& recvPacket);
+ void HandleInitiateTradeOpcode(WorldPacket& recvPacket);
+ void HandleSetTradeGoldOpcode(WorldPacket& recvPacket);
+ void HandleSetTradeItemOpcode(WorldPacket& recvPacket);
+ void HandleUnacceptTradeOpcode(WorldPacket& recvPacket);
+
+ void HandleAuctionHelloOpcode(WorldPacket& recvPacket);
+ void HandleAuctionListItems( WorldPacket & recv_data );
+ void HandleAuctionListBidderItems( WorldPacket & recv_data );
+ void HandleAuctionSellItem( WorldPacket & recv_data );
+ void HandleAuctionRemoveItem( WorldPacket & recv_data );
+ void HandleAuctionListOwnerItems( WorldPacket & recv_data );
+ void HandleAuctionPlaceBid( WorldPacket & recv_data );
+
+ void HandleGetMail( WorldPacket & recv_data );
+ void HandleSendMail( WorldPacket & recv_data );
+ void HandleTakeMoney( WorldPacket & recv_data );
+ void HandleTakeItem( WorldPacket & recv_data );
+ void HandleMarkAsRead( WorldPacket & recv_data );
+ void HandleReturnToSender( WorldPacket & recv_data );
+ void HandleMailDelete( WorldPacket & recv_data );
+ void HandleItemTextQuery( WorldPacket & recv_data);
+ void HandleMailCreateTextItem(WorldPacket & recv_data );
+ void HandleMsgQueryNextMailtime(WorldPacket & recv_data );
+ void HandleCancelChanneling(WorldPacket & recv_data );
+
+ void SendItemPageInfo( ItemPrototype *itemProto );
+ void HandleSplitItemOpcode(WorldPacket& recvPacket);
+ void HandleSwapInvItemOpcode(WorldPacket& recvPacket);
+ void HandleDestroyItemOpcode(WorldPacket& recvPacket);
+ void HandleAutoEquipItemOpcode(WorldPacket& recvPacket);
+ void HandleItemQuerySingleOpcode(WorldPacket& recvPacket);
+ void HandleSellItemOpcode(WorldPacket& recvPacket);
+ void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket);
+ void HandleBuyItemOpcode(WorldPacket& recvPacket);
+ void HandleListInventoryOpcode(WorldPacket& recvPacket);
+ void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket);
+ void HandleReadItem(WorldPacket& recvPacket);
+ void HandleAutoEquipItemSlotOpcode(WorldPacket & recvPacket);
+ void HandleSwapItem( WorldPacket & recvPacket);
+ void HandleBuybackItem(WorldPacket & recvPacket);
+ void HandleAutoBankItemOpcode(WorldPacket& recvPacket);
+ void HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket);
+ void HandleWrapItemOpcode(WorldPacket& recvPacket);
+
+ void HandleAttackSwingOpcode(WorldPacket& recvPacket);
+ void HandleAttackStopOpcode(WorldPacket& recvPacket);
+ void HandleSetSheathedOpcode(WorldPacket& recvPacket);
+
+ void HandleUseItemOpcode(WorldPacket& recvPacket);
+ void HandleOpenItemOpcode(WorldPacket& recvPacket);
+ void HandleCastSpellOpcode(WorldPacket& recvPacket);
+ void HandleCancelCastOpcode(WorldPacket& recvPacket);
+ void HandleCancelAuraOpcode(WorldPacket& recvPacket);
+ void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
+ void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket);
+
+ void HandleLearnTalentOpcode(WorldPacket& recvPacket);
+ void HandleTalentWipeOpcode(WorldPacket& recvPacket);
+ void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
+
+ void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverHelloOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverQuestQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverChooseRewardOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket);
+ void HandleQuestQueryOpcode(WorldPacket& recvPacket);
+ void HandleQuestgiverCancel(WorldPacket& recv_data );
+ void HandleQuestLogSwapQuest(WorldPacket& recv_data );
+ void HandleQuestLogRemoveQuest(WorldPacket& recv_data);
+ void HandleQuestConfirmAccept(WorldPacket& recv_data);
+ void HandleQuestComplete(WorldPacket& recv_data);
+ void HandleQuestAutoLaunch(WorldPacket& recvPacket);
+ void HandleQuestPushToParty(WorldPacket& recvPacket);
+ void HandleQuestPushResult(WorldPacket& recvPacket);
+
+ void HandleMessagechatOpcode(WorldPacket& recvPacket);
+ void HandleTextEmoteOpcode(WorldPacket& recvPacket);
+ void HandleChatIgnoredOpcode(WorldPacket& recvPacket);
+
+ void HandleCorpseReclaimOpcode( WorldPacket& recvPacket );
+ void HandleCorpseQueryOpcode( WorldPacket& recvPacket );
+ void HandleResurrectResponseOpcode(WorldPacket& recvPacket);
+ void HandleSummonResponseOpcode(WorldPacket& recv_data);
+
+ void HandleChannelJoin(WorldPacket& recvPacket);
+ void HandleChannelLeave(WorldPacket& recvPacket);
+ void HandleChannelList(WorldPacket& recvPacket);
+ void HandleChannelPassword(WorldPacket& recvPacket);
+ void HandleChannelSetOwner(WorldPacket& recvPacket);
+ void HandleChannelOwner(WorldPacket& recvPacket);
+ void HandleChannelModerator(WorldPacket& recvPacket);
+ void HandleChannelUnmoderator(WorldPacket& recvPacket);
+ void HandleChannelMute(WorldPacket& recvPacket);
+ void HandleChannelUnmute(WorldPacket& recvPacket);
+ void HandleChannelInvite(WorldPacket& recvPacket);
+ void HandleChannelKick(WorldPacket& recvPacket);
+ void HandleChannelBan(WorldPacket& recvPacket);
+ void HandleChannelUnban(WorldPacket& recvPacket);
+ void HandleChannelAnnounce(WorldPacket& recvPacket);
+ void HandleChannelModerate(WorldPacket& recvPacket);
+ void HandleChannelRosterQuery(WorldPacket& recvPacket);
+ void HandleChannelInfoQuery(WorldPacket& recvPacket);
+ void HandleChannelJoinNotify(WorldPacket& recvPacket);
+
+ void HandleCompleteCinema(WorldPacket& recvPacket);
+ void HandleNextCinematicCamera(WorldPacket& recvPacket);
+
+ void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket);
+ void HandlePageQueryOpcode(WorldPacket& recvPacket);
+
+ void HandleTutorialFlag ( WorldPacket & recv_data );
+ void HandleTutorialClear( WorldPacket & recv_data );
+ void HandleTutorialReset( WorldPacket & recv_data );
+
+ //Pet
+ void HandlePetAction( WorldPacket & recv_data );
+ void HandlePetNameQuery( WorldPacket & recv_data );
+ void HandlePetSetAction( WorldPacket & recv_data );
+ void HandlePetAbandon( WorldPacket & recv_data );
+ void HandlePetRename( WorldPacket & recv_data );
+ void HandlePetCancelAuraOpcode( WorldPacket& recvPacket );
+ void HandlePetUnlearnOpcode( WorldPacket& recvPacket );
+ void HandlePetSpellAutocastOpcode( WorldPacket& recvPacket );
+ void HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket );
+
+ void HandleSetActionBar(WorldPacket& recv_data);
+
+ void HandleChangePlayerNameOpcode(WorldPacket& recv_data);
+ void HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data);
+
+ void HandleTotemDestroy(WorldPacket& recv_data);
+
+ //BattleGround
+ void HandleBattleGroundHelloOpcode(WorldPacket &recv_data);
+ void HandleBattleGroundJoinOpcode(WorldPacket &recv_data);
+ void HandleBattleGroundPlayerPositionsOpcode(WorldPacket& recv_data);
+ void HandleBattleGroundPVPlogdataOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundListOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundLeaveOpcode( WorldPacket &recv_data );
+ void HandleBattleGroundArenaJoin( WorldPacket &recv_data );
+ void HandleBattleGroundReportAFK( WorldPacket &recv_data );
+
+ void HandleWardenDataOpcode(WorldPacket& recv_data);
+ void HandleWorldTeleportOpcode(WorldPacket& recv_data);
+ void HandleMinimapPingOpcode(WorldPacket& recv_data);
+ void HandleRandomRollOpcode(WorldPacket& recv_data);
+ void HandleFarSightOpcode(WorldPacket& recv_data);
+ void HandleSetLfgOpcode(WorldPacket& recv_data);
+ void HandleDungeonDifficultyOpcode(WorldPacket& recv_data);
+ void HandleMoveFlyModeChangeAckOpcode(WorldPacket& recv_data);
+ void HandleLfgAutoJoinOpcode(WorldPacket& recv_data);
+ void HandleLfgCancelAutoJoinOpcode(WorldPacket& recv_data);
+ void HandleLfmAutoAddMembersOpcode(WorldPacket& recv_data);
+ void HandleLfmCancelAutoAddmembersOpcode(WorldPacket& recv_data);
+ void HandleLfgClearOpcode(WorldPacket& recv_data);
+ void HandleLfmSetNoneOpcode(WorldPacket& recv_data);
+ void HandleLfmSetOpcode(WorldPacket& recv_data);
+ void HandleLfgSetCommentOpcode(WorldPacket& recv_data);
+ void HandleNewUnknownOpcode(WorldPacket& recv_data);
+ void HandleChooseTitleOpcode(WorldPacket& recv_data);
+ void HandleRealmStateRequestOpcode(WorldPacket& recv_data);
+ void HandleAllowMoveAckOpcode(WorldPacket& recv_data);
+ void HandleWhoisOpcode(WorldPacket& recv_data);
+ void HandleResetInstancesOpcode(WorldPacket& recv_data);
+
+ // Arena Team
+ void HandleInspectArenaStatsOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamQueryOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamRosterOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamAddMemberOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamInviteAcceptOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamInviteDeclineOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamLeaveOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamRemoveFromTeamOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamDisbandOpcode(WorldPacket& recv_data);
+ void HandleArenaTeamPromoteToCaptainOpcode(WorldPacket& recv_data);
+
+ void HandleAreaSpiritHealerQueryOpcode(WorldPacket& recv_data);
+ void HandleAreaSpiritHealerQueueOpcode(WorldPacket& recv_data);
+ void HandleDismountOpcode(WorldPacket& recv_data);
+ void HandleSelfResOpcode(WorldPacket& recv_data);
+ void HandleReportSpamOpcode(WorldPacket& recv_data);
+ void HandleRequestPetInfoOpcode(WorldPacket& recv_data);
+
+ // Socket gem
+ void HandleSocketOpcode(WorldPacket& recv_data);
+
+ void HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data);
+
+ void HandleChannelEnableVoiceOpcode(WorldPacket & recv_data);
+ void HandleVoiceSettingsOpcode(WorldPacket& recv_data);
+ void HandleChannelVoiceChatQuery(WorldPacket& recv_data);
+ void HandleSetTaxiBenchmarkOpcode(WorldPacket& recv_data);
+
+ // Guild Bank
+ void HandleGuildBankGetRights(WorldPacket& recv_data);
+ void HandleGuildBankGetMoneyAmount(WorldPacket& recv_data);
+ void HandleGuildBankQuery(WorldPacket& recv_data);
+ void HandleGuildBankTabColon(WorldPacket& recv_data);
+ void HandleGuildBankLog(WorldPacket& recv_data);
+ void HandleGuildBankDeposit(WorldPacket& recv_data);
+ void HandleGuildBankWithdraw(WorldPacket& recv_data);
+ void HandleGuildBankDepositItem(WorldPacket& recv_data);
+ void HandleGuildBankModifyTab(WorldPacket& recv_data);
+ void HandleGuildBankBuyTab(WorldPacket& recv_data);
+ void HandleGuildBankTabText(WorldPacket& recv_data);
+ void HandleGuildBankSetTabText(WorldPacket& recv_data);
+ private:
+ // private trade methods
+ void moveItems(Item* myItems[], Item* hisItems[]);
+
+ // logging helper
+ void logUnexpectedOpcode(WorldPacket *packet, const char * reason);
+ Player *_player;
+ WorldSocket *_socket;
+
+ uint32 _security;
+ uint32 _accountId;
+ bool m_isTBC;
+
+ time_t _logoutTime;
+ bool m_playerLoading; // code processed in LoginPlayer
+ bool m_playerLogout; // code processed in LogoutPlayer
+ bool m_playerRecentlyLogout;
+ LocaleConstant m_sessionDbcLocale;
+ int m_sessionDbLocaleIndex;
+ uint32 m_latency;
+
+ ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> _recvQueue;
+};
+#endif
+/// @}
diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp
new file mode 100644
index 00000000000..d52c0962862
--- /dev/null
+++ b/src/game/WorldSocket.cpp
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "Common.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Database/DatabaseEnv.h"
+#include "Auth/Sha1.h"
+#include "WorldPacket.h"
+#include "WorldSocket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "WorldSocketMgr.h"
+#include "Policies/SingletonImp.h"
+#include "WorldLog.h"
+#include "AddonHandler.h"
+#include "sockets/Utility.h"
+#include "Util.h"
+
+// 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
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+/// Client Packet Header
+struct ClientPktHeader
+{
+ uint16 size;
+ uint32 cmd;
+};
+
+/// Server Packet Header
+struct ServerPktHeader
+{
+ uint16 size;
+ uint16 cmd;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+#define SOCKET_CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S));
+
+/// WorldSocket construction and initialization.
+WorldSocket::WorldSocket(ISocketHandler &sh): TcpSocket(sh), _cmd(0), _remaining(0), _session(NULL)
+{
+ _seed = static_cast<uint32>(rand32());
+ m_LastPingMSTime = 0; // first time it will counted as overspeed maybe, but this is not important
+ m_OverSpeedPings = 0;
+
+ if (sWorld.getConfig(CONFIG_TCP_NO_DELAY))
+ SetTcpNodelay(true);
+}
+
+/// WorldSocket destructor
+WorldSocket::~WorldSocket()
+{
+ if(_session)
+ _session->SetSocket(0);
+
+ WorldPacket *packet;
+
+ ///- Go through the to-be-sent queue and delete remaining packets
+ while(!_sendQueue.empty())
+ {
+ packet = _sendQueue.next();
+ delete packet;
+ }
+}
+
+/// Copy the packet to the to-be-sent queue
+void WorldSocket::SendPacket(WorldPacket const* packet)
+{
+ WorldPacket *pck = new WorldPacket(*packet);
+ ASSERT(pck);
+ _sendQueue.add(pck);
+}
+
+/// On client connection
+void WorldSocket::OnAccept()
+{
+ ///- Add the current socket to the list of sockets to be managed (WorldSocketMgr)
+ sWorldSocketMgr.AddSocket(this);
+ Utility::ResolveLocal();
+
+ ///- Send a AUTH_CHALLENGE packet
+ WorldPacket packet( SMSG_AUTH_CHALLENGE, 4 );
+ packet << _seed;
+
+ SendPacket(&packet);
+}
+
+/// Read the client transmitted data
+void WorldSocket::OnRead()
+{
+ TcpSocket::OnRead();
+
+ while(1)
+ {
+ ///- Read the packet header and decipher it (if needed)
+ if (!_remaining)
+ {
+ if (ibuf.GetLength() < 6)
+ break;
+
+ ClientPktHeader hdr;
+
+ ibuf.Read((char *)&hdr, 6);
+ _crypt.DecryptRecv((uint8 *)&hdr, 6);
+
+ _remaining = ntohs(hdr.size) - 4;
+ _cmd = hdr.cmd;
+ }
+
+ if (ibuf.GetLength() < _remaining)
+ break;
+
+ ///- Read the remaining of the packet
+ WorldPacket packet((uint16)_cmd, _remaining);
+
+ packet.resize(_remaining);
+ if(_remaining) ibuf.Read((char*)packet.contents(), _remaining);
+ _remaining = 0;
+
+ ///- If log of world packets is enable, log the incoming packet
+ if( sWorldLog.LogWorld() )
+ {
+ sWorldLog.Log("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
+ (uint32)GetSocket(),
+ packet.size(),
+ LookupOpcodeName(packet.GetOpcode()),
+ packet.GetOpcode());
+
+ uint32 p = 0;
+ while (p < packet.size())
+ {
+ for (uint32 j = 0; j < 16 && p < packet.size(); j++)
+ sWorldLog.Log("%.2X ", packet[p++]);
+ sWorldLog.Log("\n");
+ }
+ sWorldLog.Log("\n\n");
+ }
+
+ ///- If the packet is PING, KEEP_ALIVE or AUTH_SESSION, handle immediately
+ switch (_cmd)
+ {
+ case CMSG_KEEP_ALIVE:
+ break; // just ignore, network connectivity timeout preventing
+ case CMSG_PING:
+ {
+ _HandlePing(packet);
+ break;
+ }
+ case CMSG_AUTH_SESSION:
+ {
+ _HandleAuthSession(packet);
+ break;
+ }
+ default:
+ {
+ ///- Else, put it in the world session queue for this user (need to be already authenticated)
+ if (_session)
+ _session->QueuePacket(packet);
+ else
+ sLog.outDetail("Received out of place packet with cmdid 0x%.4X", _cmd);
+ break;
+ }
+ }
+ }
+}
+
+/// On socket closing
+void WorldSocket::CloseSocket()
+{
+ ///- Set CloseAndDelete flag for TcpSocket class
+ SetCloseAndDelete(true);
+
+ ///- Set _session to NULL. Prevent crashes
+ _session = NULL;
+}
+
+/// On socket deleting
+void WorldSocket::OnDelete()
+{
+ ///- Stop sending remaining data through this socket
+ if (_session)
+ {
+ _session->SetSocket(NULL);
+ // Session deleted from World session list at socket==0, This is only back reference from socket to session.
+ _session = NULL;
+ }
+
+ ///- Remove the socket from the WorldSocketMgr list
+ sWorldSocketMgr.RemoveSocket(this);
+
+ ///- Removes socket from player queue
+ sWorld.RemoveQueuedPlayer(this);
+}
+
+/// Handle the client authentication packet
+void WorldSocket::_HandleAuthSession(WorldPacket& recvPacket)
+{
+ uint8 digest[20];
+ uint32 clientSeed;
+ uint32 unk2;
+ uint32 BuiltNumberClient;
+ uint32 id, security;
+ bool tbc = false;
+ std::string account;
+ Sha1Hash sha1;
+ BigNumber v, s, g, N, x, I;
+ WorldPacket packet, SendAddonPacked;
+
+ BigNumber K;
+
+ SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+1+4+20);
+
+ ///- Read the content of the packet
+ recvPacket >> BuiltNumberClient; // for now no use
+ recvPacket >> unk2;
+ recvPacket >> account;
+
+ // recheck size
+ SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+(account.size()+1)+4+20);
+
+ recvPacket >> clientSeed;
+ recvPacket.read(digest, 20);
+
+ sLog.outDebug("Auth: client %u, unk2 %u, account %s, clientseed %u", BuiltNumberClient, unk2, account.c_str(), clientSeed);
+
+ ///- Normalize account name
+ //utf8ToUpperOnlyLatin(account); -- client already send account in expected form
+
+ ///- 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);
+ //No SQL injection, username escaped.
+ // 0 1 2 3 4 5 6 7 8 9 10
+ QueryResult *result = loginDatabase.PQuery("SELECT id, gmlevel, sessionkey, last_ip, locked, sha_pass_hash, v, s, tbc, mutetime, locale FROM account WHERE username = '%s'", safe_account.c_str());
+
+ ///- Stop if the account is not found
+ if ( !result )
+ {
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_UNKNOWN_ACCOUNT );
+ SendPacket( &packet );
+ sLog.outDetail( "SOCKET: Sent Auth Response (unknown account)." );
+ return;
+ }
+
+ Field* fields = result->Fetch();
+
+ tbc = fields[8].GetUInt8() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+
+ N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
+ g.SetDword(7);
+ I.SetHexStr(fields[5].GetString());
+
+ //In case of leading zeros in the I hash, restore them
+ uint8 mDigest[SHA_DIGEST_LENGTH];
+ memset(mDigest,0,SHA_DIGEST_LENGTH);
+ if (I.GetNumBytes() <= SHA_DIGEST_LENGTH)
+ memcpy(mDigest,I.AsByteArray(),I.GetNumBytes());
+
+ std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH);
+
+ s.SetHexStr(fields[7].GetString());
+ sha1.UpdateData(s.AsByteArray(), s.GetNumBytes());
+ sha1.UpdateData(mDigest, SHA_DIGEST_LENGTH);
+ sha1.Finalize();
+ x.SetBinary(sha1.GetDigest(), sha1.GetLength());
+ v = g.ModExp(x, N);
+
+ const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free()
+ const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free()
+ const char* vold = fields[6].GetString();
+ sLog.outDebug("SOCKET: (s,v) check s: %s v_old: %s v_new: %s", sStr, vold, vStr );
+ loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE username = '%s'", safe_account.c_str());
+ if ( !vold || strcmp( vStr, vold ) )
+ {
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_UNKNOWN_ACCOUNT );
+ SendPacket( &packet );
+ sLog.outDetail( "SOCKET: User not logged.");
+ delete result;
+ OPENSSL_free((void*)sStr);
+ OPENSSL_free((void*)vStr);
+ return;
+ }
+ OPENSSL_free((void*)sStr);
+ OPENSSL_free((void*)vStr);
+
+ ///- Re-check ip locking (same check as in realmd).
+ if(fields[4].GetUInt8() == 1) // if ip is locked
+ {
+ if ( strcmp(fields[3].GetString(),GetRemoteAddress().c_str()) )
+ {
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_FAILED );
+ SendPacket( &packet );
+
+ sLog.outDetail( "SOCKET: Sent Auth Response (Account IP differs)." );
+ delete result;
+ return;
+ }
+ }
+
+ id = fields[0].GetUInt32();
+ security = fields[1].GetUInt16();
+ K.SetHexStr(fields[2].GetString());
+ time_t mutetime = time_t(fields[9].GetUInt64());
+
+ LocaleConstant locale = LocaleConstant(fields[10].GetUInt8());
+ if (locale>=MAX_LOCALE)
+ locale=LOCALE_enUS;
+
+ delete result;
+
+ ///- Re-check account ban (same check as in realmd) /// TO DO: why on earth do 2 checks for same thing?
+ QueryResult *banresult = loginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = '%u' AND active = 1", id);
+ if(banresult) // if account banned
+ {
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_BANNED );
+ SendPacket( &packet );
+
+ sLog.outDetail( "SOCKET: Sent Auth Response (Account banned)." );
+ delete banresult;
+ return;
+ }
+
+ ///- Check locked state for server
+ AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
+ if( allowedAccountType > SEC_PLAYER && security < allowedAccountType)
+ {
+ WorldPacket Packet(SMSG_AUTH_RESPONSE, 1);
+ Packet << uint8(AUTH_UNAVAILABLE);
+ SendPacket(&Packet);
+ return;
+ }
+
+ ///- kick already loaded player with same account (if any) and remove session
+ ///- if player is in loading and want to load again, return
+ if(!sWorld.RemoveSession(id))
+ {
+ return;
+ }
+
+ ///- Check that Key and account name are the same on client and server
+ Sha1Hash sha;
+
+ uint32 t = 0;
+ uint32 seed = _seed;
+
+ sha.UpdateData(account);
+ sha.UpdateData((uint8 *)&t, 4);
+ sha.UpdateData((uint8 *)&clientSeed, 4);
+ sha.UpdateData((uint8 *)&seed, 4);
+ sha.UpdateBigNumbers(&K, NULL);
+ sha.Finalize();
+
+ if (memcmp(sha.GetDigest(), digest, 20))
+ {
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_FAILED );
+ SendPacket( &packet );
+
+ sLog.outDetail( "SOCKET: Sent Auth Response (authentication failed)." );
+ return;
+ }
+
+ ///- Initialize the encryption with the Key
+ _crypt.SetKey(&K);
+ _crypt.Init();
+
+ ///- Send 'Auth is ok'
+ packet.Initialize( SMSG_AUTH_RESPONSE, 1+4+1+4+1 );
+ packet << uint8( AUTH_OK );
+ packet << uint32(0); // unknown random value...
+ packet << uint8(0); // can be 0 and 2
+ packet << uint32(0); // const 0
+ packet << uint8(tbc ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
+ SendPacket(&packet);
+
+ ///- Create a new WorldSession for the player and add it to the World
+ _session = new WorldSession(id, this,security,tbc,mutetime,locale);
+ sWorld.AddSession(_session);
+
+ if(sLog.IsOutDebug()) // optimize disabled debug output
+ {
+ sLog.outDebug( "SOCKET: Client '%s' authenticated successfully.", account.c_str() );
+ sLog.outDebug( "Account: '%s' Logged in from IP %s.", account.c_str(), GetRemoteAddress().c_str());
+ }
+
+ ///- Update the last_ip in the database
+ //No SQL injection, username escaped.
+ std::string address = GetRemoteAddress();
+ loginDatabase.escape_string(address);
+ loginDatabase.PExecute("UPDATE account SET last_ip = '%s' WHERE username = '%s'",address.c_str(), safe_account.c_str());
+
+ // do small delay (10ms) at accepting successful authed connection to prevent dropping packets by client
+ // don't must harm anyone (let login ~100 accounts in 1 sec ;) )
+ #ifdef WIN32
+ Sleep(10);
+ #else
+ ZThread::Thread::sleep(10);
+ #endif
+
+ ///- Check that we do not exceed the maximum number of online players in the realm
+ uint32 Sessions = sWorld.GetActiveAndQueuedSessionCount();
+ uint32 pLimit = sWorld.GetPlayerAmountLimit();
+ uint32 QueueSize = sWorld.GetQueueSize(); //number of players in the queue
+ bool inQueue = false;
+ --Sessions; //so we don't count the user trying to login as a session and queue the socket that we are using
+
+ if( pLimit > 0 && Sessions >= pLimit && security == SEC_PLAYER )
+ {
+ sWorld.AddQueuedPlayer(this);
+ SendAuthWaitQue(sWorld.GetQueuePos(this));
+ sWorld.UpdateMaxSessionCounters();
+ sLog.outDetail( "PlayerQueue: %s is in Queue Position (%u).",safe_account.c_str(),++QueueSize);
+ inQueue = true;
+ }
+
+ ///- Create and send the Addon packet
+ if(sAddOnHandler.BuildAddonPacket(&recvPacket, &SendAddonPacked))
+ SendPacket(&SendAddonPacked);
+
+ if(inQueue)
+ return;
+
+ sWorld.UpdateMaxSessionCounters();
+
+ // Updates the population
+ if (pLimit > 0)
+ {
+ float popu = sWorld.GetActiveSessionCount(); //updated number of users on the server
+ popu /= pLimit;
+ popu *= 2;
+ loginDatabase.PExecute("UPDATE realmlist SET population = '%f' WHERE id = '%d'",popu,realmID);
+ sLog.outDetail( "Server Population (%f).",popu);
+ }
+
+ return;
+}
+
+/// Handle the Ping packet
+void WorldSocket::_HandlePing(WorldPacket& recvPacket)
+{
+ uint32 ping;
+ uint32 latency;
+
+ CHECK_PACKET_SIZE(recvPacket,8);
+
+ ///- Get the ping packet content
+ recvPacket >> ping;
+ recvPacket >> latency;
+
+ if (_session )
+ _session->SetLatency(latency);
+
+ ///- check ping speed for players
+ if(_session && _session->GetSecurity() == SEC_PLAYER)
+ {
+ uint32 cur_mstime = getMSTime();
+
+ // can overflow and start from 0
+ uint32 diff_mstime = getMSTimeDiff(m_LastPingMSTime,cur_mstime);
+ m_LastPingMSTime = cur_mstime;
+ if(diff_mstime < 27000) // should be 30000 (=30 secs), add little tolerance
+ {
+ ++m_OverSpeedPings;
+
+ uint32 max_count = sWorld.getConfig(CONFIG_MAX_OVERSPEED_PINGS);
+ if(max_count && m_OverSpeedPings > max_count)
+ {
+ sLog.outBasic("Player %s from account id %u kicked for overspeed ping packets from client (non-playable connection lags or cheating) ",_session->GetPlayerName(),_session->GetAccountId());
+ _session->KickPlayer();
+ return;
+ }
+ }
+ else
+ m_OverSpeedPings = 0;
+
+ }
+
+ ///- And put the pong answer in the to-be-sent queue
+ WorldPacket packet( SMSG_PONG, 4 );
+ packet << ping;
+ SendPacket(&packet);
+
+ return;
+}
+
+/// Handle the update order for the socket
+void WorldSocket::SendSinglePacket()
+{
+ WorldPacket *packet;
+ ServerPktHeader hdr;
+
+ ///- If we have packet to send
+ if (!_sendQueue.empty())
+ {
+ packet = _sendQueue.next();
+
+ hdr.size = ntohs((uint16)packet->size() + 2);
+ hdr.cmd = packet->GetOpcode();
+
+ if( sWorldLog.LogWorld() )
+ {
+ sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
+ (uint32)GetSocket(),
+ packet->size(),
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+
+ uint32 p = 0;
+ while (p < packet->size())
+ {
+ for (uint32 j = 0; j < 16 && p < packet->size(); j++)
+ sWorldLog.Log("%.2X ", (*packet)[p++]);
+
+ sWorldLog.Log("\n");
+ }
+
+ sWorldLog.Log("\n\n");
+ }
+
+ ///- Encrypt (if needed) the header
+ _crypt.EncryptSend((uint8*)&hdr, 4);
+
+ ///- Send the header and body to the client
+ TcpSocket::SendBuf((char*)&hdr, 4);
+ if(!packet->empty()) TcpSocket::SendBuf((char*)packet->contents(), packet->size());
+
+ delete packet;
+ }
+}
+
+void WorldSocket::Update(time_t diff)
+{
+ const uint32 SEND_PACKETS_MAX = 100;
+ const uint32 SEND_BUFFER_SIZE = 1024;
+
+ uint8 sendBuffer[SEND_BUFFER_SIZE];
+
+ while (!_sendQueue.empty())
+ {
+ bool haveBigPacket = false;
+ uint32 bufferSize = 0;
+
+ ///- While we have packets to send
+ for (uint32 packetCount = 0; (packetCount < SEND_PACKETS_MAX) && !_sendQueue.empty(); packetCount++)
+ {
+ ServerPktHeader *hdr = (ServerPktHeader*)&sendBuffer[bufferSize];
+
+ // check merge possibility.
+ WorldPacket *front = _sendQueue.front();
+ uint32 packetSize = front->size();
+
+ if ((sizeof(*hdr) + packetSize) > SEND_BUFFER_SIZE)
+ {
+ haveBigPacket = true;
+ break;
+ }
+
+ if ((bufferSize + sizeof(*hdr) + packetSize) > sizeof(sendBuffer))
+ break;
+
+ // can be merged
+ WorldPacket *packet = _sendQueue.next();
+
+ hdr->size = ntohs((uint16)packetSize + 2);
+ hdr->cmd = packet->GetOpcode();
+
+ if( sWorldLog.LogWorld() )
+ {
+ sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
+ (uint32)GetSocket(),
+ packetSize,
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+
+ uint32 p = 0;
+ while (p < packetSize)
+ {
+ for (uint32 j = 0; j < 16 && p < packetSize; j++)
+ sWorldLog.Log("%.2X ", (*packet)[p++]);
+
+ sWorldLog.Log("\n");
+ }
+
+ sWorldLog.Log("\n\n");
+ }
+
+ ///- Encrypt (if needed) the header
+ _crypt.EncryptSend((uint8*)hdr, sizeof(*hdr));
+ bufferSize += sizeof(*hdr);
+
+ if (packetSize)
+ {
+ memcpy(&sendBuffer[bufferSize], packet->contents(), packetSize);
+ bufferSize += packetSize;
+ }
+
+ ///- Send the header and body to the client
+ delete packet;
+ }
+
+ // send merged packets
+ if (bufferSize) TcpSocket::SendBuf((char*)sendBuffer, bufferSize);
+ // send too big non-merged packet
+ if (haveBigPacket) SendSinglePacket();
+ }
+}
+
+/// Handle the authentication waiting queue (to be completed)
+void WorldSocket::SendAuthWaitQue(uint32 position)
+{
+ if(position == 0)
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_OK );
+ SendPacket(&packet);
+ }
+ else
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
+ packet << uint8( AUTH_WAIT_QUEUE );
+ packet << uint32 (position); //amount of players in queue
+ SendPacket(&packet);
+ }
+}
+
+void WorldSocket::SizeError(WorldPacket const& packet, uint32 size) const
+{
+ sLog.outError("Client send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
+ LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
+}
diff --git a/src/game/WorldSocket.h b/src/game/WorldSocket.h
new file mode 100644
index 00000000000..26f8fb882f0
--- /dev/null
+++ b/src/game/WorldSocket.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w
+/// @{
+/// \file
+
+#ifndef __WORLDSOCKET_H
+#define __WORLDSOCKET_H
+
+#include "sockets/TcpSocket.h"
+#include "Auth/AuthCrypt.h"
+
+enum ResponseCodes
+{
+ RESPONSE_SUCCESS = 0x00,
+ RESPONSE_FAILURE = 0x01,
+ RESPONSE_CANCELLED = 0x02,
+ RESPONSE_DISCONNECTED = 0x03,
+ RESPONSE_FAILED_TO_CONNECT = 0x04,
+ RESPONSE_CONNECTED = 0x05,
+ RESPONSE_VERSION_MISMATCH = 0x06,
+
+ CSTATUS_CONNECTING = 0x07,
+ CSTATUS_NEGOTIATING_SECURITY = 0x08,
+ CSTATUS_NEGOTIATION_COMPLETE = 0x09,
+ CSTATUS_NEGOTIATION_FAILED = 0x0A,
+ CSTATUS_AUTHENTICATING = 0x0B,
+
+ AUTH_OK = 0x0C,
+ AUTH_FAILED = 0x0D,
+ AUTH_REJECT = 0x0E,
+ AUTH_BAD_SERVER_PROOF = 0x0F,
+ AUTH_UNAVAILABLE = 0x10,
+ AUTH_SYSTEM_ERROR = 0x11,
+ AUTH_BILLING_ERROR = 0x12,
+ AUTH_BILLING_EXPIRED = 0x13,
+ AUTH_VERSION_MISMATCH = 0x14,
+ AUTH_UNKNOWN_ACCOUNT = 0x15,
+ AUTH_INCORRECT_PASSWORD = 0x16,
+ AUTH_SESSION_EXPIRED = 0x17,
+ AUTH_SERVER_SHUTTING_DOWN = 0x18,
+ AUTH_ALREADY_LOGGING_IN = 0x19,
+ AUTH_LOGIN_SERVER_NOT_FOUND = 0x1A,
+ AUTH_WAIT_QUEUE = 0x1B,
+ AUTH_BANNED = 0x1C,
+ AUTH_ALREADY_ONLINE = 0x1D,
+ AUTH_NO_TIME = 0x1E,
+ AUTH_DB_BUSY = 0x1F,
+ AUTH_SUSPENDED = 0x20,
+ AUTH_PARENTAL_CONTROL = 0x21,
+ AUTH_LOCKED_ENFORCED = 0x22,
+
+ REALM_LIST_IN_PROGRESS = 0x23,
+ REALM_LIST_SUCCESS = 0x24,
+ REALM_LIST_FAILED = 0x25,
+ REALM_LIST_INVALID = 0x26,
+ REALM_LIST_REALM_NOT_FOUND = 0x27,
+
+ ACCOUNT_CREATE_IN_PROGRESS = 0x28,
+ ACCOUNT_CREATE_SUCCESS = 0x29,
+ ACCOUNT_CREATE_FAILED = 0x2A,
+
+ CHAR_LIST_RETRIEVING = 0x2B,
+ CHAR_LIST_RETRIEVED = 0x2C,
+ CHAR_LIST_FAILED = 0x2D,
+
+ CHAR_CREATE_IN_PROGRESS = 0x2E,
+ CHAR_CREATE_SUCCESS = 0x2F,
+ CHAR_CREATE_ERROR = 0x30,
+ CHAR_CREATE_FAILED = 0x31,
+ CHAR_CREATE_NAME_IN_USE = 0x32,
+ CHAR_CREATE_DISABLED = 0x33,
+ CHAR_CREATE_PVP_TEAMS_VIOLATION = 0x34,
+ CHAR_CREATE_SERVER_LIMIT = 0x35,
+ CHAR_CREATE_ACCOUNT_LIMIT = 0x36,
+ CHAR_CREATE_SERVER_QUEUE = 0x37,
+ CHAR_CREATE_ONLY_EXISTING = 0x38,
+ CHAR_CREATE_EXPANSION = 0x39,
+
+ CHAR_DELETE_IN_PROGRESS = 0x3A,
+ CHAR_DELETE_SUCCESS = 0x3B,
+ CHAR_DELETE_FAILED = 0x3C,
+ CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x3D,
+ CHAR_DELETE_FAILED_GUILD_LEADER = 0x3E,
+ CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x3F,
+
+ CHAR_LOGIN_IN_PROGRESS = 0x40,
+ CHAR_LOGIN_SUCCESS = 0x41,
+ CHAR_LOGIN_NO_WORLD = 0x42,
+ CHAR_LOGIN_DUPLICATE_CHARACTER = 0x43,
+ CHAR_LOGIN_NO_INSTANCES = 0x44,
+ CHAR_LOGIN_FAILED = 0x45,
+ CHAR_LOGIN_DISABLED = 0x46,
+ CHAR_LOGIN_NO_CHARACTER = 0x47,
+ CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x48,
+ CHAR_LOGIN_LOCKED_BY_BILLING = 0x49,
+
+ CHAR_NAME_SUCCESS = 0x4A,
+ CHAR_NAME_FAILURE = 0x4B,
+ CHAR_NAME_NO_NAME = 0x4C,
+ CHAR_NAME_TOO_SHORT = 0x4D,
+ CHAR_NAME_TOO_LONG = 0x4E,
+ CHAR_NAME_INVALID_CHARACTER = 0x4F,
+ CHAR_NAME_MIXED_LANGUAGES = 0x50,
+ CHAR_NAME_PROFANE = 0x51,
+ CHAR_NAME_RESERVED = 0x52,
+ CHAR_NAME_INVALID_APOSTROPHE = 0x53,
+ CHAR_NAME_MULTIPLE_APOSTROPHES = 0x54,
+ CHAR_NAME_THREE_CONSECUTIVE = 0x55,
+ CHAR_NAME_INVALID_SPACE = 0x56,
+ CHAR_NAME_CONSECUTIVE_SPACES = 0x57,
+ CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 0x58,
+ CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x59,
+ CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5A,
+};
+
+class WorldPacket;
+class SocketHandler;
+class WorldSession;
+
+/// Handle connection with the client software
+class WorldSocket : public TcpSocket
+{
+ public:
+ WorldSocket(ISocketHandler&);
+ ~WorldSocket();
+
+ void SendPacket(WorldPacket const* packet);
+ void CloseSocket();
+
+ void OnAccept();
+ void OnRead();
+ void OnDelete();
+
+ void Update(time_t diff);
+ // Player Queue
+ void SendAuthWaitQue(uint32 position);
+
+ WorldSession* GetSession() const { return _session; }
+ protected:
+ void SendSinglePacket();
+
+ protected:
+ void _HandleAuthSession(WorldPacket& recvPacket);
+ void _HandlePing(WorldPacket& recvPacket);
+
+ private:
+ AuthCrypt _crypt;
+ uint32 _seed;
+ uint32 _cmd;
+ uint16 _remaining;
+ WorldSession* _session;
+
+ ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> _sendQueue;
+
+ uint32 m_LastPingMSTime;
+ uint32 m_OverSpeedPings;
+
+ // internal checks
+ void SizeError(WorldPacket const& packet, uint32 size) const;
+};
+#endif
+/// @}
diff --git a/src/game/WorldSocketMgr.cpp b/src/game/WorldSocketMgr.cpp
new file mode 100644
index 00000000000..0281d200109
--- /dev/null
+++ b/src/game/WorldSocketMgr.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2008,2007 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "Common.h"
+#include "WorldSocket.h"
+#include "WorldSocketMgr.h"
+#include "Policies/SingletonImp.h"
+
+INSTANTIATE_SINGLETON_1( WorldSocketMgr );
+
+/// WorldSocketMgr constructor
+WorldSocketMgr::WorldSocketMgr()
+{
+}
+
+/// Add a WorldSocket to the set
+void WorldSocketMgr::AddSocket(WorldSocket *s)
+{
+ m_sockets.insert(s);
+}
+
+/// Remove a WorldSocket to the set
+void WorldSocketMgr::RemoveSocket(WorldSocket *s)
+{
+ m_sockets.erase(s);
+}
+
+/// Triggers an 'update' to all sockets in the set
+void WorldSocketMgr::Update(time_t diff)
+{
+ SocketSet::iterator i;
+ for(i = m_sockets.begin(); i != m_sockets.end(); i++)
+ {
+ (*i)->Update(diff);
+ }
+}
diff --git a/src/game/WorldSocketMgr.h b/src/game/WorldSocketMgr.h
new file mode 100644
index 00000000000..5a26d739f4a
--- /dev/null
+++ b/src/game/WorldSocketMgr.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2008,2007 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+/// \addtogroup u2w User to World Communication
+/// @{
+/// \file
+
+#ifndef __WORLDSOCKETMGR_H
+#define __WORLDSOCKETMGR_H
+
+#include "Policies/Singleton.h"
+
+class WorldSocket;
+
+/// Manages the list of connected WorldSockets
+class WorldSocketMgr
+{
+ public:
+ WorldSocketMgr();
+
+ void AddSocket(WorldSocket *s);
+ void RemoveSocket(WorldSocket *s);
+ void Update(time_t diff);
+
+ private:
+ typedef std::set<WorldSocket*> SocketSet;
+ SocketSet m_sockets;
+};
+
+#define sWorldSocketMgr MaNGOS::Singleton<WorldSocketMgr>::Instance()
+#endif
+/// @}
diff --git a/src/game/debugcmds.cpp b/src/game/debugcmds.cpp
new file mode 100644
index 00000000000..98d0984f545
--- /dev/null
+++ b/src/game/debugcmds.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "Log.h"
+#include "Unit.h"
+#include "ObjectAccessor.h"
+#include "GossipDef.h"
+#include "Language.h"
+#include "MapManager.h"
+
+bool ChatHandler::HandleDebugInArcCommand(const char* /*args*/)
+{
+ Object *obj = getSelectedUnit();
+
+ if(!obj)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ return true;
+ }
+
+ SendSysMessage(LANG_NOT_IMPLEMENTED);
+
+ return true;
+}
+
+bool ChatHandler::HandleDebugSpellFailCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ if(!px)
+ return false;
+
+ uint8 failnum = (uint8)atoi(px);
+
+ WorldPacket data(SMSG_CAST_FAILED, 5);
+ data << (uint32)133;
+ data << failnum;
+ m_session->SendPacket(&data);
+
+ return true;
+}
+
+bool ChatHandler::HandleSetPoiCommand(const char* args)
+{
+ Player *pPlayer = m_session->GetPlayer();
+ Unit* target = getSelectedUnit();
+ 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)
+ return false;
+
+ uint32 icon = atol(icon_text);
+ if ( icon < 0 )
+ icon = 0;
+
+ uint32 flags = atol(flags_text);
+
+ sLog.outDetail("Command : POI, NPC = %u, icon = %u flags = %u", target->GetGUIDLow(), icon,flags);
+ pPlayer->PlayerTalkClass->SendPointOfInterest(target->GetPositionX(), target->GetPositionY(), Poi_Icon(icon), flags, 30, "Test POI");
+ return true;
+}
+
+bool ChatHandler::HandleEquipErrorCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ uint8 msg = atoi(args);
+ m_session->GetPlayer()->SendEquipError(msg, 0, 0);
+ return true;
+}
+
+bool ChatHandler::HandleSellErrorCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ uint8 msg = atoi(args);
+ m_session->GetPlayer()->SendSellError(msg, 0, 0, 0);
+ return true;
+}
+
+bool ChatHandler::HandleBuyErrorCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ uint8 msg = atoi(args);
+ m_session->GetPlayer()->SendBuyError(msg, 0, 0, 0);
+ return true;
+}
+
+bool ChatHandler::HandleSendOpcodeCommand(const char* args)
+{
+ Unit *unit = getSelectedUnit();
+ if (!unit || (unit->GetTypeId() != TYPEID_PLAYER))
+ unit = m_session->GetPlayer();
+
+ FILE *file = fopen("opcode.txt", "r");
+ if(!file)
+ return false;
+
+ uint32 type;
+
+ uint32 val1;
+ uint64 val2;
+ float val3;
+ char val4[101];
+
+ uint32 opcode = 0;
+ fscanf(file, "%u", &opcode);
+ if(!opcode)
+ {
+ fclose(file);
+ return false;
+ }
+
+ WorldPacket data(opcode, 0);
+
+ while(fscanf(file, "%u", &type) != EOF)
+ {
+ switch(type)
+ {
+ case 0: // uint8
+ fscanf(file, "%u", &val1);
+ data << uint8(val1);
+ break;
+ case 1: // uint16
+ fscanf(file, "%u", &val1);
+ data << uint16(val1);
+ break;
+ case 2: // uint32
+ fscanf(file, "%u", &val1);
+ data << uint32(val1);
+ break;
+ case 3: // uint64
+ fscanf(file, I64FMTD, &val2);
+ data << uint64(val2);
+ break;
+ case 4: // float
+ fscanf(file, "%f", &val3);
+ data << float(val3);
+ break;
+ case 5: // string
+ fscanf(file, "%s", val4, 101);
+ data << val4;
+ break;
+ case 6: // packed guid
+ data.append(unit->GetPackGUID());
+ break;
+ default:
+ fclose(file);
+ return false;
+ }
+ }
+ fclose(file);
+ sLog.outDebug("Sending opcode %u", data.GetOpcode());
+ data.hexlike();
+ ((Player*)unit)->GetSession()->SendPacket(&data);
+ PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName());
+ return true;
+}
+
+bool ChatHandler::HandleUpdateWorldStateCommand(const char* args)
+{
+ char* w = strtok((char*)args, " ");
+ char* s = strtok(NULL, " ");
+
+ if (!w || !s)
+ return false;
+
+ uint32 world = (uint32)atoi(w);
+ uint32 state = (uint32)atoi(s);
+ m_session->GetPlayer()->SendUpdateWorldState(world, state);
+ return true;
+}
+
+bool ChatHandler::HandlePlaySound2Command(const char* args)
+{
+ if(!args)
+ return false;
+
+ uint32 soundid = atoi(args);
+ m_session->GetPlayer()->PlaySound(soundid, false);
+ return true;
+}
+
+//Send notification in channel
+bool ChatHandler::HandleSendChannelNotifyCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ const char *name = "test";
+ uint8 code = atoi(args);
+
+ WorldPacket data(SMSG_CHANNEL_NOTIFY, (1+10));
+ data << code; // notify type
+ data << name; // channel name
+ data << uint32(0);
+ data << uint32(0);
+ m_session->SendPacket(&data);
+ return true;
+}
+
+//Send notification in chat
+bool ChatHandler::HandleSendChatMsgCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ const char *msg = "testtest";
+ uint8 type = atoi(args);
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, m_session, type, 0, "chan", m_session->GetPlayer()->GetGUID(), msg, m_session->GetPlayer());
+ m_session->SendPacket(&data);
+ return true;
+}
+
+bool ChatHandler::HandleSendQuestPartyMsgCommand(const char* args)
+{
+ uint32 msg = atol((char*)args);
+ if (msg >= 0)
+ m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg);
+ return true;
+}
+
+bool ChatHandler::HandleGetLootRecipient(const char* args)
+{
+ Creature* target = getSelectedCreature();
+ if(!target)
+ return false;
+
+ PSendSysMessage("loot recipient: %s", target->hasLootRecipient()?(target->GetLootRecipient()?target->GetLootRecipient()->GetName():"offline"):"no loot recipient");
+ return true;
+}
+
+bool ChatHandler::HandleSendQuestInvalidMsgCommand(const char* args)
+{
+ uint32 msg = atol((char*)args);
+ if (msg >= 0)
+ m_session->GetPlayer()->SendCanTakeQuestResponse(msg);
+ return true;
+}
+
+bool ChatHandler::HandleGetItemState(const char* args)
+{
+ if (!args)
+ return false;
+
+ std::string state_str = args;
+
+ ItemUpdateState state = ITEM_UNCHANGED;
+ bool list_queue = false, check_all = false;
+ if (state_str == "unchanged") state = ITEM_UNCHANGED;
+ else if (state_str == "changed") state = ITEM_CHANGED;
+ else if (state_str == "new") state = ITEM_NEW;
+ else if (state_str == "removed") state = ITEM_REMOVED;
+ else if (state_str == "queue") list_queue = true;
+ else if (state_str == "check_all") check_all = true;
+ else return false;
+
+ Player* player = getSelectedPlayer();
+ if (!player) player = m_session->GetPlayer();
+
+ if (!list_queue && !check_all)
+ {
+ state_str = "The player has the following " + state_str + " items: ";
+ SendSysMessage(state_str.c_str());
+ for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END)
+ continue;
+
+ Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
+ if (!item) continue;
+ if (!item->IsBag())
+ {
+ if (item->GetState() == state)
+ PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()));
+ }
+ else
+ {
+ Bag *bag = (Bag*)item;
+ const ItemPrototype *proto = bag->GetProto();
+ for (uint8 j = 0; j < proto->ContainerSlots; ++j)
+ {
+ Item* item = bag->GetItemByPos(j);
+ if (item && item->GetState() == state)
+ PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()));
+ }
+ }
+ }
+ }
+
+ if (list_queue)
+ {
+ std::vector<Item *> &updateQueue = player->GetItemUpdateQueue();
+ for(size_t i = 0; i < updateQueue.size(); i++)
+ {
+ Item *item = updateQueue[i];
+ if(!item) continue;
+
+ Bag *container = item->GetContainer();
+ uint8 bag_slot = container ? container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0);
+
+ std::string st;
+ switch(item->GetState())
+ {
+ case ITEM_UNCHANGED: st = "unchanged"; break;
+ case ITEM_CHANGED: st = "changed"; break;
+ case ITEM_NEW: st = "new"; break;
+ case ITEM_REMOVED: st = "removed"; break;
+ }
+
+ PSendSysMessage("bag: %d slot: %d guid: %d - state: %s", bag_slot, item->GetSlot(), item->GetGUIDLow(), st.c_str());
+ }
+ if (updateQueue.empty())
+ PSendSysMessage("updatequeue empty");
+ }
+
+ if (check_all)
+ {
+ bool error = false;
+ std::vector<Item *> &updateQueue = player->GetItemUpdateQueue();
+ for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END)
+ continue;
+
+ Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
+ if (!item) continue;
+
+ if (item->GetSlot() != i)
+ {
+ PSendSysMessage("item at slot %d, guid %d has an incorrect slot value: %d", i, item->GetGUIDLow(), item->GetSlot());
+ error = true; continue;
+ }
+
+ if (item->GetOwnerGUID() != player->GetGUID())
+ {
+ PSendSysMessage("for the item at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow());
+ error = true; continue;
+ }
+
+ if (Bag *container = item->GetContainer())
+ {
+ PSendSysMessage("item at slot: %d guid: %d has a container (slot: %d, guid: %d) but shouldnt!", item->GetSlot(), item->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow());
+ error = true; continue;
+ }
+
+ if (item->IsInUpdateQueue())
+ {
+ uint16 qp = item->GetQueuePos();
+ if (qp > updateQueue.size())
+ {
+ PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", item->GetSlot(), item->GetGUIDLow(), qp);
+ error = true; continue;
+ }
+
+ if (updateQueue[qp] == NULL)
+ {
+ PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", item->GetSlot(), item->GetGUIDLow(), qp);
+ error = true; continue;
+ }
+
+ if (updateQueue[qp] != item)
+ {
+ PSendSysMessage("item at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", item->GetSlot(), item->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow());
+ error = true; continue;
+ }
+ }
+ else if (item->GetState() != ITEM_UNCHANGED)
+ {
+ PSendSysMessage("item at slot: %d guid: %d is not in queue but should be (state: %d)!", item->GetSlot(), item->GetGUIDLow(), item->GetState());
+ error = true; continue;
+ }
+
+ if(item->IsBag())
+ {
+ Bag *bag = (Bag*)item;
+ const ItemPrototype *proto = bag->GetProto();
+ for (uint8 j = 0; j < proto->ContainerSlots; ++j)
+ {
+ Item* item = bag->GetItemByPos(j);
+ if (!item) continue;
+
+ if (item->GetSlot() != j)
+ {
+ PSendSysMessage("the item in bag %d slot %d, guid %d has an incorrect slot value: %d", bag->GetSlot(), j, item->GetGUIDLow(), item->GetSlot());
+ error = true; continue;
+ }
+
+ if (item->GetOwnerGUID() != player->GetGUID())
+ {
+ PSendSysMessage("for the item in bag %d at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow());
+ error = true; continue;
+ }
+
+ Bag *container = item->GetContainer();
+ if (!container)
+ {
+ PSendSysMessage("the item in bag %d at slot %d with guid %d has no container!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow());
+ error = true; continue;
+ }
+
+ if (container != bag)
+ {
+ PSendSysMessage("the item in bag %d at slot %d with guid %d has a different container(slot %d guid %d)!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow());
+ error = true; continue;
+ }
+
+ if (item->IsInUpdateQueue())
+ {
+ uint16 qp = item->GetQueuePos();
+ if (qp > updateQueue.size())
+ {
+ PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp);
+ error = true; continue;
+ }
+
+ if (updateQueue[qp] == NULL)
+ {
+ PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp);
+ error = true; continue;
+ }
+
+ if (updateQueue[qp] != item)
+ {
+ PSendSysMessage("item in bag: %d at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow());
+ error = true; continue;
+ }
+ }
+ else if (item->GetState() != ITEM_UNCHANGED)
+ {
+ PSendSysMessage("item in bag: %d at slot: %d guid: %d is not in queue but should be (state: %d)!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), item->GetState());
+ error = true; continue;
+ }
+ }
+ }
+ }
+
+ for(size_t i = 0; i < updateQueue.size(); i++)
+ {
+ Item *item = updateQueue[i];
+ if(!item) continue;
+
+ 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());
+ 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());
+ error = true; continue;
+ }
+
+ if (item->GetState() == ITEM_REMOVED) continue;
+ Item *test = player->GetItemByPos( item->GetBagSlot(), item->GetSlot());
+
+ 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());
+ 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());
+ error = true; continue;
+ }
+ }
+ if (!error)
+ SendSysMessage("All OK!");
+ }
+
+ return true;
+}
diff --git a/src/game/tools.cpp b/src/game/tools.cpp
new file mode 100644
index 00000000000..7b4b4cffbaa
--- /dev/null
+++ b/src/game/tools.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "Tools.h"
+
+// THIS CAN BE A LOT FASTER
+bool readGUID(WorldPacket & data, uint64& guid)
+{
+ if(data.rpos()+1 > data.size())
+ return false;
+
+ uint8 guidmark=0;
+ uint8 bit;
+ uint8 shiftdata=0x1;
+ uint64 Temp=0;
+
+ guid = 0;
+
+ data >> guidmark;
+ for(int i=0;i<8;i++)
+ {
+ if(guidmark & shiftdata)
+ {
+ Temp = 0;
+
+ if(data.rpos()+1 > data.size())
+ return false;
+
+ data >> bit;
+ Temp = bit;
+ Temp <<= i*8;
+ guid |= Temp;
+ }
+ shiftdata=shiftdata<<1;
+ }
+
+ return true;
+}
+
+void writeGUID(WorldPacket & data, uint64 & guid)
+{
+ uint8 RAWmask = 0;
+ uint8 PackedGuid[8] = {0,0,0,0,0,0,0,0};
+
+ int j = 1;
+ uint8 * test = (uint8*)&guid;
+
+ if (*test)
+ {
+ PackedGuid[j] = *test;
+ RAWmask |= 1;
+ ++j;
+ }
+ if (*(test+1))
+ {
+ PackedGuid[j] = *(test+1);
+ RAWmask |= 2;
+ ++j;
+ }
+ if (*(test+2))
+ {
+ PackedGuid[j] = *(test+2);
+ RAWmask |= 4;
+ ++j;
+ }
+ if (*(test+3))
+ {
+ PackedGuid[j] = *(test+3);
+ RAWmask |= 8;
+ ++j;
+ }
+ if (*(test+4))
+ {
+ PackedGuid[j] = *(test+4);
+ RAWmask |= 16;
+ ++j;
+ }
+ if (*(test+5))
+ {
+ PackedGuid[j] = *(test+5);
+ RAWmask |= 32;
+ ++j;
+ }
+ if (*(test+6))
+ {
+ PackedGuid[j] = *(test+6);
+ RAWmask |= 64;
+ ++j;
+ }
+ if (*(test+7))
+ {
+ PackedGuid[j] = *(test+7);
+ RAWmask |= 128;
+ ++j;
+ }
+ PackedGuid[0] = RAWmask;
+
+ data.append(PackedGuid,j);
+}