Merge remote branch 'origin/master' into 330

This commit is contained in:
tomrus88 2010-04-03 11:33:45 +04:00
commit d131f137cc
44 changed files with 670 additions and 498 deletions

View file

@ -95,6 +95,7 @@ bool LoginQueryHolder::Initialize()
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBGDATA, "SELECT instance_id, team, join_x, join_y, join_z, join_o, join_map, taxi_start, taxi_end, mount_spell FROM character_battleground_data WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILS, "SELECT id,messageType,sender,receiver,subject,body,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC", GUID_LOPART(m_guid));

View file

@ -266,10 +266,11 @@ ChatCommand * ChatHandler::getCommandTable()
static ChatCommand listCommandTable[] =
{
{ "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL },
{ "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL },
{ "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListItemCommand, "", NULL },
{ "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListObjectCommand, "", NULL },
{ "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL },
{ "talents", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListTalentsCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};

View file

@ -226,6 +226,7 @@ class ChatHandler
bool HandleListCreatureCommand(const char* args);
bool HandleListItemCommand(const char* args);
bool HandleListObjectCommand(const char* args);
bool HandleListTalentsCommand(const char * args);
bool HandleLookupAreaCommand(const char* args);
bool HandleLookupCreatureCommand(const char* args);
@ -542,6 +543,7 @@ class ChatHandler
bool HandleUnBanHelper(BanMode mode,char const* args);
void HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel);
void HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id);
void ShowSpellListHelper(Player* target, SpellEntry const* spellInfo, LocaleConstant loc);
void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
private:

View file

@ -267,4 +267,12 @@ bool Corpse::IsFriendlyTo( Unit const* unit ) const
return owner->IsFriendlyTo(unit);
else
return true;
}
}
bool Corpse::IsExpired(time_t t) const
{
if(m_type == CORPSE_BONES)
return m_time < t - 60*MINUTE;
else
return m_time < t - 3*DAY;
}

View file

@ -91,6 +91,8 @@ class Corpse : public WorldObject
GridReference<Corpse> &GetGridRef() { return m_gridRef; }
bool isActiveObject() const { return false; }
bool IsExpired(time_t t) const;
private:
GridReference<Corpse> m_gridRef;

View file

@ -665,14 +665,19 @@ TalentSpellPos const* GetTalentSpellPos(uint32 spellId)
return &itr->second;
}
uint32 GetTalentSpellCost(uint32 spellId)
uint32 GetTalentSpellCost(TalentSpellPos const* pos)
{
if(TalentSpellPos const* pos = GetTalentSpellPos(spellId))
if (pos)
return pos->rank+1;
return 0;
}
uint32 GetTalentSpellCost(uint32 spellId)
{
return GetTalentSpellCost(GetTalentSpellPos(spellId));
}
int32 GetAreaFlagByAreaID(uint32 area_id)
{
AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id);

View file

@ -33,6 +33,7 @@ typedef std::list<uint32> SimpleFactionsList;
SimpleFactionsList const* GetFactionTeamList(uint32 faction);
char* GetPetName(uint32 petfamily, uint32 dbclang);
uint32 GetTalentSpellCost(uint32 spellId);
uint32 GetTalentSpellCost(TalentSpellPos const* pos);
TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found

View file

@ -1,86 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file
\ingroup world
*/
#include "Log.h"
#include "Database/DatabaseEnv.h"
#include "Database/DatabaseImpl.h"
#include "Platform/Define.h"
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "GlobalEvents.h"
#include "ObjectGuid.h"
#include "Corpse.h"
static void CorpsesEraseCallBack(QueryResult *result, bool bones)
{
if(!result)
return;
do
{
Field *fields = result->Fetch();
uint32 guidlow = fields[0].GetUInt32();
float positionX = fields[1].GetFloat();
float positionY = fields[2].GetFloat();
uint32 mapid = fields[3].GetUInt32();
uint64 player_guid = MAKE_NEW_GUID(fields[4].GetUInt32(), 0, HIGHGUID_PLAYER);
uint64 guid = MAKE_NEW_GUID(guidlow, 0, HIGHGUID_CORPSE);
sLog.outDebug("[Global event] Removing %s %u (X:%f Y:%f Map:%u).",(bones?"bones":"corpse"),guidlow,positionX,positionY,mapid);
/// Resurrectable - convert corpses to bones
if(!bones)
{
if(!sObjectAccessor.ConvertCorpseForPlayer(player_guid))
{
sLog.outDebug("Corpse %u not found in world or bones creating forbidden. Delete from DB.",guidlow);
CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow);
}
}
else
///- or delete bones
{
sMapMgr.RemoveBonesFromMap(mapid, guid, positionX, positionY);
///- remove bones from the database
CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow);
}
} while (result->NextRow());
delete result;
}
/// Handle periodic erase of corpses and bones
static void CorpsesErase(bool bones,uint32 delay)
{
///- Get the list of eligible corpses/bones to be removed
//No SQL injection (uint32 and enum)
CharacterDatabase.AsyncPQuery(&CorpsesEraseCallBack, bones, "SELECT guid,position_x,position_y,map,player FROM corpse WHERE time < (UNIX_TIMESTAMP()+'%u') AND corpse_type %s '0'", delay, (bones ? "=" : "<>"));
}
/// not thread guarded variant for call from other thread
void CorpsesErase()
{
CorpsesErase(true, 20*MINUTE);
CorpsesErase(false,3*DAY);
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/// \addtogroup world
/// @{
/// \file
#ifndef __GLOBALEVENTS_H
#define __GLOBALEVENTS_H
void CorpsesErase();
void HandleCorpsesErase(void*);
#endif
/// @}

View file

@ -819,7 +819,9 @@ enum MangosStrings
LANG_MOVEGENS_FOLLOW_PLAYER = 1132,
LANG_MOVEGENS_FOLLOW_CREATURE = 1133,
LANG_MOVEGENS_FOLLOW_NULL = 1134,
// Room for more level 3 1135-1199 not used
LANG_LIST_TALENTS_TITLE = 1135,
LANG_LIST_TALENTS_COUNT = 1136,
// Room for more level 3 1137-1199 not used
// Debug commands
LANG_CINEMATIC_NOT_EXIST = 1200,

View file

@ -41,7 +41,6 @@
#include <iostream>
#include <fstream>
#include <map>
#include "GlobalEvents.h"
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
@ -832,7 +831,7 @@ bool ChatHandler::HandleGameObjectNearCommand(const char* args)
if(!gInfo)
continue;
PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid);
PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name, x, y, z, mapid);
++count;
} while (result->NextRow());
@ -4200,7 +4199,7 @@ bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit)
/// Triggering corpses expire check in world
bool ChatHandler::HandleServerCorpsesCommand(const char* /*args*/)
{
CorpsesErase();
sObjectAccessor.RemoveOldCorpses();
return true;
}

View file

@ -1953,6 +1953,8 @@ bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/)
player->learnSpellHighRank(spellid);
}
player->SendTalentsInfoData(false);
SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
return true;
}
@ -2029,6 +2031,8 @@ bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
pet->learnSpellHighRank(spellid);
}
player->SendTalentsInfoData(true);
SendSysMessage(LANG_COMMAND_LEARN_PET_TALENTS);
return true;
}
@ -2860,6 +2864,53 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args)
return true;
}
void ChatHandler::ShowSpellListHelper(Player* target, SpellEntry const* spellInfo, LocaleConstant loc)
{
uint32 id = spellInfo->Id;
bool known = target && target->HasSpell(id);
bool learn = (spellInfo->Effect[EFFECT_INDEX_0] == SPELL_EFFECT_LEARN_SPELL);
uint32 talentCost = GetTalentSpellCost(id);
bool talent = (talentCost > 0);
bool passive = IsPassiveSpell(id);
bool active = target && target->HasAura(id);
// unit32 used to prevent interpreting uint8 as char at output
// find rank of learned spell for learning spell, or talent rank
uint32 rank = talentCost ? talentCost : sSpellMgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[EFFECT_INDEX_0] : id);
// send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
std::ostringstream ss;
if (m_session)
ss << id << " - |cffffffff|Hspell:" << id << "|h[" << spellInfo->SpellName[loc];
else
ss << id << " - " << spellInfo->SpellName[loc];
// include rank in link name
if(rank)
ss << GetMangosString(LANG_SPELL_RANK) << rank;
if (m_session)
ss << " " << localeNames[loc] << "]|h|r";
else
ss << " " << localeNames[loc];
if(talent)
ss << GetMangosString(LANG_TALENT);
if(passive)
ss << GetMangosString(LANG_PASSIVE);
if(learn)
ss << GetMangosString(LANG_LEARN);
if(known)
ss << GetMangosString(LANG_KNOWN);
if(active)
ss << GetMangosString(LANG_ACTIVE);
SendSysMessage(ss.str().c_str());
}
bool ChatHandler::HandleLookupSpellCommand(const char* args)
{
if(!*args)
@ -2909,48 +2960,7 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args)
if(loc < MAX_LOCALE)
{
bool known = target && target->HasSpell(id);
bool learn = (spellInfo->Effect[EFFECT_INDEX_0] == SPELL_EFFECT_LEARN_SPELL);
uint32 talentCost = GetTalentSpellCost(id);
bool talent = (talentCost > 0);
bool passive = IsPassiveSpell(id);
bool active = target && target->HasAura(id);
// unit32 used to prevent interpreting uint8 as char at output
// find rank of learned spell for learning spell, or talent rank
uint32 rank = talentCost ? talentCost : sSpellMgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[EFFECT_INDEX_0] : id);
// send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
std::ostringstream ss;
if (m_session)
ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
else
ss << id << " - " << name;
// include rank in link name
if(rank)
ss << GetMangosString(LANG_SPELL_RANK) << rank;
if (m_session)
ss << " " << localeNames[loc] << "]|h|r";
else
ss << " " << localeNames[loc];
if(talent)
ss << GetMangosString(LANG_TALENT);
if(passive)
ss << GetMangosString(LANG_PASSIVE);
if(learn)
ss << GetMangosString(LANG_LEARN);
if(known)
ss << GetMangosString(LANG_KNOWN);
if(active)
ss << GetMangosString(LANG_ACTIVE);
SendSysMessage(ss.str().c_str());
ShowSpellListHelper(target, spellInfo, LocaleConstant(loc));
++counter;
}
}
@ -4342,6 +4352,43 @@ bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
return true;
}
bool ChatHandler::HandleListTalentsCommand (const char * /*args*/)
{
Player *player = getSelectedPlayer();
if (!player)
{
SendSysMessage(LANG_NO_CHAR_SELECTED);
SetSentErrorMessage(true);
return false;
}
SendSysMessage(LANG_LIST_TALENTS_TITLE);
uint32 count = 0;
uint32 cost = 0;
PlayerSpellMap const& uSpells = player->GetSpellMap();
for (PlayerSpellMap::const_iterator itr = uSpells.begin(); itr != uSpells.end(); ++itr)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled)
continue;
uint32 cost_itr = GetTalentSpellCost(itr->first);
if (cost_itr == 0)
continue;
SpellEntry const* spellEntry = sSpellStore.LookupEntry(itr->first);
if (!spellEntry)
continue;
ShowSpellListHelper(player, spellEntry, GetSessionDbcLocale());
++count;
cost += cost_itr;
}
PSendSysMessage(LANG_LIST_TALENTS_COUNT, count, cost);
return true;
}
bool ChatHandler::HandleResetAchievementsCommand (const char * args)
{
Player* target;

View file

@ -124,8 +124,6 @@ libmangosgame_a_SOURCES = \
GameEventMgr.h \
GameObject.cpp \
GameObject.h \
GlobalEvents.cpp \
GlobalEvents.h \
GMTicketHandler.cpp \
GMTicketMgr.cpp \
GMTicketMgr.h \

View file

@ -765,42 +765,6 @@ void Map::Remove(Player *player, bool remove)
DeleteFromWorld(player);
}
bool Map::RemoveBones(uint64 guid, float x, float y)
{
if (IsRemovalGrid(x, y))
{
Corpse* corpse = ObjectAccessor::GetCorpseInMap(guid,GetId());
if (!corpse)
return false;
CellPair p = MaNGOS::ComputeCellPair(x,y);
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
{
sLog.outError("Map::RemoveBones: invalid coordinates supplied X:%f Y:%f grid cell [%u:%u]", x, y, p.x_coord, p.y_coord);
return false;
}
CellPair q = MaNGOS::ComputeCellPair(corpse->GetPositionX(),corpse->GetPositionY());
if(q.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || q.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
{
sLog.outError("Map::RemoveBones: object (GUID: %u TypeId: %u) has invalid coordinates X:%f Y:%f grid cell [%u:%u]", corpse->GetGUIDLow(), corpse->GetTypeId(), corpse->GetPositionX(), corpse->GetPositionY(), q.x_coord, q.y_coord);
return false;
}
int32 dx = int32(p.x_coord) - int32(q.x_coord);
int32 dy = int32(p.y_coord) - int32(q.y_coord);
if (dx <= -2 || dx >= 2 || dy <= -2 || dy >= 2)
return false;
if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES)
corpse->DeleteBonesFromWorld();
else
return false;
}
return true;
}
template<class T>
void
Map::Remove(T *obj, bool remove)

View file

@ -361,8 +361,6 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
void AddObjectToRemoveList(WorldObject *obj);
virtual bool RemoveBones(uint64 guid, float x, float y);
void UpdateObjectVisibility(WorldObject* obj, Cell cell, CellPair cellpair);
void UpdatePlayerVisibility(Player* player, Cell cell, CellPair cellpair);
void UpdateObjectsVisibilityFor(Player* player, Cell cell, CellPair cellpair);

View file

@ -76,18 +76,6 @@ void MapInstanced::RemoveAllObjectsInRemoveList()
Map::RemoveAllObjectsInRemoveList();
}
bool MapInstanced::RemoveBones(uint64 guid, float x, float y)
{
bool remove_result = false;
for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
{
remove_result = remove_result || i->second->RemoveBones(guid, x, y);
}
return remove_result || Map::RemoveBones(guid,x,y);
}
void MapInstanced::UnloadAll(bool pForce)
{
// Unload instanced maps

View file

@ -35,7 +35,6 @@ class MANGOS_DLL_DECL MapInstanced : public Map
// functions overwrite Map versions
void Update(const uint32&);
void RemoveAllObjectsInRemoveList();
bool RemoveBones(uint64 guid, float x, float y);
void UnloadAll(bool pForce);
Map* CreateInstance(const uint32 mapId, Player * player);

View file

@ -249,16 +249,6 @@ void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId)
((MapInstanced*)m)->DestroyInstance(instanceId);
}
void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y)
{
bool remove_result = _createBaseMap(mapid)->RemoveBones(guid, x, y);
if (!remove_result)
{
sLog.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid));
}
}
void
MapManager::Update(uint32 diff)
{

View file

@ -121,7 +121,6 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
TransportMap m_TransportsByMap;
bool CanPlayerEnter(uint32 mapid, Player* player);
void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y);
uint32 GenerateInstanceId() { return ++i_MaxInstanceId; }
void InitMaxInstanceId();
void InitializeVisibilityDistanceInfo();

View file

@ -258,6 +258,22 @@ ObjectAccessor::ConvertCorpseForPlayer(ObjectGuid player_guid, bool insignia)
return bones;
}
void ObjectAccessor::RemoveOldCorpses()
{
time_t now = time(NULL);
Player2CorpsesMapType::iterator next;
for(Player2CorpsesMapType::iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); itr = next)
{
next = itr;
++next;
if(!itr->second->IsExpired(now))
continue;
ConvertCorpseForPlayer(itr->first);
}
}
/// Define the static member of HashMapHolder
template <class T> UNORDERED_MAP< uint64, T* > HashMapHolder<T>::m_objectMap;

View file

@ -117,6 +117,7 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor,
void AddCorpse(Corpse* corpse);
void AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map);
Corpse* ConvertCorpseForPlayer(ObjectGuid player_guid, bool insignia = false);
void RemoveOldCorpses();
// For call from Player/Corpse AddToWorld/RemoveFromWorld only
void AddObject(Corpse *object) { HashMapHolder<Corpse>::Insert(object); }

View file

@ -6450,8 +6450,8 @@ void ObjectMgr::LoadQuestPOI()
uint32 count = 0;
// 0 1 2 3 4 5 6
QueryResult *result = WorldDatabase.Query("SELECT questId, objIndex, mapId, unk1, unk2, unk3, unk4 FROM quest_poi");
// 0 1 2 3 4 5 6 7
QueryResult *result = WorldDatabase.Query("SELECT questId, poiId, objIndex, mapId, mapAreaId, floorId, unk3, unk4 FROM quest_poi");
if(!result)
{
@ -6471,31 +6471,16 @@ void ObjectMgr::LoadQuestPOI()
Field *fields = result->Fetch();
bar.step();
uint32 questId = fields[0].GetUInt32();
int32 objIndex = fields[1].GetInt32();
uint32 mapId = fields[2].GetUInt32();
uint32 unk1 = fields[3].GetUInt32();
uint32 unk2 = fields[4].GetUInt32();
uint32 unk3 = fields[5].GetUInt32();
uint32 unk4 = fields[6].GetUInt32();
uint32 questId = fields[0].GetUInt32();
uint32 poiId = fields[1].GetUInt32();
int32 objIndex = fields[2].GetInt32();
uint32 mapId = fields[3].GetUInt32();
uint32 mapAreaId = fields[4].GetUInt32();
uint32 floorId = fields[5].GetUInt32();
uint32 unk3 = fields[6].GetUInt32();
uint32 unk4 = fields[7].GetUInt32();
QuestPOI POI(objIndex, mapId, unk1, unk2, unk3, unk4);
QueryResult *points = WorldDatabase.PQuery("SELECT x, y FROM quest_poi_points WHERE questId='%u' AND objIndex='%i'", questId, objIndex);
if(points)
{
do
{
Field *pointFields = points->Fetch();
int32 x = pointFields[0].GetInt32();
int32 y = pointFields[1].GetInt32();
QuestPOIPoint point(x, y);
POI.points.push_back(point);
} while (points->NextRow());
delete points;
}
QuestPOI POI(poiId, objIndex, mapId, mapAreaId, floorId, unk3, unk4);
mQuestPOIMap[questId].push_back(POI);
@ -6504,6 +6489,35 @@ void ObjectMgr::LoadQuestPOI()
delete result;
QueryResult *points = WorldDatabase.Query("SELECT questId, poiId, x, y FROM quest_poi_points");
if (points)
{
do
{
Field *pointFields = points->Fetch();
uint32 questId = pointFields[0].GetUInt32();
uint32 poiId = pointFields[1].GetUInt32();
int32 x = pointFields[2].GetInt32();
int32 y = pointFields[3].GetInt32();
QuestPOIVector& vect = mQuestPOIMap[questId];
for(QuestPOIVector::iterator itr = vect.begin(); itr != vect.end(); ++itr)
{
if (itr->PoiId != poiId)
continue;
QuestPOIPoint point(x, y);
itr->points.push_back(point);
break;
}
} while (points->NextRow());
delete points;
}
sLog.outString();
sLog.outString(">> Loaded %u quest POI definitions", count);
}

View file

@ -259,16 +259,17 @@ struct QuestPOIPoint
struct QuestPOI
{
int32 ObjectiveIndex;
uint32 PoiId;
int32 ObjectiveIndex;
uint32 MapId;
uint32 Unk1;
uint32 Unk2;
uint32 MapAreaId;
uint32 FloorId;
uint32 Unk3;
uint32 Unk4;
std::vector<QuestPOIPoint> points;
QuestPOI() : ObjectiveIndex(0), MapId(0), Unk1(0), Unk2(0), Unk3(0), Unk4(0) {}
QuestPOI(int32 objIndex, uint32 mapId, uint32 unk1, uint32 unk2, uint32 unk3, uint32 unk4) : ObjectiveIndex(objIndex), MapId(mapId), Unk1(unk1), Unk2(unk2), Unk3(unk3), Unk4(unk4) {}
QuestPOI() : PoiId(0), ObjectiveIndex(0), MapId(0), MapAreaId(0), FloorId(0), Unk3(0), Unk4(0) {}
QuestPOI(uint32 poiId, int32 objIndex, uint32 mapId, uint32 mapAreaId, uint32 floorId, uint32 unk3, uint32 unk4) : PoiId(poiId), ObjectiveIndex(objIndex), MapId(mapId), MapAreaId(mapAreaId), FloorId(floorId), Unk3(unk3), Unk4(unk4) {}
};
typedef std::vector<QuestPOI> QuestPOIVector;

View file

@ -1380,11 +1380,8 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
uint32 talentCost = GetTalentSpellCost(spell_id);
if (talentCost)
{
int32 free_points = GetMaxTalentPointsForLevel(getLevel());
m_usedTalentCount+=talentCost;
// update free talent points
free_points-=m_usedTalentCount;
SetFreeTalentPoints(free_points > 0 ? free_points : 0);
UpdateFreeTalentPoints(false);
}
return true;
}
@ -1493,9 +1490,8 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
m_usedTalentCount-=talentCost;
else
m_usedTalentCount = 0;
// update free talent points
int32 free_points = GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount;
SetFreeTalentPoints(free_points > 0 ? free_points : 0);
UpdateFreeTalentPoints(false);
}
if (learn_prev)
@ -1566,7 +1562,7 @@ bool Pet::resetTalents(bool no_cost)
if (m_usedTalentCount == 0)
{
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false); // for fix if need counter
return false;
}
@ -1599,31 +1595,11 @@ bool Pet::resetTalents(bool no_cost)
continue;
for (int j = 0; j < MAX_TALENT_RANK; j++)
{
for(PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();)
{
if(itr->second.state == PETSPELL_REMOVED)
{
++itr;
continue;
}
// remove learned spells (all ranks)
uint32 itrFirstId = sSpellMgr.GetFirstSpellInChain(itr->first);
// unlearn if first rank is talent or learned by talent
if (itrFirstId == talentInfo->RankID[j] || sSpellMgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
{
removeSpell(itr->first,false);
itr = m_spells.begin();
continue;
}
else
++itr;
}
}
if (talentInfo->RankID[j])
removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false);
}
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false);
if(!no_cost)
{
@ -1720,17 +1696,33 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= NULL*/)
CharacterDatabase.Execute(ss.str().c_str());
}
void Pet::InitTalentForLevel()
void Pet::UpdateFreeTalentPoints(bool resetIfNeed)
{
uint32 level = getLevel();
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
// Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent)
if(talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel)
if (talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel)
{
// Remove all talent points
resetTalents(true);
// Remove all talent points (except for admin pets)
if (resetIfNeed)
{
Unit *owner = GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER || ((Player*)owner)->GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
resetTalents(true);
else
SetFreeTalentPoints(0);
}
else
SetFreeTalentPoints(0);
}
SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
else
SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
}
void Pet::InitTalentForLevel()
{
UpdateFreeTalentPoints();
Unit *owner = GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER)

View file

@ -230,6 +230,7 @@ class Pet : public Creature
uint8 GetMaxTalentPointsForLevel(uint32 level);
uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }
void UpdateFreeTalentPoints(bool resetIfNeed = true);
uint32 m_resetTalentsCost;
time_t m_resetTalentsTime;

View file

@ -2533,16 +2533,17 @@ void Player::GiveLevel(uint32 level)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
}
void Player::InitTalentForLevel()
void Player::UpdateFreeTalentPoints(bool resetIfNeed)
{
uint32 level = getLevel();
// talents base at level diff ( talents = level - 9 but some can be used already)
if(level < 10)
if (level < 10)
{
// Remove all talent points
if(m_usedTalentCount > 0) // Free any used talents
if (m_usedTalentCount > 0) // Free any used talents
{
resetTalents(true);
if (resetIfNeed)
resetTalents(true);
SetFreeTalentPoints(0);
}
}
@ -2551,9 +2552,9 @@ void Player::InitTalentForLevel()
uint32 talentPointsForLevel = CalculateTalentsPoints();
// if used more that have then reset
if(m_usedTalentCount > talentPointsForLevel)
if (m_usedTalentCount > talentPointsForLevel)
{
if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
if (resetIfNeed && GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
resetTalents(true);
else
SetFreeTalentPoints(0);
@ -2562,8 +2563,13 @@ void Player::InitTalentForLevel()
else
SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
}
}
if(!GetSession()->PlayerLoading())
void Player::InitTalentForLevel()
{
UpdateFreeTalentPoints();
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(false); // update at client
}
@ -3011,10 +3017,12 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
}
TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
{
// talent: unlearn all other talent ranks (high and low)
if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
if (talentPos)
{
if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
{
@ -3100,11 +3108,45 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
return false;
}
uint32 talentCost = GetTalentSpellCost(spell_id);
if (talentPos)
{
// update talent map
PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
if (iter != m_talents[m_activeSpec].end())
{
// check if ranks different or removed
if ((*iter).second.state == PLAYERSPELL_REMOVED || talentPos->rank != (*iter).second.currentRank)
{
(*iter).second.currentRank = talentPos->rank;
if ((*iter).second.state != PLAYERSPELL_NEW)
(*iter).second.state = PLAYERSPELL_CHANGED;
}
}
else
{
PlayerTalent talent;
talent.currentRank = talentPos->rank;
talent.m_talentEntry = sTalentStore.LookupEntry(talentPos->talent_id);
talent.state = IsInWorld() ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
m_talents[m_activeSpec][talentPos->talent_id] = talent;
}
// update used talent points count
m_usedTalentCount += GetTalentSpellCost(talentPos);
UpdateFreeTalentPoints(false);
}
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
SetFreePrimaryProfessions(freeProfs-1);
}
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
if (talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL))
if (talentPos && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL))
{
// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
CastSpell(this, spell_id, true);
@ -3121,16 +3163,6 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
return false;
}
// update used talent points count
m_usedTalentCount += talentCost;
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if(sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id))
SetFreePrimaryProfessions(freeProfs-1);
}
// add dependent skills
uint16 maxskill = GetMaxSkillValueForLevel();
@ -3275,7 +3307,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
// re-search, it can be corrupted in prev loop
itr = m_spells.find(spell_id);
if (itr == m_spells.end())
if (itr == m_spells.end() || itr->second.state == PLAYERSPELL_REMOVED)
return; // already unleared
bool cur_active = itr->second.active;
@ -3302,14 +3334,30 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
if(PetAura const* petSpell = sSpellMgr.GetPetAura(spell_id, SpellEffectIndex(i)))
RemovePetAura(petSpell);
// free talent points
uint32 talentCosts = GetTalentSpellCost(spell_id);
if(talentCosts > 0)
TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id);
if (talentPos)
{
// update talent map
PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id);
if (iter != m_talents[m_activeSpec].end())
{
if ((*iter).second.state != PLAYERSPELL_NEW)
(*iter).second.state = PLAYERSPELL_REMOVED;
else
m_talents[m_activeSpec].erase(iter);
}
else
sLog.outError("removeSpell: Player (GUID: %u) has talent spell (id: %u) but doesn't have talent",GetGUIDLow(), spell_id );
// free talent points
uint32 talentCosts = GetTalentSpellCost(talentPos);
if(talentCosts < m_usedTalentCount)
m_usedTalentCount -= talentCosts;
else
m_usedTalentCount = 0;
UpdateFreeTalentPoints(false);
}
// update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
@ -3397,7 +3445,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
// if talent then lesser rank also talent and need learn
if (talentCosts)
if (talentPos)
{
if(learn_low_rank)
learnSpell(prev_id, false);
@ -3433,6 +3481,17 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo
}
}
if (m_canTitanGrip)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_TITAN_GRIP))
{
m_canTitanGrip = false;
if(sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET))
AutoUnequipOffhandIfNeed();
}
}
// remove from spell book if not replaced by lesser rank
if (!prev_activate && sendUpdate)
{
@ -3613,11 +3672,9 @@ bool Player::resetTalents(bool no_cost)
if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true);
uint32 talentPointsForLevel = CalculateTalentsPoints();
if (m_usedTalentCount == 0)
{
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false); // for fix if need counter
return false;
}
@ -3634,56 +3691,46 @@ bool Player::resetTalents(bool no_cost)
}
}
for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
for (PlayerTalentMap::iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end();)
{
TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
if (iter->second.state == PLAYERSPELL_REMOVED)
{
++iter;
continue;
}
if (!talentInfo) continue;
TalentEntry const *talentInfo = (*iter).second.m_talentEntry;
if (!talentInfo)
{
iter = m_talents[m_activeSpec].erase(iter);
continue;
}
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
if(!talentTabInfo)
if (!talentTabInfo)
{
iter = m_talents[m_activeSpec].erase(iter);
continue;
}
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
{
++iter;
continue;
}
for (int j = 0; j < MAX_TALENT_RANK; ++j)
{
for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
{
if(itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled)
{
++itr;
continue;
}
if (talentInfo->RankID[j])
removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false);
// remove learned spells (all ranks)
uint32 itrFirstId = sSpellMgr.GetFirstSpellInChain(itr->first);
// unlearn if first rank is talent or learned by talent
if (itrFirstId == talentInfo->RankID[j])
{
removeSpell(itr->first,!IsPassiveSpell(itr->first),false);
itr = GetSpellMap().begin();
continue;
}
else if (sSpellMgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
{
removeSpell(itr->first,!IsPassiveSpell(itr->first));
itr = GetSpellMap().begin();
continue;
}
else
++itr;
}
}
iter = m_talents[m_activeSpec].begin();
}
SetFreeTalentPoints(talentPointsForLevel);
UpdateFreeTalentPoints(false);
if(!no_cost)
{
@ -3704,15 +3751,6 @@ bool Player::resetTalents(bool no_cost)
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
}
*/
if(m_canTitanGrip)
{
m_canTitanGrip = false;
if(sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET))
AutoUnequipOffhandIfNeed();
}
return true;
}
@ -4120,6 +4158,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
@ -4318,7 +4357,7 @@ Corpse* Player::CreateCorpse()
Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
SetPvPDeath(false);
if(!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this))
if (!corpse->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_CORPSE), this))
{
delete corpse;
return NULL;
@ -4342,11 +4381,11 @@ Corpse* Player::CreateCorpse()
corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
uint32 flags = CORPSE_FLAG_UNK2;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
flags |= CORPSE_FLAG_HIDE_HELM;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
flags |= CORPSE_FLAG_HIDE_CLOAK;
if(InBattleGround() && !InArena())
if (InBattleGround() && !InArena())
flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
@ -4359,7 +4398,7 @@ Corpse* Player::CreateCorpse()
uint32 _cfi;
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
if(m_items[i])
if (m_items[i])
{
iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
iIventoryType = m_items[i]->GetProto()->InventoryType;
@ -4369,10 +4408,8 @@ Corpse* Player::CreateCorpse()
}
}
// we don't SaveToDB for players in battlegrounds so don't do it for corpses either
const MapEntry *entry = sMapStore.LookupEntry(corpse->GetMapId());
ASSERT(entry);
if(entry->map_type != MAP_BATTLEGROUND)
// we not need saved corpses for BG/arenas
if (!GetMap()->IsBattleGroundOrArena())
corpse->SaveToDB();
// register for player, but not show
@ -5664,10 +5701,10 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const
void Player::SendInitialActionButtons() const
{
sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
sLog.outDetail( "Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec);
WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
data << uint8(1); // can be 0, 1, 2 (talent spec)
data << uint8(1); // talent spec amount (in packet)
ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button)
{
@ -5679,7 +5716,7 @@ void Player::SendInitialActionButtons() const
}
GetSession()->SendPacket( &data );
sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
sLog.outDetail( "Action Buttons for '%u' spec '%u' Initialized", GetGUIDLow(), m_activeSpec );
}
bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg)
@ -15114,6 +15151,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
_LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
_LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
_LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
// after spell and quest load
InitTalentForLevel();
learnDefaultSpells();
@ -15906,7 +15945,17 @@ void Player::_LoadSpells(QueryResult *result)
{
Field *fields = result->Fetch();
addSpell(fields[0].GetUInt32(), fields[1].GetBool(), false, false, fields[2].GetBool());
uint32 spell_id = fields[0].GetUInt32();
// skip talents & drop unneeded data
if(GetTalentSpellPos(spell_id))
{
sLog.outError("Player::_LoadSpells: Player (GUID: %u) has talent spell in character_spell, removing it.", GetGUIDLow(), spell_id);
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id);
continue;
}
addSpell(spell_id, fields[1].GetBool(), false, false, fields[2].GetBool());
}
while( result->NextRow() );
@ -15914,6 +15963,82 @@ void Player::_LoadSpells(QueryResult *result)
}
}
void Player::_LoadTalents(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'",GetGUIDLow());
if (result)
{
do
{
Field *fields = result->Fetch();
uint32 talent_id = fields[0].GetUInt32();
TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id );
if (!talentInfo)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent_id: %u , this talent will be deleted from character_talent",GetGUIDLow(), talent_id );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
continue;
}
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
if (!talentTabInfo)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talentTabInfo: %u for talentID: %u , this talent will be deleted from character_talent",GetGUIDLow(), talentInfo->TalentTab, talentInfo->TalentID );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id);
continue;
}
// prevent load talent for different class (cheating)
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has talent with ClassMask: %u , but Player's ClassMask is: %u , talentID: %u , this talent will be deleted from character_talent",GetGUIDLow(), talentTabInfo->ClassMask, getClassMask() ,talentInfo->TalentID );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
continue;
}
uint32 currentRank = fields[1].GetUInt32();
if (currentRank > MAX_TALENT_RANK || talentInfo->RankID[currentRank] == 0)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent rank: %u , talentID: %u , this talent will be deleted from character_talent",GetGUIDLow(), currentRank, talentInfo->TalentID );
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id);
continue;
}
uint32 spec = fields[2].GetUInt32();
if (spec > MAX_TALENT_SPEC_COUNT)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u, spec will be deleted from character_talent", GetGUIDLow(), spec);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spec = '%u' ", spec);
continue;
}
if (spec >= m_specsCount)
{
sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u , this spec will be deleted from character_talent.", GetGUIDLow(), spec);
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND spec = '%u' ", GetGUIDLow(), spec);
continue;
}
if (m_activeSpec == spec)
addSpell(talentInfo->RankID[currentRank], true,false,false,false);
else
{
PlayerTalent talent;
talent.currentRank = currentRank;
talent.m_talentEntry = talentInfo;
talent.state = PLAYERSPELL_UNCHANGED;
m_talents[spec][talentInfo->TalentID] = talent;
}
}
while (result->NextRow());
delete result;
}
}
void Player::_LoadGroup(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
@ -16405,6 +16530,7 @@ void Player::SaveToDB()
_SaveEquipmentSets();
GetSession()->SaveTutorialsData(); // changed only while character in game
_SaveGlyphs();
_SaveTalents();
CharacterDatabase.CommitTransaction();
@ -16750,12 +16876,17 @@ void Player::_SaveSpells()
{
for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
uint32 talentCosts = GetTalentSpellCost(itr->first);
// add only changed/new not dependent spells
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.active ? 1 : 0,itr->second.disabled ? 1 : 0);
if (!talentCosts)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
// add only changed/new not dependent spells
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.active ? 1 : 0,itr->second.disabled ? 1 : 0);
}
if (itr->second.state == PLAYERSPELL_REMOVED)
m_spells.erase(itr++);
@ -16768,6 +16899,30 @@ void Player::_SaveSpells()
}
}
void Player::_SaveTalents()
{
for (int32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
{
for (PlayerTalentMap::iterator itr = m_talents[i].begin(); itr != m_talents[i].end();)
{
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and talent_id = '%u' and spec = '%u'", GetGUIDLow(),itr->first, i);
// add only changed/new talents
if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.currentRank, i);
if (itr->second.state == PLAYERSPELL_REMOVED)
m_talents[i].erase(itr++);
else
{
itr->second.state = PLAYERSPELL_UNCHANGED;
++itr;
}
}
}
}
void Player::outDebugValues() const
{
if(!sLog.IsOutDebug()) // optimize disabled debug output
@ -20839,14 +20994,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
// find current max talent rank
uint32 curtalent_maxrank = 0;
for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
{
if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
{
curtalent_maxrank = k + 1;
break;
}
}
PlayerTalentMap::iterator itr = m_talents[m_activeSpec].find(talentId);
if (itr != m_talents[m_activeSpec].end() && itr->second.state != PLAYERSPELL_REMOVED)
curtalent_maxrank = itr->second.currentRank + 1;
// we already have same or higher talent rank learned
if(curtalent_maxrank >= (talentRank + 1))
@ -20862,12 +21012,14 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
{
bool hasEnoughRank = false;
for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i)
PlayerTalentMap::iterator dependsOnTalent = m_talents[m_activeSpec].find(depTalentInfo->TalentID);
if (dependsOnTalent != m_talents[m_activeSpec].end() && dependsOnTalent->second.state != PLAYERSPELL_REMOVED)
{
if (depTalentInfo->RankID[i] != 0)
if (HasSpell(depTalentInfo->RankID[i]))
hasEnoughRank = true;
PlayerTalent depTalent = (*dependsOnTalent).second;
if (depTalent.currentRank >= talentInfo->DependsOnRank)
hasEnoughRank = true;
}
if (!hasEnoughRank)
return;
}
@ -20879,28 +21031,9 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
uint32 tTab = talentInfo->TalentTab;
if (talentInfo->Row > 0)
{
unsigned int numRows = sTalentStore.GetNumRows();
for (unsigned int i = 0; i < numRows; ++i) // Loop through all talents.
{
// Someday, someone needs to revamp
const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
if (tmpTalent) // the way talents are tracked
{
if (tmpTalent->TalentTab == tTab)
{
for (int j = 0; j < MAX_TALENT_RANK; ++j)
{
if (tmpTalent->RankID[j] != 0)
{
if (HasSpell(tmpTalent->RankID[j]))
{
spentPoints += j + 1;
}
}
}
}
}
}
for (PlayerTalentMap::const_iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end(); ++iter)
if (iter->second.state != PLAYERSPELL_REMOVED && iter->second.m_talentEntry->TalentTab == tTab)
spentPoints += iter->second.currentRank + 1;
}
// not have required min points spent in talent tree
@ -20922,9 +21055,6 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
// learn! (other talent ranks will unlearned at learning)
learnSpell(spellid, false);
sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
// update free talent points
SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
@ -21057,9 +21187,6 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
// learn! (other talent ranks will unlearned at learning)
pet->learnSpell(spellid);
sLog.outDetail("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
// update free talent points
pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
void Player::UpdateKnownCurrencies(uint32 itemId, bool apply)
@ -21147,34 +21274,19 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
for(uint32 i = 0; i < 3; ++i)
{
uint32 talentTabId = talentTabIds[i];
for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
for(PlayerTalentMap::iterator iter = m_talents[specIdx].begin(); iter != m_talents[specIdx].end(); ++iter)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if(!talentInfo)
PlayerTalent talent = (*iter).second;
if (talent.state == PLAYERSPELL_REMOVED)
continue;
// skip another tab talents
if(talentInfo->TalentTab != talentTabId)
if(talent.m_talentEntry->TalentTab != talentTabId)
continue;
// find max talent rank
int32 curtalent_maxrank = -1;
for(int32 k = MAX_TALENT_RANK-1; k > -1; --k)
{
if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k]))
{
curtalent_maxrank = k;
break;
}
}
// not learned talent
if(curtalent_maxrank < 0)
continue;
*data << uint32(talentInfo->TalentID); // Talent.dbc
*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
*data << uint32(talent.m_talentEntry->TalentID); // Talent.dbc
*data << uint8(talent.currentRank); // talentMaxRank (0-4)
++talentIdCount;
}
@ -21449,13 +21561,75 @@ void Player::ActivateSpec(uint8 specNum)
if(specNum >= GetSpecsCount())
return;
// unlearn GetActiveSpec() talents (not learned in specNum);
// learn specNum talents
UnsummonPetTemporaryIfAny();
ApplyGlyphs(false);
// copy of new talent spec (we will use it as model for converting current tlanet state to new)
PlayerTalentMap tempSpec = m_talents[specNum];
// copy old spec talents to new one, must be before spec switch to have previous spec num(as m_activeSpec)
m_talents[specNum] = m_talents[m_activeSpec];
SetActiveSpec(specNum);
// remove all talent spells that don't exist in next spec but exist in old
for (PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].begin(); specIter != m_talents[m_activeSpec].end();)
{
PlayerTalent& talent = (*specIter).second;
if (talent.state == PLAYERSPELL_REMOVED)
{
++specIter;
continue;
}
PlayerTalentMap::iterator iterTempSpec = tempSpec.find(specIter->first);
// remove any talent rank if talent not listed in temp spec
if (iterTempSpec == tempSpec.end() || iterTempSpec->second.state == PLAYERSPELL_REMOVED)
{
for(int r = 0; r < MAX_TALENT_RANK; ++r)
if (talent.m_talentEntry->RankID[r])
removeSpell(talent.m_talentEntry->RankID[r],!IsPassiveSpell(talent.m_talentEntry->RankID[r]),false);
specIter = m_talents[m_activeSpec].begin();
}
else
++specIter;
}
// now new spec data have only talents (maybe different rank) as in temp spec data, sync ranks then.
for (PlayerTalentMap::const_iterator tempIter = tempSpec.begin(); tempIter != tempSpec.end(); ++tempIter)
{
PlayerTalent const& talent = (*tempIter).second;
// removed state talent already unlearned in prev. loop
// but we need restore it if it deleted for finish removed-marked data in DB
if (talent.state == PLAYERSPELL_REMOVED)
{
m_talents[m_activeSpec][tempIter->first] = talent;
continue;
}
// learn talent spells if they not in new spec (old spec copy)
// and if they have different rank
PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].find(tempIter->first);
if (specIter != m_talents[m_activeSpec].end() && specIter->second.state != PLAYERSPELL_REMOVED)
{
if ((*specIter).second.currentRank != talent.currentRank)
learnSpell(talent.m_talentEntry->RankID[talent.currentRank], false);
}
else
learnSpell(talent.m_talentEntry->RankID[talent.currentRank], false);
// sync states - original state is changed in addSpell that learnSpell calls
specIter = m_talents[m_activeSpec].find(tempIter->first);
(*specIter).second.state = talent.state;
}
InitTalentForLevel();
// recheck action buttons (not checked at loading/spec copy)
ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec];
for(ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end(); ++itr)
@ -21464,11 +21638,17 @@ void Player::ActivateSpec(uint8 specNum)
if (!IsActionButtonDataValid(itr->first,itr->second.GetAction(),itr->second.GetType(), this, false))
removeActionButton(m_activeSpec,itr->first);
ResummonPetTemporaryUnSummonedIfAny();
ApplyGlyphs(true);
SendInitialActionButtons();
InitTalentForLevel();
Powers pw = getPowerType();
if(pw != POWER_MANA)
SetPower(POWER_MANA, 0);
SetPower(pw, 0);
}
void Player::UpdateSpecCount(uint8 count)

View file

@ -96,7 +96,15 @@ struct PlayerSpell
bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks
};
struct PlayerTalent
{
PlayerSpellState state;
TalentEntry const *m_talentEntry;
uint32 currentRank;
};
typedef UNORDERED_MAP<uint32, PlayerSpell> PlayerSpellMap;
typedef UNORDERED_MAP<uint32, PlayerTalent> PlayerTalentMap;
// Spell modifier (used for modify other spells)
struct SpellModifier
@ -893,7 +901,8 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOADGLYPHS = 22,
PLAYER_LOGIN_QUERY_LOADMAILS = 23,
PLAYER_LOGIN_QUERY_LOADMAILEDITEMS = 24,
MAX_PLAYER_LOGIN_QUERY = 25
PLAYER_LOGIN_QUERY_LOADTALENTS = 25,
MAX_PLAYER_LOGIN_QUERY = 26
};
enum PlayerDelayedOperations
@ -1548,6 +1557,7 @@ class MANGOS_DLL_SPEC Player : public Unit
uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); }
void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1,points); }
void UpdateFreeTalentPoints(bool resetIfNeed = true);
bool resetTalents(bool no_cost = false);
uint32 resetTalentsCost() const;
void InitTalentForLevel();
@ -2318,6 +2328,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void _LoadGroup(QueryResult *result);
void _LoadSkills(QueryResult *result);
void _LoadSpells(QueryResult *result);
void _LoadTalents(QueryResult *result);
void _LoadFriendList(QueryResult *result);
bool _LoadHomeBind(QueryResult *result);
void _LoadDeclinedNames(QueryResult *result);
@ -2342,6 +2353,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void _SaveEquipmentSets();
void _SaveBGData();
void _SaveGlyphs();
void _SaveTalents();
void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
@ -2393,6 +2405,7 @@ class MANGOS_DLL_SPEC Player : public Unit
PlayerMails m_mail;
PlayerSpellMap m_spells;
PlayerTalentMap m_talents[MAX_TALENT_SPEC_COUNT];
SpellCooldowns m_spellCooldowns;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use

View file

@ -520,14 +520,13 @@ void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
data << uint32(questId); // quest ID
data << uint32(POI->size()); // POI count
int index = 0;
for(QuestPOIVector::const_iterator itr = POI->begin(); itr != POI->end(); ++itr)
{
data << uint32(index); // POI index
data << uint32(itr->PoiId); // POI index
data << int32(itr->ObjectiveIndex); // objective index
data << uint32(itr->MapId); // mapid
data << uint32(itr->Unk1); // unknown
data << uint32(itr->Unk2); // unknown
data << uint32(itr->MapAreaId); // world map area id
data << uint32(itr->FloorId); // floor id
data << uint32(itr->Unk3); // unknown
data << uint32(itr->Unk4); // unknown
data << uint32(itr->points.size()); // POI points count
@ -537,7 +536,6 @@ void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
data << int32(itr2->x); // POI point x
data << int32(itr2->y); // POI point y
}
++index;
}
}
else
@ -553,7 +551,6 @@ void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
}
}
data.hexlike();
SendPacket(&data);
}

View file

@ -51,7 +51,6 @@
#include "BattleGroundMgr.h"
#include "TemporarySummon.h"
#include "VMapFactory.h"
#include "GlobalEvents.h"
#include "GameEventMgr.h"
#include "PoolManager.h"
#include "Database/DatabaseImpl.h"
@ -894,8 +893,8 @@ void World::SetInitialWorldSettings()
uint32 realm_zone = getConfig(CONFIG_UINT32_REALM_ZONE);
loginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID);
///- Remove the bones after a restart
CharacterDatabase.Execute("DELETE FROM corpse WHERE corpse_type = '0'");
///- Remove the bones (they should not exist in DB though) and old corpses after a restart
CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0' OR time < (UNIX_TIMESTAMP()-'%u')", 3*DAY);
///- Load the DBC files
sLog.outString("Initialize data stores...");
@ -1233,8 +1232,7 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILLISECONDS);
m_timers[WUPDATE_UPTIME].SetInterval(m_configUint32Values[CONFIG_UINT32_UPTIME_UPDATE]*MINUTE*IN_MILLISECONDS);
//Update "uptime" table based on configuration entry in minutes.
m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*IN_MILLISECONDS);
//erase corpses every 20 minutes
m_timers[WUPDATE_CORPSES].SetInterval(3*HOUR*IN_MILLISECONDS);
//to set mailtimer to return mails every day between 4 and 5 am
//mailtimer is increased when updating auctions
@ -1417,7 +1415,7 @@ void World::Update(uint32 diff)
{
m_timers[WUPDATE_CORPSES].Reset();
CorpsesErase();
sObjectAccessor.RemoveOldCorpses();
}
///- Process Game events when necessary

View file

@ -49,9 +49,9 @@ ConfVersion=2010030401
RealmID = 1
DataDir = "."
LogsDir = ""
LoginDatabaseInfo = "127.0.0.1;3306;root;mangos;realmd"
WorldDatabaseInfo = "127.0.0.1;3306;root;mangos;mangos"
CharacterDatabaseInfo = "127.0.0.1;3306;root;mangos;characters"
LoginDatabaseInfo = "127.0.0.1;3306;mangos;mangos;realmd"
WorldDatabaseInfo = "127.0.0.1;3306;mangos;mangos;mangos"
CharacterDatabaseInfo = "127.0.0.1;3306;mangos;mangos;characters"
MaxPingTime = 30
WorldServerPort = 8085
BindIP = "0.0.0.0"

View file

@ -96,7 +96,7 @@ ConfVersion=2007062001
#
###################################################################################################################
LoginDatabaseInfo = "127.0.0.1;3306;root;mangos;realmd"
LoginDatabaseInfo = "127.0.0.1;3306;mangos;mangos;realmd"
LogsDir = ""
MaxPingTime = 30
RealmServerPort = 3724

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "9649"
#define REVISION_NR "9664"
#endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_9646_01_characters_characters"
#define REVISION_DB_MANGOS "required_9636_01_mangos_item_template"
#define REVISION_DB_CHARACTERS "required_9661_01_characters_character_talent"
#define REVISION_DB_MANGOS "required_9663_01_mangos_mangos_string"
#define REVISION_DB_REALMD "required_9010_01_realmd_realmlist"
#endif // __REVISION_SQL_H__