From 6750ce9185fdfa9c3be49d154d34e59be4a2e99a Mon Sep 17 00:00:00 2001 From: hunuza Date: Wed, 31 Mar 2010 16:40:13 +0200 Subject: [PATCH 01/15] [9650] Some corpse related clean ups. Old bones remove code anyway was dead, so remove it. Signed-off-by: hunuza --- src/game/Corpse.cpp | 10 ++++- src/game/Corpse.h | 2 + src/game/GlobalEvents.cpp | 86 ------------------------------------- src/game/GlobalEvents.h | 29 ------------- src/game/Level2.cpp | 3 +- src/game/Makefile.am | 2 - src/game/Map.cpp | 36 ---------------- src/game/Map.h | 2 - src/game/MapInstanced.cpp | 12 ------ src/game/MapInstanced.h | 1 - src/game/MapManager.cpp | 10 ----- src/game/MapManager.h | 1 - src/game/ObjectAccessor.cpp | 16 +++++++ src/game/ObjectAccessor.h | 1 + src/game/World.cpp | 10 ++--- src/shared/revision_nr.h | 2 +- win/VC100/game.vcxproj | 2 - win/VC80/game.vcproj | 8 ---- win/VC90/game.vcproj | 8 ---- 19 files changed, 34 insertions(+), 207 deletions(-) delete mode 100644 src/game/GlobalEvents.cpp delete mode 100644 src/game/GlobalEvents.h diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp index e04ca300a..3beb2bda6 100644 --- a/src/game/Corpse.cpp +++ b/src/game/Corpse.cpp @@ -267,4 +267,12 @@ bool Corpse::IsFriendlyTo( Unit const* unit ) const return owner->IsFriendlyTo(unit); else return true; -} \ No newline at end of file +} + +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; +} diff --git a/src/game/Corpse.h b/src/game/Corpse.h index 4cbeb6d79..e01523850 100644 --- a/src/game/Corpse.h +++ b/src/game/Corpse.h @@ -91,6 +91,8 @@ class Corpse : public WorldObject GridReference &GetGridRef() { return m_gridRef; } bool isActiveObject() const { return false; } + + bool IsExpired(time_t t) const; private: GridReference m_gridRef; diff --git a/src/game/GlobalEvents.cpp b/src/game/GlobalEvents.cpp deleted file mode 100644 index 910c68f2b..000000000 --- a/src/game/GlobalEvents.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2005-2010 MaNGOS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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); -} diff --git a/src/game/GlobalEvents.h b/src/game/GlobalEvents.h deleted file mode 100644 index 563375852..000000000 --- a/src/game/GlobalEvents.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2005-2010 MaNGOS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/// \addtogroup world -/// @{ -/// \file - -#ifndef __GLOBALEVENTS_H -#define __GLOBALEVENTS_H - -void CorpsesErase(); -void HandleCorpsesErase(void*); -#endif -/// @} diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 462ae23c1..a10259c89 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -41,7 +41,6 @@ #include #include #include -#include "GlobalEvents.h" #include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand @@ -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; } diff --git a/src/game/Makefile.am b/src/game/Makefile.am index 39d6982e1..abce5db3b 100644 --- a/src/game/Makefile.am +++ b/src/game/Makefile.am @@ -124,8 +124,6 @@ libmangosgame_a_SOURCES = \ GameEventMgr.h \ GameObject.cpp \ GameObject.h \ - GlobalEvents.cpp \ - GlobalEvents.h \ GMTicketHandler.cpp \ GMTicketMgr.cpp \ GMTicketMgr.h \ diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 4586ebc86..0ec96ec2c 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -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 void Map::Remove(T *obj, bool remove) diff --git a/src/game/Map.h b/src/game/Map.h index 30d34dd0b..a73cdee90 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -361,8 +361,6 @@ class MANGOS_DLL_SPEC Map : public GridRefManager, 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); diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp index 19e74eabd..16516413e 100644 --- a/src/game/MapInstanced.cpp +++ b/src/game/MapInstanced.cpp @@ -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 diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h index df2bb9834..79dca285b 100644 --- a/src/game/MapInstanced.h +++ b/src/game/MapInstanced.h @@ -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); diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 3240a05b7..f493605aa 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -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) { diff --git a/src/game/MapManager.h b/src/game/MapManager.h index a72e30185..c07a7c11a 100644 --- a/src/game/MapManager.h +++ b/src/game/MapManager.h @@ -121,7 +121,6 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singletonsecond->IsExpired(now)) + continue; + + ConvertCorpseForPlayer(itr->first); + } +} + /// Define the static member of HashMapHolder template UNORDERED_MAP< uint64, T* > HashMapHolder::m_objectMap; diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h index e704f10fe..2853d6097 100644 --- a/src/game/ObjectAccessor.h +++ b/src/game/ObjectAccessor.h @@ -117,6 +117,7 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton::Insert(object); } diff --git a/src/game/World.cpp b/src/game/World.cpp index 29957cb89..5924b0f0c 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -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 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index fd7738aec..f42f7ecac 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9649" + #define REVISION_NR "9650" #endif // __REVISION_NR_H__ diff --git a/win/VC100/game.vcxproj b/win/VC100/game.vcxproj index 7f9ba21ae..d4f3331f8 100644 --- a/win/VC100/game.vcxproj +++ b/win/VC100/game.vcxproj @@ -394,7 +394,6 @@ - @@ -543,7 +542,6 @@ - diff --git a/win/VC80/game.vcproj b/win/VC80/game.vcproj index 49835dc06..e7f36ea12 100644 --- a/win/VC80/game.vcproj +++ b/win/VC80/game.vcproj @@ -1469,14 +1469,6 @@ RelativePath="..\..\src\game\DBCStructure.h" > - - - - diff --git a/win/VC90/game.vcproj b/win/VC90/game.vcproj index d0dea11a7..db84c571b 100644 --- a/win/VC90/game.vcproj +++ b/win/VC90/game.vcproj @@ -1470,14 +1470,6 @@ RelativePath="..\..\src\game\DBCStructure.h" > - - - - From 09b873a3165b6b3bea8abd18b9e18d1113fe2cf4 Mon Sep 17 00:00:00 2001 From: Blizzy Date: Wed, 31 Mar 2010 20:54:32 +0200 Subject: [PATCH 02/15] [9651] Correct code for quest points of interest. Adjusting field names and type, and send data accordingly to avoid client crash not using auto-generated id. Due to the nature of the primary keys, two tables are truncated. Be sure to make backup if you have data you do now want to loose. In addition, speed up load times of data from quest_poi_points, thanks Hunuza for helping out. --- sql/mangos.sql | 17 +++--- sql/updates/9651_01_mangos_quest_poi.sql | 20 +++++++ sql/updates/Makefile.am | 2 + src/game/ObjectMgr.cpp | 66 ++++++++++++++---------- src/game/ObjectMgr.h | 11 ++-- src/game/QueryHandler.cpp | 9 ++-- src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 8 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 sql/updates/9651_01_mangos_quest_poi.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index ae74d03e8..b01b0c120 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_9636_01_mangos_item_template` bit(1) default NULL + `required_9651_01_mangos_quest_poi` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -13715,14 +13715,15 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `quest_poi`; CREATE TABLE `quest_poi` ( - `questid` int(11) unsigned NOT NULL DEFAULT '0', + `questId` mediumint(8) unsigned NOT NULL DEFAULT '0', + `poiId` tinyint(3) NOT NULL DEFAULT '0', `objIndex` int(11) NOT NULL DEFAULT '0', `mapId` int(11) unsigned NOT NULL DEFAULT '0', - `unk1` int(11) unsigned NOT NULL DEFAULT '0', - `unk2` int(11) unsigned NOT NULL DEFAULT '0', + `mapAreaId` mediumint(8) unsigned NOT NULL DEFAULT '0', + `floorId` tinyint(3) unsigned NOT NULL DEFAULT '0', `unk3` int(11) unsigned NOT NULL DEFAULT '0', `unk4` int(11) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`questid`,`objIndex`) + PRIMARY KEY (`questId`,`poiId`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- @@ -13740,11 +13741,11 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `quest_poi_points`; CREATE TABLE `quest_poi_points` ( - `questId` int(11) unsigned NOT NULL DEFAULT '0', - `objIndex` int(11) NOT NULL DEFAULT '0', + `questId` mediumint(8) unsigned NOT NULL DEFAULT '0', + `poiId` tinyint(3) NOT NULL DEFAULT '0', `x` int(11) NOT NULL DEFAULT '0', `y` int(11) NOT NULL DEFAULT '0', - KEY `idx` (`questId`,`objIndex`) + KEY `idx_poip` (`questId`,`poiId`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- diff --git a/sql/updates/9651_01_mangos_quest_poi.sql b/sql/updates/9651_01_mangos_quest_poi.sql new file mode 100644 index 000000000..ddc4a556e --- /dev/null +++ b/sql/updates/9651_01_mangos_quest_poi.sql @@ -0,0 +1,20 @@ +ALTER TABLE db_version CHANGE COLUMN required_9636_01_mangos_item_template required_9651_01_mangos_quest_poi bit; + +-- Sorry, this was only way I knew, to avoid problems adding new primary key. Take backup if you don't want to loose your current data. +TRUNCATE quest_poi; +TRUNCATE quest_poi_points; + +ALTER TABLE quest_poi ADD COLUMN poiId tinyint(3) UNSIGNED DEFAULT '0' NOT NULL AFTER questid; +ALTER TABLE quest_poi CHANGE COLUMN questid questId mediumint(8) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE quest_poi CHANGE COLUMN unk1 mapAreaId mediumint(8) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE quest_poi CHANGE COLUMN unk2 floorId tinyint(3) UNSIGNED DEFAULT '0' NOT NULL; + +ALTER TABLE quest_poi_points ADD COLUMN poiId tinyint(3) UNSIGNED DEFAULT '0' NOT NULL AFTER questId; +ALTER TABLE quest_poi_points CHANGE COLUMN questId questId mediumint(8) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE quest_poi_points DROP COLUMN objIndex; + +ALTER TABLE quest_poi DROP PRIMARY KEY, + ADD PRIMARY KEY idx_poi (questId, poiId); + +ALTER TABLE quest_poi_points DROP INDEX idx, + ADD KEY idx_poip (questId, poiId); diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 449c05baf..f5318ed8a 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -97,6 +97,7 @@ pkgdata_DATA = \ 9635_01_characters_characters.sql \ 9636_01_mangos_item_template.sql \ 9646_01_characters_characters.sql \ + 9651_01_mangos_quest_poi.sql \ README ## Additional files to include when running 'make dist' @@ -174,4 +175,5 @@ EXTRA_DIST = \ 9635_01_characters_characters.sql \ 9636_01_mangos_item_template.sql \ 9646_01_characters_characters.sql \ + 9651_01_mangos_quest_poi.sql \ README diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index f39b89837..d96c32f8b 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6455,8 +6455,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) { @@ -6476,31 +6476,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); @@ -6509,6 +6494,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); } diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 0cd054708..e3b22c5d0 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -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 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 QuestPOIVector; diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp index 5e3f8c850..dc4b943a9 100644 --- a/src/game/QueryHandler.cpp +++ b/src/game/QueryHandler.cpp @@ -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); } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f42f7ecac..bc9dfe6c2 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9650" + #define REVISION_NR "9651" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 6d2f87090..75dfa2780 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -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_MANGOS "required_9651_01_mangos_quest_poi" #define REVISION_DB_REALMD "required_9010_01_realmd_realmlist" #endif // __REVISION_SQL_H__ From d004549fd4415da5e96c5a4e964db5c4d22494bb Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Thu, 1 Apr 2010 16:49:56 +0400 Subject: [PATCH 03/15] [9652] Avoid corpse save at arenas also, small cleanups. --- src/game/Player.cpp | 16 +++++++--------- src/shared/revision_nr.h | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index d2ce2e31a..a5ccb3033 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -4302,7 +4302,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; @@ -4326,11 +4326,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 ); @@ -4343,7 +4343,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; @@ -4353,10 +4353,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 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index bc9dfe6c2..5fdf5ba98 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9651" + #define REVISION_NR "9652" #endif // __REVISION_NR_H__ From daaeb0a99869aed0f6df3e856f209f0a14db481b Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 02:44:27 +0400 Subject: [PATCH 04/15] [9653] Correctly update talent free points in diff cases. Before at .unlearn or .learn all_mytalents commands free talent points not updated correctly. Same for .learn all_mypettalents. --- src/game/DBCStores.cpp | 9 ++++- src/game/DBCStores.h | 1 + src/game/Level3.cpp | 4 ++ src/game/Pet.cpp | 36 ++++++++++++------ src/game/Pet.h | 1 + src/game/Player.cpp | 81 +++++++++++++++++++++++----------------- src/game/Player.h | 1 + src/shared/revision_nr.h | 2 +- 8 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp index 51feb835f..a94b411bf 100644 --- a/src/game/DBCStores.cpp +++ b/src/game/DBCStores.cpp @@ -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); diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h index e9cf95674..5acc2b806 100644 --- a/src/game/DBCStores.h +++ b/src/game/DBCStores.h @@ -33,6 +33,7 @@ typedef std::list 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 diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 520251d7a..23291aece 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -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; } diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index dc88b571f..e2a7918ac 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -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) @@ -1720,17 +1716,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) diff --git a/src/game/Pet.h b/src/game/Pet.h index fdad92cef..efc9ecdf3 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -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; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index a5ccb3033..a39906a44 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -2518,16 +2518,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); } } @@ -2536,9 +2537,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); @@ -2547,8 +2548,13 @@ void Player::InitTalentForLevel() else SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount); } +} - if(!GetSession()->PlayerLoading()) +void Player::InitTalentForLevel() +{ + UpdateFreeTalentPoints(); + + if (!GetSession()->PlayerLoading()) SendTalentsInfoData(false); // update at client } @@ -2996,10 +3002,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 )) { @@ -3085,11 +3093,23 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen return false; } - uint32 talentCost = GetTalentSpellCost(spell_id); + if (talentPos) + { + // 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); @@ -3106,16 +3126,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(); @@ -3286,14 +3296,18 @@ 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) { + // 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) @@ -3381,7 +3395,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); @@ -3626,20 +3640,20 @@ bool Player::resetTalents(bool no_cost) TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); - if(!talentTabInfo) + if (!talentTabInfo) continue; // unlearn only talents for character class // some spell learned by one class as normal spells or know at creation but another class learn it as talent, // to prevent unexpected lost normal learned spell skip another class talents - if( (getClassMask() & talentTabInfo->ClassMask) == 0 ) + if ((getClassMask() & talentTabInfo->ClassMask) == 0) continue; for (int j = 0; j < MAX_TALENT_RANK; ++j) { for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();) { - if(itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled) + if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled) { ++itr; continue; @@ -5632,10 +5646,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(m_activeSpec); // can be 0, 1, 2 (talent spec) ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) { @@ -5647,7 +5661,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) @@ -15874,7 +15888,9 @@ 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(); + + addSpell(spell_id, fields[1].GetBool(), false, false, fields[2].GetBool()); } while( result->NextRow() ); @@ -20892,9 +20908,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) diff --git a/src/game/Player.h b/src/game/Player.h index b9c62ca43..18c93ac23 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1544,6 +1544,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(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 5fdf5ba98..c66795c22 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9652" + #define REVISION_NR "9653" #endif // __REVISION_NR_H__ From a9d6eed447c135bb8d84470ce6f75b95ec99cc1c Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 03:40:46 +0400 Subject: [PATCH 05/15] [9654] Restore action bar proper content show at spec switch. --- src/game/Player.cpp | 2 +- src/shared/revision_nr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index a39906a44..a2727f84d 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5649,7 +5649,7 @@ void Player::SendInitialActionButtons() const sLog.outDetail( "Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec); WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4)); - data << uint8(m_activeSpec); // can be 0, 1, 2 (talent spec) + data << uint8(1); // talent spec amount ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) { diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index c66795c22..15d4fddcd 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9653" + #define REVISION_NR "9654" #endif // __REVISION_NR_H__ From db22a0a9fbcfefcd393ba94e92cb65caf4cf526d Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 06:32:32 +0400 Subject: [PATCH 06/15] [9655] Fixed bug with possible double remove spell. In case when saved in DB spell remove triggering by dependendences chain self remove attempt. --- src/game/Player.cpp | 4 ++-- src/shared/revision_nr.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index a2727f84d..06e135f45 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3269,7 +3269,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; @@ -5649,7 +5649,7 @@ void Player::SendInitialActionButtons() const 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); // talent spec amount + data << uint8(1); // talent spec amount (in packet) ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; for(uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) { diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 15d4fddcd..faf5de9f2 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9654" + #define REVISION_NR "9655" #endif // __REVISION_NR_H__ From 034da8f6d426f217301235d13132de27d818d8fd Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 06:35:18 +0400 Subject: [PATCH 07/15] [9656] Implement '.list talents' command. Command show really know by selected player talent ranks, including bugged cases like 2 rank same talent known and etc. Also command claculated count of talents and total used talent points cost of known talent ranks. Can be helpful in bug debuging and cheating cases. --- sql/mangos.sql | 5 +- sql/updates/9656_01_mangos_command.sql | 5 + sql/updates/9656_02_mangos_mangos_string.sql | 7 + sql/updates/Makefile.am | 4 + src/game/Chat.cpp | 3 +- src/game/Chat.h | 2 + src/game/Language.h | 4 +- src/game/Level3.cpp | 127 +++++++++++++------ src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 10 files changed, 114 insertions(+), 47 deletions(-) create mode 100644 sql/updates/9656_01_mangos_command.sql create mode 100644 sql/updates/9656_02_mangos_mangos_string.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index b01b0c120..e0fc7d304 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_9651_01_mangos_quest_poi` bit(1) default NULL + `required_9656_02_mangos_mangos_string` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -626,6 +626,7 @@ INSERT INTO `command` VALUES ('list creature',3,'Syntax: .list creature #creature_id [#max_count]\r\n\r\nOutput creatures with creature id #creature_id found in world. Output creature guids and coordinates sorted by distance from character. Will be output maximum #max_count creatures. If #max_count not provided use 10 as default value.'), ('list item',3,'Syntax: .list item #item_id [#max_count]\r\n\r\nOutput items with item id #item_id found in all character inventories, mails, auctions, and guild banks. Output item guids, item owner guid, owner account and owner name (guild name and guid in case guild bank). Will be output maximum #max_count items. If #max_count not provided use 10 as default value.'), ('list object',3,'Syntax: .list object #gameobject_id [#max_count]\r\n\r\nOutput gameobjects with gameobject id #gameobject_id found in world. Output gameobject guids and coordinates sorted by distance from character. Will be output maximum #max_count gameobject. If #max_count not provided use 10 as default value.'), +('list talents',3,'Syntax: .list talents\r\n\r\nShow list all really known (as learned spells) talent rank spells for selected player or self.'), ('loadscripts',3,'Syntax: .loadscripts $scriptlibraryname\r\n\r\nUnload current and load the script library $scriptlibraryname or reload current if $scriptlibraryname omitted, in case you changed it while the server was running.'), ('lookup area',1,'Syntax: .lookup area $namepart\r\n\r\nLooks up an area by $namepart, and returns all matches with their area ID\'s.'), ('lookup creature',3,'Syntax: .lookup creature $namepart\r\n\r\nLooks up a creature by $namepart, and returns all matches with their creature ID\'s.'), @@ -3612,6 +3613,8 @@ INSERT INTO `mangos_string` VALUES (1132,' Follow player %s (lowguid %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1133,' Follow creature %s (lowguid %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1134,' Follow ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1135,'List known talents:',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1136,' (Found talents: %u used talent points: %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1200,'You try to view cinemitic %u but it doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1201,'You try to view movie %u but it doesn\'t exist.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `mangos_string` ENABLE KEYS */; diff --git a/sql/updates/9656_01_mangos_command.sql b/sql/updates/9656_01_mangos_command.sql new file mode 100644 index 000000000..a06881453 --- /dev/null +++ b/sql/updates/9656_01_mangos_command.sql @@ -0,0 +1,5 @@ +ALTER TABLE db_version CHANGE COLUMN required_9651_01_mangos_quest_poi required_9656_01_mangos_command bit; + +DELETE FROM command WHERE name IN ('list talents'); +INSERT INTO command VALUES +('list talents',3,'Syntax: .list talents\r\n\r\nShow list all really known (as learned spells) talent rank spells for selected player or self.'); diff --git a/sql/updates/9656_02_mangos_mangos_string.sql b/sql/updates/9656_02_mangos_mangos_string.sql new file mode 100644 index 000000000..22ee73c77 --- /dev/null +++ b/sql/updates/9656_02_mangos_mangos_string.sql @@ -0,0 +1,7 @@ +ALTER TABLE db_version CHANGE COLUMN required_9656_01_mangos_command required_9656_02_mangos_mangos_string bit; + +DELETE FROM mangos_string WHERE entry in (1135,1136); + +INSERT INTO mangos_string VALUES +(1135,'List known talents:',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1136,' (Found talents: %u used talent points: %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index f5318ed8a..b399428b9 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -98,6 +98,8 @@ pkgdata_DATA = \ 9636_01_mangos_item_template.sql \ 9646_01_characters_characters.sql \ 9651_01_mangos_quest_poi.sql \ + 9656_01_mangos_command.sql \ + 9656_02_mangos_mangos_string.sql \ README ## Additional files to include when running 'make dist' @@ -176,4 +178,6 @@ EXTRA_DIST = \ 9636_01_mangos_item_template.sql \ 9646_01_characters_characters.sql \ 9651_01_mangos_quest_poi.sql \ + 9656_01_mangos_command.sql \ + 9656_02_mangos_mangos_string.sql \ README diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index d004e6ca9..c410d2e31 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -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 } }; diff --git a/src/game/Chat.h b/src/game/Chat.h index da0563e01..10979cfe8 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -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: diff --git a/src/game/Language.h b/src/game/Language.h index be1c6f0f7..2802c687e 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -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, diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 23291aece..3fa28a872 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -2864,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) @@ -2913,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; } } @@ -4346,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) + 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; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index faf5de9f2..ebae4fdf1 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9655" + #define REVISION_NR "9656" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 75dfa2780..daac74de6 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -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_9651_01_mangos_quest_poi" + #define REVISION_DB_MANGOS "required_9656_02_mangos_mangos_string" #define REVISION_DB_REALMD "required_9010_01_realmd_realmlist" #endif // __REVISION_SQL_H__ From 973a21060bf92d69978c393360c397062591a1a6 Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 18:29:03 +0400 Subject: [PATCH 08/15] [9657] Not include in .list talents disabled spells Also some more cleanups in free talent points update calls. --- src/game/Level3.cpp | 2 +- src/game/Pet.cpp | 4 ++-- src/game/Player.cpp | 10 +++------- src/shared/revision_nr.h | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 3fa28a872..adf33d382 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -4368,7 +4368,7 @@ bool ChatHandler::HandleListTalentsCommand (const char * /*args*/) PlayerSpellMap const& uSpells = player->GetSpellMap(); for (PlayerSpellMap::const_iterator itr = uSpells.begin(); itr != uSpells.end(); ++itr) { - if (itr->second.state == PLAYERSPELL_REMOVED) + if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.disabled) continue; uint32 cost_itr = GetTalentSpellCost(itr->first); diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index e2a7918ac..4e408ca32 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1562,7 +1562,7 @@ bool Pet::resetTalents(bool no_cost) if (m_usedTalentCount == 0) { - SetFreeTalentPoints(talentPointsForLevel); + UpdateFreeTalentPoints(false); // for fix if need counter return false; } @@ -1619,7 +1619,7 @@ bool Pet::resetTalents(bool no_cost) } } - SetFreeTalentPoints(talentPointsForLevel); + UpdateFreeTalentPoints(false); if(!no_cost) { diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 06e135f45..94f644598 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3611,11 +3611,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; } @@ -3681,7 +3679,7 @@ bool Player::resetTalents(bool no_cost) } } - SetFreeTalentPoints(talentPointsForLevel); + UpdateFreeTalentPoints(false); if(!no_cost) { @@ -20854,6 +20852,7 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank) if (HasSpell(depTalentInfo->RankID[i])) hasEnoughRank = true; } + if (!hasEnoughRank) return; } @@ -21040,9 +21039,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) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index ebae4fdf1..efc5bd2bc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9656" + #define REVISION_NR "9657" #endif // __REVISION_NR_H__ From eee89851a6c4dd00ab91c368df69ff77ae2c14d9 Mon Sep 17 00:00:00 2001 From: Laise Date: Fri, 2 Apr 2010 22:47:35 +0400 Subject: [PATCH 09/15] [9658] Remove SPELL_EFFECT_TITAN_GRIP flag at related spell remove. This let have correct state for flag at .unlearn related spell and etc. Not only at full talent reset. Signed-off-by: VladimirMangos --- src/game/Player.cpp | 20 +++++++++++--------- src/shared/revision_nr.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 94f644598..db9ed39fa 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3431,6 +3431,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) { @@ -3700,15 +3711,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; } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index efc5bd2bc..deefa4b7a 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9657" + #define REVISION_NR "9658" #endif // __REVISION_NR_H__ From 12426b158d2d6b05a15076a8e20ed739fd809eac Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 23:09:17 +0400 Subject: [PATCH 10/15] [9659] Simplify resetTalent code. Currently all code related to deepndent spells unlern exist in removeSpell so not need do it in special way in talentReset. --- src/game/Player.cpp | 31 ++----------------------------- src/shared/revision_nr.h | 2 +- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index db9ed39fa..841496f4c 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3659,35 +3659,8 @@ bool Player::resetTalents(bool no_cost) 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; - } - - // 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; - } - } + if (talentInfo->RankID[j]) + removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false); } UpdateFreeTalentPoints(false); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index deefa4b7a..f940ee49d 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9658" + #define REVISION_NR "9659" #endif // __REVISION_NR_H__ From ce40dedaf08c89903c617faaef11411f3c958c2a Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 2 Apr 2010 23:20:10 +0400 Subject: [PATCH 11/15] [9660] Simplify talents reset for pet case. --- src/game/Pet.cpp | 24 ++---------------------- src/shared/revision_nr.h | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 4e408ca32..f4edc6bae 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1595,28 +1595,8 @@ 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); } UpdateFreeTalentPoints(false); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f940ee49d..67546f67c 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9659" + #define REVISION_NR "9660" #endif // __REVISION_NR_H__ From 7fb7c47de5d3ca5e3821a0d0b922a0bfbce2ba90 Mon Sep 17 00:00:00 2001 From: Laise Date: Fri, 2 Apr 2010 23:34:20 +0400 Subject: [PATCH 12/15] [9661] Implement dual talent specializations in talent switch part. * Implement talent specs switch functionality. * Only gossip part still not implemented. * At update server to this commit or later character talents will be reset with some spam in logs about wrong places talents in character_spell. It can be ignored as part of conversion to new table support. Thanks to all getmangos.com community members who take part in creating and updating original dual spec patch. Signed-off-by: VladimirMangos --- sql/characters.sql | 27 +- .../9661_01_characters_character_talent.sql | 13 + sql/updates/Makefile.am | 2 + src/game/CharacterHandler.cpp | 1 + src/game/Player.cpp | 332 ++++++++++++++---- src/game/Player.h | 14 +- src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 8 files changed, 322 insertions(+), 71 deletions(-) create mode 100644 sql/updates/9661_01_characters_character_talent.sql diff --git a/sql/characters.sql b/sql/characters.sql index cd14803d8..683c03d59 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_9646_01_characters_characters` bit(1) default NULL + `required_9661_01_characters_character_talent` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -830,6 +830,31 @@ LOCK TABLES `character_spell_cooldown` WRITE; /*!40000 ALTER TABLE `character_spell_cooldown` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `character_talent` +-- + +DROP TABLE IF EXISTS `character_talent`; +CREATE TABLE `character_talent` ( + `guid` int(11) unsigned NOT NULL, + `talent_id` int(11) unsigned NOT NULL, + `current_rank` tinyint(3) unsigned NOT NULL DEFAULT '0', + `spec` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`guid`,`talent_id`,`spec`), + KEY guid_key (`guid`), + KEY talent_key (`talent_id`), + KEY spec_key (`spec`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `character_talent` +-- + +LOCK TABLES `character_talent` WRITE; +/*!40000 ALTER TABLE `character_talent` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_talent` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `character_ticket` -- diff --git a/sql/updates/9661_01_characters_character_talent.sql b/sql/updates/9661_01_characters_character_talent.sql new file mode 100644 index 000000000..8772134e8 --- /dev/null +++ b/sql/updates/9661_01_characters_character_talent.sql @@ -0,0 +1,13 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_9646_01_characters_characters required_9661_01_characters_character_talent bit; + +DROP TABLE IF EXISTS `character_talent`; +CREATE TABLE `character_talent` ( + `guid` int(11) unsigned NOT NULL, + `talent_id` int(11) unsigned NOT NULL, + `current_rank` tinyint(3) unsigned NOT NULL DEFAULT '0', + `spec` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`guid`,`talent_id`,`spec`), + KEY guid_key (`guid`), + KEY talent_key (`talent_id`), + KEY spec_key (`spec`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index b399428b9..72e610325 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -100,6 +100,7 @@ pkgdata_DATA = \ 9651_01_mangos_quest_poi.sql \ 9656_01_mangos_command.sql \ 9656_02_mangos_mangos_string.sql \ + 9661_01_characters_character_talent.sql \ README ## Additional files to include when running 'make dist' @@ -180,4 +181,5 @@ EXTRA_DIST = \ 9651_01_mangos_quest_poi.sql \ 9656_01_mangos_command.sql \ 9656_02_mangos_mangos_string.sql \ + 9661_01_characters_character_talent.sql \ README diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 13e45f6b5..c684452f1 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -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,itemTextId,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC", GUID_LOPART(m_guid)); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 841496f4c..5915123a1 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3095,6 +3095,28 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen 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); @@ -3299,6 +3321,18 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bo 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); @@ -3641,26 +3675,43 @@ 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) + { + 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) + { + ++iter; continue; + } for (int j = 0; j < MAX_TALENT_RANK; ++j) if (talentInfo->RankID[j]) removeSpell(talentInfo->RankID[j],!IsPassiveSpell(talentInfo->RankID[j]),false); + + iter = m_talents[m_activeSpec].begin(); } UpdateFreeTalentPoints(false); @@ -4091,6 +4142,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); @@ -15069,6 +15121,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(); @@ -15863,6 +15917,14 @@ void Player::_LoadSpells(QueryResult *result) 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() ); @@ -15871,6 +15933,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()); @@ -16362,6 +16500,7 @@ void Player::SaveToDB() _SaveEquipmentSets(); GetSession()->SaveTutorialsData(); // changed only while character in game _SaveGlyphs(); + _SaveTalents(); CharacterDatabase.CommitTransaction(); @@ -16708,12 +16847,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++); @@ -16726,6 +16870,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 @@ -20798,14 +20966,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)) @@ -20821,11 +20984,12 @@ 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) @@ -20839,28 +21003,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 @@ -21101,34 +21246,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; } @@ -21403,13 +21533,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) @@ -21418,11 +21610,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) diff --git a/src/game/Player.h b/src/game/Player.h index 18c93ac23..127273f4d 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -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 PlayerSpellMap; +typedef UNORDERED_MAP 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 @@ -2314,6 +2323,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); @@ -2338,6 +2348,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; @@ -2389,6 +2400,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 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 67546f67c..e7a9e4d8d 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9660" + #define REVISION_NR "9661" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index daac74de6..47bfd6168 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ - #define REVISION_DB_CHARACTERS "required_9646_01_characters_characters" + #define REVISION_DB_CHARACTERS "required_9661_01_characters_character_talent" #define REVISION_DB_MANGOS "required_9656_02_mangos_mangos_string" #define REVISION_DB_REALMD "required_9010_01_realmd_realmlist" #endif // __REVISION_SQL_H__ From 6148675b0ec8fc8d97ae15b52d1b1bf8857f461e Mon Sep 17 00:00:00 2001 From: DasBlub Date: Sat, 3 Apr 2010 04:07:19 +0400 Subject: [PATCH 13/15] [9662] Use the db-user 'mangos' instead of 'root' in the config file. Change done for consistence with 'mangos' created in sql/create_mysql.sql NOTE: for secirity reason better not use this user name (and password) in real configs/MySQL. Signed-off-by: VladimirMangos --- src/mangosd/mangosd.conf.dist.in | 6 +++--- src/realmd/realmd.conf.dist.in | 2 +- src/shared/revision_nr.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index d10f46034..296ae772f 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -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" diff --git a/src/realmd/realmd.conf.dist.in b/src/realmd/realmd.conf.dist.in index 217cb15d2..66335b718 100644 --- a/src/realmd/realmd.conf.dist.in +++ b/src/realmd/realmd.conf.dist.in @@ -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 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index e7a9e4d8d..3875f82cb 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9661" + #define REVISION_NR "9662" #endif // __REVISION_NR_H__ From 53a010d5a567eb67ebe4c07c5b456a58a90c2cb9 Mon Sep 17 00:00:00 2001 From: NoFantasy Date: Sat, 3 Apr 2010 02:26:28 +0200 Subject: [PATCH 14/15] [9663] Include entry of gameobject in gobject near command Signed-off-by: NoFantasy --- sql/mangos.sql | 2 +- sql/updates/9663_01_mangos_mangos_string.sql | 4 ++++ sql/updates/Makefile.am | 2 ++ src/game/Level2.cpp | 2 +- src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 sql/updates/9663_01_mangos_mangos_string.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index e0fc7d304..7eacc4698 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_9656_02_mangos_mangos_string` bit(1) default NULL + `required_9663_01_mangos_mangos_string` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- diff --git a/sql/updates/9663_01_mangos_mangos_string.sql b/sql/updates/9663_01_mangos_mangos_string.sql new file mode 100644 index 000000000..fa713086c --- /dev/null +++ b/sql/updates/9663_01_mangos_mangos_string.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_9656_02_mangos_mangos_string required_9663_01_mangos_mangos_string bit; + +DELETE FROM mangos_string WHERE entry=517; +INSERT INTO mangos_string VALUES (517,'%d, Entry %d - |cffffffff|Hgameobject:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 72e610325..aa0add420 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -101,6 +101,7 @@ pkgdata_DATA = \ 9656_01_mangos_command.sql \ 9656_02_mangos_mangos_string.sql \ 9661_01_characters_character_talent.sql \ + 9663_01_mangos_mangos_string.sql \ README ## Additional files to include when running 'make dist' @@ -182,4 +183,5 @@ EXTRA_DIST = \ 9656_01_mangos_command.sql \ 9656_02_mangos_mangos_string.sql \ 9661_01_characters_character_talent.sql \ + 9663_01_mangos_mangos_string.sql \ README diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index a10259c89..de77eca9a 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -831,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()); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 3875f82cb..b70476176 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9662" + #define REVISION_NR "9663" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 47bfd6168..d715a463b 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_9661_01_characters_character_talent" - #define REVISION_DB_MANGOS "required_9656_02_mangos_mangos_string" + #define REVISION_DB_MANGOS "required_9663_01_mangos_mangos_string" #define REVISION_DB_REALMD "required_9010_01_realmd_realmlist" #endif // __REVISION_SQL_H__ From 8bd8986d5ddc4f2d4d1342a0a6aefb074c304222 Mon Sep 17 00:00:00 2001 From: NoFantasy Date: Sat, 3 Apr 2010 02:32:31 +0200 Subject: [PATCH 15/15] [9664] Fix what author of [9663] messed up, and include the changes in mangos.sql Signed-off-by: NoFantasy --- sql/mangos.sql | 2 +- src/shared/revision_nr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/mangos.sql b/sql/mangos.sql index 7eacc4698..d3e16f58d 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -3330,7 +3330,7 @@ INSERT INTO `mangos_string` VALUES (514,'%d - |cffffffff|Hcreature_entry:%d|h[%s]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (515,'%d - |cffffffff|Hcreature:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (516,'%d - |cffffffff|Hgameobject_entry:%d|h[%s]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(517,'%d - |cffffffff|Hgameobject:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(517,'%d, Entry %d - |cffffffff|Hgameobject:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (518,'%d - |cffffffff|Hitemset:%d|h[%s %s]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (519,'|cffffffff|Htele:%s|h[%s]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (520,'%d - |cffffffff|Hspell:%d|h[%s]|h|r ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index b70476176..51ddabebc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9663" + #define REVISION_NR "9664" #endif // __REVISION_NR_H__