diff options
author | Neo2003 <none@none> | 2008-10-06 04:48:59 -0500 |
---|---|---|
committer | Neo2003 <none@none> | 2008-10-06 04:48:59 -0500 |
commit | 1fc5c0d6d7200048009f99c2cb4d3fd12858ed2f (patch) | |
tree | 58895d02973f2387143bc3d1c1e5ecf8a28984fc /src/game | |
parent | 010ed993e1a00246dd15df97a3ba6893410d2d3f (diff) |
[svn] * Little fix in RandomMovementGenerator
* Updated to 6731 and 680
--HG--
branch : trunk
rename : 6721-676 => 6731-680
Diffstat (limited to 'src/game')
-rw-r--r-- | src/game/Chat.cpp | 56 | ||||
-rw-r--r-- | src/game/Chat.h | 13 | ||||
-rw-r--r-- | src/game/Creature.cpp | 161 | ||||
-rw-r--r-- | src/game/Creature.h | 107 | ||||
-rw-r--r-- | src/game/GameObject.cpp | 2500 | ||||
-rw-r--r-- | src/game/GameObject.h | 1184 | ||||
-rw-r--r-- | src/game/GossipDef.cpp | 12 | ||||
-rw-r--r-- | src/game/ItemHandler.cpp | 2432 | ||||
-rw-r--r-- | src/game/Language.h | 1 | ||||
-rw-r--r-- | src/game/Level2.cpp | 57 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 247 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 27 | ||||
-rw-r--r-- | src/game/Pet.cpp | 3500 | ||||
-rw-r--r-- | src/game/Player.cpp | 55 | ||||
-rw-r--r-- | src/game/Player.h | 1 | ||||
-rw-r--r-- | src/game/PointMovementGenerator.cpp | 149 | ||||
-rw-r--r-- | src/game/QuestDef.cpp | 399 | ||||
-rw-r--r-- | src/game/QuestDef.h | 662 | ||||
-rw-r--r-- | src/game/RandomMovementGenerator.cpp | 322 | ||||
-rw-r--r-- | src/game/Spell.cpp | 5 | ||||
-rw-r--r-- | src/game/SpellEffects.cpp | 117 | ||||
-rw-r--r-- | src/game/TargetedMovementGenerator.cpp | 413 | ||||
-rw-r--r-- | src/game/WaypointMovementGenerator.cpp | 4 |
23 files changed, 6327 insertions, 6097 deletions
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index d9ec554ee0d..e85053a2204 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -34,62 +34,6 @@ 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[] =
diff --git a/src/game/Chat.h b/src/game/Chat.h index 96b813bfe8a..26e3a3a969a 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -28,19 +28,6 @@ class Player; class Unit;
struct GameTele;
-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:
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index d0800c9b77f..555bb4db02a 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -62,15 +62,36 @@ TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const return NULL;
}
+bool VendorItemData::RemoveItem( uint32 item_id )
+{
+ for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
+ {
+ if((*i)->item==item_id)
+ {
+ m_items.erase(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+VendorItem const* VendorItemData::FindItem(uint32 item_id) const
+{
+ for(VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i )
+ if((*i)->item==item_id)
+ return *i;
+ return NULL;
+}
+
Creature::Creature() :
Unit(), i_AI(NULL),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
-m_itemsLoaded(false), m_lootMoney(0), m_lootRecipient(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_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_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0)
{
m_valuesCount = UNIT_END;
@@ -87,7 +108,7 @@ Creature::~Creature() {
CleanupsBeforeDelete();
- m_vendor_items.clear();
+ m_vendorItemCounts.clear();
delete i_AI;
i_AI = NULL;
@@ -503,6 +524,7 @@ bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, cons m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
break;
}
+ LoadCreaturesAddon();
}
return bResult;
@@ -673,16 +695,16 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) cantalking=false;
break;
case GOSSIP_OPTION_VENDOR:
- // load vendor items if not yet
- LoadGoods();
-
- if(!GetItemCount())
+ {
+ VendorItemData const* vItems = GetVendorItems();
+ if(!vItems || vItems->Empty())
{
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:
if(!isCanTrainingOf(pPlayer,false))
cantalking=false;
@@ -934,6 +956,9 @@ uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid) uint32 Creature::GetNpcTextId()
{
+ if (!m_DBTableGuid)
+ return DEFAULT_GOSSIP_MESSAGE;
+
if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
return pos;
@@ -1046,6 +1071,8 @@ void Creature::SaveToDB() void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
{
// update in loaded data
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
uint32 displayId = GetNativeDisplayId();
@@ -1237,7 +1264,6 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
- m_DBTableGuid = guidlow;
if(!UpdateEntry(Entry, team, data))
return false;
@@ -1263,7 +1289,7 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) return false;
}
- uint32 stored_guid = guid;
+ m_DBTableGuid = guid;
if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
uint16 team = 0;
@@ -1278,9 +1304,6 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) return false;
}
- m_DBTableGuid = stored_guid;
- LoadCreaturesAddon();
-
m_respawnradius = data->spawndist;
m_respawnDelay = data->spawntimesecs;
@@ -1346,24 +1369,6 @@ void Creature::LoadEquipment(uint32 equip_entry, bool force) }
}
-void Creature::LoadGoods()
-{
- // already loaded;
- if(m_itemsLoaded)
- return;
-
- m_vendor_items.clear();
-
- VendorItemList const* vList = objmgr.GetNpcVendorItemList(GetEntry());
- if(!vList)
- return;
-
- for (VendorItemList::const_iterator _item_iter = vList->begin(); _item_iter != vList->end(); ++_item_iter)
- AddItem( (*_item_iter)->item, (*_item_iter)->maxcount, (*_item_iter)->incrtime, (*_item_iter)->ExtendedCost);
-
- m_itemsLoaded = true;
-}
-
bool Creature::hasQuest(uint32 quest_id) const
{
QuestRelations const& qr = objmgr.mCreatureQuestRelations;
@@ -1388,6 +1393,12 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const void Creature::DeleteFromDB()
{
+ if (!m_DBTableGuid)
+ {
+ sLog.outDebug("Trying to delete not saved creature!");
+ return;
+ }
+
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
objmgr.DeleteCreatureData(m_DBTableGuid);
@@ -1494,7 +1505,8 @@ void Creature::Respawn() if(getDeathState()==DEAD)
{
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ if (m_DBTableGuid)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
m_respawnTime = time(NULL); // respawn at next tick
}
}
@@ -1716,7 +1728,7 @@ void Creature::CallAssistence() void Creature::SaveRespawnTime()
{
- if(isPet())
+ if(isPet() || !m_DBTableGuid)
return;
if(m_respawnTime > time(NULL)) // dead (no corpse)
@@ -1752,8 +1764,11 @@ bool Creature::IsOutOfThreatArea(Unit* pVictim) const CreatureDataAddon const* Creature::GetCreatureAddon() const
{
- if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
- return addon;
+ if (m_DBTableGuid)
+ {
+ if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
+ return addon;
+ }
// dependent from heroic mode entry
return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
@@ -1956,6 +1971,84 @@ char const* Creature::GetScriptName() const return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
}
+
+VendorItemData const* Creature::GetVendorItems() const
+{
+ return objmgr.GetNpcVendorItemList(GetEntry());
+}
+
+uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
+{
+ if(!vItem->maxcount)
+ return vItem->maxcount;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ return vItem->maxcount;
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
+ {
+ m_vendorItemCounts.erase(itr);
+ return vItem->maxcount;
+ }
+
+ vCount->count += diff * pProto->BuyCount;
+ vCount->lastIncrementTime = ptime;
+ }
+
+ return vCount->count;
+}
+
+uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
+{
+ if(!vItem->maxcount)
+ return 0;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ {
+ uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
+ m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
+ return new_count;
+ }
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
+ vCount->count += diff * pProto->BuyCount;
+ else
+ vCount->count = vItem->maxcount;
+ }
+
+ vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
+ vCount->lastIncrementTime = ptime;
+ return vCount->count;
+}
+
TrainerSpellData const* Creature::GetTrainerSpells() const
{
return objmgr.GetNpcTrainerSpells(GetEntry());
diff --git a/src/game/Creature.h b/src/game/Creature.h index 2ade7c48695..6145b946892 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -112,19 +112,6 @@ struct GossipOption 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;
-};
-
enum CreatureFlagsExtra
{
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
@@ -291,6 +278,56 @@ enum InhabitTypeValues #pragma pack(pop)
#endif
+// Vendors
+struct VendorItem
+{
+ VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
+ : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {}
+
+ uint32 item;
+ uint32 maxcount; // 0 for infinity item amount
+ uint32 incrtime; // time for restore items amount if maxcount != 0
+ uint32 ExtendedCost;
+};
+typedef std::vector<VendorItem*> VendorItemList;
+
+struct VendorItemData
+{
+ VendorItemList m_items;
+
+ VendorItem* GetItem(uint32 slot) const
+ {
+ if(slot>=m_items.size()) return NULL;
+ return m_items[slot];
+ }
+ bool Empty() const { return m_items.empty(); }
+ uint8 GetItemCount() const { return m_items.size(); }
+ void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
+ {
+ m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost));
+ }
+ bool RemoveItem( uint32 item_id );
+ VendorItem const* FindItem(uint32 item_id) const;
+
+ void Clear()
+ {
+ for (VendorItemList::iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
+ delete (*itr);
+ }
+};
+
+struct VendorItemCount
+{
+ explicit VendorItemCount(uint32 _item, uint32 _count)
+ : itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
+
+ uint32 itemId;
+ uint32 count;
+ time_t lastIncrementTime;
+};
+
+typedef std::list<VendorItemCount> VendorItemCounts;
+
struct TrainerSpell
{
uint32 spell;
@@ -418,41 +455,9 @@ class MANGOS_DLL_SPEC Creature : public Unit 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;
- }
+ VendorItemData const* GetVendorItems() const;
+ uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
+ uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
TrainerSpellData const* GetTrainerSpells() const;
@@ -562,9 +567,7 @@ class MANGOS_DLL_SPEC Creature : public Unit 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
+ VendorItemCounts m_vendorItemCounts;
void _RealtimeSetCreatureInfo();
@@ -592,7 +595,7 @@ class MANGOS_DLL_SPEC Creature : public Unit uint32 m_regenTimer;
MovementGeneratorType m_defaultMovementType;
Cell m_currentCell; // store current cell where creature listed
- uint32 m_DBTableGuid;
+ uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid
uint32 m_equipmentId;
bool m_AlreadyCallAssistence;
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index c3b8cf3d6e4..dc69b830760 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -1,1253 +1,1247 @@ -/* - * 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); -} +/*
+ * 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;
+
+ m_DBTableGuid = 0;
+}
+
+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_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 gameobject 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;
+
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
+ // 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;
+
+ m_DBTableGuid = 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;
+
+ 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(m_DBTableGuid, 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->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ 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->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ 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 index 30f3a815397..bab73fb1968 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -1,592 +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 +/*
+ * 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; ///< For new or temporary gameobjects is 0 for saved it is lowguid
+ GameObjectInfo const* m_goInfo;
+ private:
+ void SwitchDoorOrButton(bool activate);
+
+ GridReference<GameObject> m_gridRef;
+};
+#endif
diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp index 08f35c8618b..645cd962146 100644 --- a/src/game/GossipDef.cpp +++ b/src/game/GossipDef.cpp @@ -391,7 +391,7 @@ void PlayerMenu::SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID ) data << uint8(questStatus);
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
+ 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 )
@@ -477,7 +477,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID }
pSession->SendPacket( &data );
- //sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
}
void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
@@ -515,7 +515,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) 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->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details)
data << uint32(pQuest->GetQuestLevel()); // may be 0
data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log
@@ -597,7 +597,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << ObjectiveText[iI];
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
+ sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
}
void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext )
@@ -679,7 +679,7 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, 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() );
+ 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 )
@@ -758,5 +758,5 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID 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() );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
}
diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index de687ad6a09..46a179fd78b 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -1,1221 +1,1211 @@ -/* - * 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); -} +/*
+ * 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();
+
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems)
+ {
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ uint8 numitems = vItems->GetItemCount();
+ uint8 count = 0;
+
+ WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
+ data << uint64(vendorguid);
+ data << uint8(numitems);
+
+ float discountMod = _player->GetReputationPriceDiscount(pCreature);
+
+ for(int i = 0; i < numitems; i++ )
+ {
+ if(VendorItem const* crItem = vItems->GetItem(i))
+ {
+ if(ItemPrototype const *pProto = objmgr.GetItemPrototype(crItem->item))
+ {
+ if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
+ continue;
+
+ ++count;
+
+ // reputation discount
+ uint32 price = uint32(floor(pProto->BuyPrice * discountMod));
+
+ data << uint32(count);
+ data << uint32(crItem->item);
+ data << uint32(pProto->DisplayInfoID);
+ data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem));
+ 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/Language.h b/src/game/Language.h index ef27a9a0cd6..dcd76059e2c 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -303,6 +303,7 @@ enum MangosStrings LANG_LOOKUP_PLAYER_ACCOUNT = 328,
LANG_LOOKUP_PLAYER_CHARACTER = 329,
LANG_NO_PLAYERS_FOUND = 330,
+ LANG_EXTENDED_COST_NOT_EXIST = 331,
// Room for more level 2
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index d3457d19493..d2911057ee3 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1237,14 +1237,6 @@ 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)
{
@@ -1252,6 +1244,7 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) SetSentErrorMessage(true);
return false;
}
+
uint32 itemId = atol(pitem);
char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
@@ -1267,41 +1260,20 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) 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;
- }
+ Creature* vendor = getSelectedCreature();
- // load vendor items if not yet
- vendor->LoadGoods();
+ uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;
- if(vendor->FindItem(itemId))
+ if(!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer()))
{
- 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;
- }
+ objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost);
+
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
- // 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;
}
@@ -1329,25 +1301,16 @@ bool ChatHandler::HandleDelVendorItemCommand(const char* args) }
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))
+ if(!objmgr.RemoveVendorItem(vendor->GetEntry(),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);
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
return true;
}
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index a6cbb0e653d..1bbca47d3b5 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -70,6 +70,40 @@ bool normalizePlayerName(std::string& name) return 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;
+}
+
ObjectMgr::ObjectMgr()
{
m_hiCharGuid = 1;
@@ -134,8 +168,7 @@ ObjectMgr::~ObjectMgr() delete itr->second;
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- for (VendorItemList::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2)
- delete (*itr2);
+ itr->second.Clear();
for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
itr->second.Clear();
@@ -2697,35 +2730,35 @@ void ObjectMgr::LoadQuests() 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
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
+ // 9 10 11 12 13 14 15 16
"RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
- // 16 17 18 19 20 21 22 23 24 25
+ // 17 18 19 20 21 22 23 24 25 26
"QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
- // 26 27 28 29 30 31 32 33 34 35
+ // 27 28 29 30 31 32 33 34 35 36
"Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
- // 36 37 38 39 40 41 42 43
+ // 37 38 39 40 41 42 43 44
"ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
- // 44 45 46 47 48 49 50 51 52 53 54 55
+ // 45 46 47 48 49 50 51 52 53 54 54 55
"ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
- // 56 57 58 59 60 61 62 63
+ // 57 58 59 60 61 62 63 64
"ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
- // 64 65 66 67
+ // 65 66 67 68
"ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
- // 68 69 70 71 72 73
+ // 69 70 71 72 73 74
"RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
- // 74 75 76 77 78 79
+ // 75 76 77 78 79 80
"RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
- // 80 81 82 83 84 85 86 87
+ // 81 82 83 84 85 86 87 88
"RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
- // 88 89 90 91 92 93 94 95 96 97
+ // 89 90 91 92 93 94 95 96 97 98
"RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
- // 98 99 100 101 102 103 104 105 106 107
+ // 99 100 101 102 103 104 105 106 107 108
"RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
- // 108 109 110 111 112 113 114 115 116 117
+ // 109 110 111 112 113 114 115 116 117 118
"DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
- // 118 119
+ // 119 120
"StartScript, CompleteScript"
" FROM quest_template");
if(result == NULL)
@@ -2761,6 +2794,11 @@ void ObjectMgr::LoadQuests() // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
+ if( qinfo->GetQuestMethod() >= 3 )
+ {
+ sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
+ }
+
if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
{
sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
@@ -6148,14 +6186,14 @@ bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min if(entry==0)
{
- sLog.outString("Table `%s` contain reserved entry 0, ignored.",table);
+ sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
continue;
}
else if(entry < min_value || entry > max_value)
{
int32 start = min_value > 0 ? min_value : max_value;
int32 end = min_value > 0 ? max_value : min_value;
- sLog.outString("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
+ sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
continue;
}
@@ -6163,7 +6201,7 @@ bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min if(data.Content.size() > 0)
{
- sLog.outString("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
+ sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
continue;
}
@@ -6656,22 +6694,30 @@ void ObjectMgr::LoadTrainerSpell() barGoLink bar( result->GetRowCount() );
- uint32 count = 0,entry,spell;
+ uint32 count = 0;
do
{
bar.step();
Field* fields = result->Fetch();
- entry = fields[0].GetUInt32();
- spell = fields[1].GetUInt32();
+ uint32 entry = fields[0].GetUInt32();
+ uint32 spell = fields[1].GetUInt32();
+
+ CreatureInfo const* cInfo = GetCreatureTemplate(entry);
- if(!GetCreatureTemplate(entry))
+ if(!cInfo)
{
sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
continue;
}
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
+ {
+ sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
+ continue;
+ }
+
SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
if(!spellinfo)
{
@@ -6715,10 +6761,7 @@ void ObjectMgr::LoadVendors() {
// For reload case
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- {
- for (VendorItemList::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2)
- delete (*itr2);
- }
+ itr->second.Clear();
m_mCacheVendorItemMap.clear();
QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
@@ -6736,48 +6779,23 @@ void ObjectMgr::LoadVendors() barGoLink bar( result->GetRowCount() );
uint32 count = 0;
- uint32 entry, item_id, ExtendedCost;
do
{
bar.step();
Field* fields = result->Fetch();
- entry = fields[0].GetUInt32();
- if(!GetCreatureTemplate(entry))
- {
- sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", entry);
- continue;
- }
-
- item_id = fields[1].GetUInt32();
- if(!GetItemPrototype(item_id))
- {
- sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",entry,item_id);
- continue;
- }
+ uint32 entry = fields[0].GetUInt32();
+ uint32 item_id = fields[1].GetUInt32();
+ uint32 maxcount = fields[2].GetUInt32();
+ uint32 incrtime = fields[3].GetUInt32();
+ uint32 ExtendedCost = fields[4].GetUInt32();
- ExtendedCost = fields[4].GetUInt32();
- if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
- {
- sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,entry);
+ if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost))
continue;
- }
- VendorItemList& vList = m_mCacheVendorItemMap[entry];
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
- if(vList.size() >= MAX_VENDOR_ITEMS)
- {
- sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vList.size(), MAX_VENDOR_ITEMS, entry);
- continue;
- }
-
- VendorItem* pVendorItem = new VendorItem();
- pVendorItem->item = item_id;
- pVendorItem->maxcount = fields[2].GetUInt32();
- pVendorItem->incrtime = fields[3].GetUInt32();
- pVendorItem->ExtendedCost = ExtendedCost;
-
- vList.push_back(pVendorItem);
+ vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
++count;
} while (result->NextRow());
@@ -6838,6 +6856,109 @@ void ObjectMgr::LoadNpcTextId() sLog.outString( ">> Loaded %d NpcTextId ", count );
}
+void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )
+{
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
+ vList.AddItem(item,maxcount,incrtime,extendedcost);
+
+ WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
+}
+
+bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item )
+{
+ CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
+ if(iter == m_mCacheVendorItemMap.end())
+ return false;
+
+ if(!iter->second.FindItem(item))
+ return false;
+
+ iter->second.RemoveItem(item);
+ WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
+ return true;
+}
+
+bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl ) const
+{
+ CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
+ if(!cInfo)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
+ return false;
+ }
+
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
+ return false;
+ }
+
+ if(!GetItemPrototype(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
+ else
+ sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
+ return false;
+ }
+
+ if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
+ return false;
+ }
+
+ if(maxcount > 0 && incrtime == 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
+ return false;
+ }
+ else if(maxcount==0 && incrtime > 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
+ if(!vItems)
+ return true; // later checks for non-empty lists
+
+ if(vItems->FindItem(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
+ return false;
+ }
+
+ return true;
+}
+
// Functions for scripting access
const char* GetAreaTriggerScriptNameById(uint32 id)
{
@@ -6848,7 +6969,7 @@ bool LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value, in {
if(start_value >= 0 || start_value <= end_value) // start/end reversed for negative values
{
- sLog.outError("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
+ sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
start_value = -1;
end_value = std::numeric_limits<int32>::min();
}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index c9baf762ac3..5bb3aafde72 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -228,18 +228,8 @@ struct PlayerCondition // NPC gossip text id
typedef HM_NAMESPACE::hash_map<uint32, uint32> CacheNpcTextIdMap;
-// Vendors
-struct VendorItem
-{
- uint32 item;
- uint32 maxcount;
- uint32 incrtime;
- uint32 ExtendedCost;
-};
-typedef std::vector<VendorItem*> VendorItemList;
-
-typedef HM_NAMESPACE::hash_map<uint32, VendorItemList> CacheVendorItemMap;
+typedef HM_NAMESPACE::hash_map<uint32, VendorItemData> CacheVendorItemMap;
typedef HM_NAMESPACE::hash_map<uint32, TrainerSpellData> CacheTrainerSpellMap;
enum SkillRangeType
@@ -258,6 +248,16 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial); bool normalizePlayerName(std::string& name);
+struct MANGOS_DLL_SPEC LanguageDesc
+{
+ Language lang_id;
+ uint32 spell_id;
+ uint32 skill_id;
+};
+
+extern LanguageDesc lang_description[LANGUAGES_COUNT];
+MANGOS_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);
+
class PlayerDumpReader;
class ObjectMgr
@@ -732,7 +732,7 @@ class ObjectMgr return &iter->second;
}
- VendorItemList const* GetNpcVendorItemList(uint32 entry) const
+ VendorItemData const* GetNpcVendorItemList(uint32 entry) const
{
CacheVendorItemMap::const_iterator iter = m_mCacheVendorItemMap.find(entry);
if(iter == m_mCacheVendorItemMap.end())
@@ -740,6 +740,9 @@ class ObjectMgr return &iter->second;
}
+ void AddVendorItem(uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost);
+ bool RemoveVendorItem(uint32 entry,uint32 item);
+ bool IsVendorItemValid( uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL ) const;
protected:
uint32 m_auctionid;
uint32 m_mailid;
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index c80bbc8f673..3d8eaf17182 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1,1750 +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); -} +/*
+ * 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 = int32(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/Player.cpp b/src/game/Player.cpp index 1d032265a80..0da7f36c3eb 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5794,7 +5794,7 @@ void Player::RewardReputation(Unit *pVictim, float rate) if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
return;
- ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(pVictim->GetEntry());
+ ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
if(!Rep)
return;
@@ -16380,20 +16380,28 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false;
}
- // load vendor items if not yet
- pCreature->LoadGoods();
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems || vItems->Empty())
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
+ return false;
+ }
- CreatureItem* crItem = pCreature->FindItem(item);
+ VendorItem const* crItem = vItems->FindItem(item);
if(!crItem)
{
SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
return false;
}
- if( crItem->maxcount != 0 && crItem->count < count )
+ // check current item amount if it limited
+ if( crItem->maxcount != 0 )
{
- SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
- return false;
+ if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count )
+ {
+ SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
+ return false;
+ }
}
if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
@@ -16508,17 +16516,16 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint if(Item *it = StoreNewItem( dest, item, true ))
{
- if( crItem->maxcount != 0 )
- crItem->count -= pProto->BuyCount * count;
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,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)crItem->item;
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
- SendNewItem(it, count, true, false, false);
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
}
}
else if( IsEquipmentPos( bag, slot ) )
@@ -16548,17 +16555,16 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true ))
{
- if( crItem->maxcount != 0 )
- crItem->count -= pProto->BuyCount * count;
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,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)crItem->item;
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
- SendNewItem(it, count, true, false, false);
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
AutoUnequipOffhandIfNeed();
}
@@ -16569,7 +16575,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false;
}
- return crItem->maxcount!=0?true:false;
+ return crItem->maxcount!=0;
}
uint32 Player::GetMaxPersonalArenaRatingRequirement()
@@ -17174,7 +17180,7 @@ void Player::SendInitialPacketsBeforeAddToMap() // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight())
- SetUnitMovementFlags(GetUnitMovementFlags() | MOVEMENTFLAG_FLYING2);
+ AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
}
void Player::SendInitialPacketsAfterAddToMap()
@@ -18167,3 +18173,14 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const return false;
}
+
+bool Player::isAllowUseBattleGroundObject()
+{
+ return ( //InBattleGround() && // in battleground - not need, check in other cases
+ !IsMounted() && // not mounted
+ !HasStealthAura() && // not stealthed
+ !HasInvisibilityAura() && // not invisible
+ !HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup
+ isAlive() // live player
+ );
+}
diff --git a/src/game/Player.h b/src/game/Player.h index 9a4362c04ba..33ccc79e307 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1898,6 +1898,7 @@ class MANGOS_DLL_SPEC Player : public Unit void ClearAfkReports() { m_bgAfkReporter.clear(); }
bool GetBGAccessByLevel(uint32 bgTypeId) const;
+ bool isAllowUseBattleGroundObject();
/*********************************************************/
/*** REST SYSTEM ***/
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index c784c799351..b03263c8bb6 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -1,73 +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 - */ - -#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); +/*
+ * 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);
+
+ if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly())
+ unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+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/QuestDef.cpp b/src/game/QuestDef.cpp index c92bb9b7b6a..d18c931440c 100644 --- a/src/game/QuestDef.cpp +++ b/src/game/QuestDef.cpp @@ -1,199 +1,200 @@ -/* - * 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)); -} +/*
+ * 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();
+ QuestMethod = questRecord[1].GetUInt32();
+ ZoneOrSort = questRecord[2].GetInt32();
+ SkillOrClass = questRecord[3].GetInt32();
+ MinLevel = questRecord[4].GetUInt32();
+ QuestLevel = questRecord[5].GetUInt32();
+ Type = questRecord[6].GetUInt32();
+ RequiredRaces = questRecord[7].GetUInt32();
+ RequiredSkillValue = questRecord[8].GetUInt32();
+ RepObjectiveFaction = questRecord[9].GetUInt32();
+ RepObjectiveValue = questRecord[10].GetInt32();
+ RequiredMinRepFaction = questRecord[11].GetUInt32();
+ RequiredMinRepValue = questRecord[12].GetInt32();
+ RequiredMaxRepFaction = questRecord[13].GetUInt32();
+ RequiredMaxRepValue = questRecord[14].GetInt32();
+ SuggestedPlayers = questRecord[15].GetUInt32();
+ LimitTime = questRecord[16].GetUInt32();
+ QuestFlags = questRecord[17].GetUInt16();
+ uint32 SpecialFlags = questRecord[18].GetUInt16();
+ CharTitleId = questRecord[19].GetUInt32();
+ PrevQuestId = questRecord[20].GetInt32();
+ NextQuestId = questRecord[21].GetInt32();
+ ExclusiveGroup = questRecord[22].GetInt32();
+ NextQuestInChain = questRecord[23].GetUInt32();
+ SrcItemId = questRecord[24].GetUInt32();
+ SrcItemCount = questRecord[25].GetUInt32();
+ SrcSpell = questRecord[26].GetUInt32();
+ Title = questRecord[27].GetCppString();
+ Details = questRecord[28].GetCppString();
+ Objectives = questRecord[29].GetCppString();
+ OfferRewardText = questRecord[30].GetCppString();
+ RequestItemsText = questRecord[31].GetCppString();
+ EndText = questRecord[32].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ObjectiveText[i] = questRecord[33+i].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemId[i] = questRecord[37+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemCount[i] = questRecord[41+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceId[i] = questRecord[45+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceCount[i] = questRecord[49+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceRef[i] = questRecord[53+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOId[i] = questRecord[57+i].GetInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOCount[i] = questRecord[61+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqSpell[i] = questRecord[65+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemId[i] = questRecord[69+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemCount[i] = questRecord[75+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemId[i] = questRecord[81+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemCount[i] = questRecord[85+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepFaction[i] = questRecord[89+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepValue[i] = questRecord[94+i].GetInt32();
+
+ RewOrReqMoney = questRecord[99].GetInt32();
+ RewMoneyMaxLevel = questRecord[100].GetUInt32();
+ RewSpell = questRecord[101].GetUInt32();
+ RewSpellCast = questRecord[102].GetUInt32();
+ RewMailTemplateId = questRecord[103].GetUInt32();
+ RewMailDelaySecs = questRecord[104].GetUInt32();
+ PointMapId = questRecord[105].GetUInt32();
+ PointX = questRecord[106].GetFloat();
+ PointY = questRecord[107].GetFloat();
+ PointOpt = questRecord[108].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmote[i] = questRecord[109+i].GetUInt32();
+
+ IncompleteEmote = questRecord[113].GetUInt32();
+ CompleteEmote = questRecord[114].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmote[i] = questRecord[115+i].GetInt32();
+
+ QuestStartScript = questRecord[119].GetUInt32();
+ QuestCompleteScript = questRecord[120].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 index ddd24bf57f0..fda013337f0 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -1,330 +1,332 @@ -/* - * 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 +/*
+ * 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_PARTY_ACCEPT = 2, // Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT
+ 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; }
+ uint32 GetQuestMethod() const { return QuestMethod; }
+ 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 QuestMethod ? false : true; }
+ 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;
+ uint32 QuestMethod;
+ 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/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index 1022a0699f6..754e76d8267 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -1,159 +1,163 @@ -/* - * 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; -} +/*
+ * 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, &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;
+
+ // prevent invalid coordinates generation
+ MaNGOS::NormalizeMapCoord(nx);
+ MaNGOS::NormalizeMapCoord(ny);
+
+ 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.AddUnitMovementFlag(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.AddUnitMovementFlag(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.AddUnitMovementFlag(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/Spell.cpp b/src/game/Spell.cpp index 7cefcc1de8f..3be0b6989b2 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -3681,6 +3681,11 @@ uint8 Spell::CanCast(bool strict) (!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
return SPELL_FAILED_BAD_TARGETS;
+ // In BattleGround players can use only flags and banners
+ if( ((Player*)m_caster)->InBattleGround() &&
+ !((Player*)m_caster)->isAllowUseBattleGroundObject() )
+ return SPELL_FAILED_TRY_AGAIN;
+
// get the lock entry
LockEntry const *lockInfo = NULL;
if (GameObject* go=m_targets.getGOTarget())
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index fd6410e55ac..9816f683b05 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -52,6 +52,8 @@ #include "Language.h"
#include "SocialMgr.h"
#include "Util.h"
+#include "TemporarySummon.h"
+
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
{
@@ -1006,10 +1008,78 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster,42337,true,NULL);
return;
}
+ case 37573: //Temporal Phase Modulator
+ {
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+ const uint32 entry_list[6] = {21821, 21820, 21817};
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(entry_list[urand(0, 2)], x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+
+ if(pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 34665: //Administer Antidote
+ {
+ if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(16992, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+ ((Player*)m_caster)->KilledMonster(16992,pCreature->GetGUID());
+
+ if (pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 44997: // Converting Sentry
+ {
+ //Converted Sentry Credit
+ m_caster->CastSpell(m_caster, 45009, true);
+ return;
+ }
case 45030: // Impale Emissary
{
// Emissary of Hate Credit
- m_caster->CastSpell(m_caster,45088,true);
+ m_caster->CastSpell(m_caster, 45088, true);
return;
}
case 50243: // Teach Language
@@ -1102,6 +1172,16 @@ void Spell::EffectDummy(uint32 i) }
return;
}
+ case 32826:
+ {
+ if ( unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT )
+ {
+ //Polymorph Cast Visual Rank 1
+ const uint32 spell_list[6] = {32813, 32816, 32817, 32818, 32819, 32820};
+ unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true);
+ }
+ return;
+ }
}
break;
case SPELLFAMILY_WARRIOR:
@@ -2760,27 +2840,26 @@ void Spell::EffectOpenLock(uint32 /*i*/) if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune ||
goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK )
{
- if(BattleGround *bg = player->GetBattleGround())// in battleground
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
+ if(BattleGround *bg = player->GetBattleGround())
{
- 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;
- }
+ // check if it's correct bg
+ if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+ return;
}
}
else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
{
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
if(BattleGround *bg = player->GetBattleGround())
+ {
if(bg->GetTypeID() == BATTLEGROUND_EY)
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
- return;
+ return;
+ }
}
lockId = gameObjTarget->GetLockId();
guid = gameObjTarget->GetGUID();
@@ -4751,6 +4830,16 @@ void Spell::EffectScriptEffect(uint32 effIndex) unitTarget->CastSpell(unitTarget, spellId, true);
break;
}
+ //5,000 Gold
+ case 46642:
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->ModifyMoney(50000000);
+
+ break;
+ }
}
if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN )
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index bba7426938d..9d465877a2a 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -1,202 +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 "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; +/*
+ * 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);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ _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);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ 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/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 48f739fa6a1..b998b61fa8a 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -112,7 +112,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &di // Now we re-set destination to same node and start travel
creature.addUnitState(UNIT_STAT_ROAMING);
if (creature.canFly())
- creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
const WaypointNode &node = i_path->at(i_currentNode);
i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
@@ -176,7 +176,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &di {
creature.addUnitState(UNIT_STAT_ROAMING);
if (creature.canFly())
- creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
const WaypointNode &node = i_path->at(i_currentNode);
i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
|