diff options
author | Shauren <shauren.trinity@gmail.com> | 2015-05-22 00:17:48 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2015-05-22 00:17:48 +0200 |
commit | b3a754cd7bef73c4f29a3fa819d2bac9f173685a (patch) | |
tree | 60d1978a3393adbafd9dfe86a7e5ab550306e480 | |
parent | 1d29328769d505caf4541b0d712972195dda8b70 (diff) |
Core/Garrisons: Implemented follower class/spec abilities and saving them to database
-rw-r--r-- | sql/base/characters_database.sql | 60 | ||||
-rw-r--r-- | sql/updates/characters/2015_05_22_00_characters.sql | 35 | ||||
-rw-r--r-- | sql/updates/world/2015_05_22_00_world.sql | 91 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Garrison/Garrison.cpp | 90 | ||||
-rw-r--r-- | src/server/game/Garrison/Garrison.h | 3 | ||||
-rw-r--r-- | src/server/game/Garrison/GarrisonMgr.cpp | 100 | ||||
-rw-r--r-- | src/server/game/Garrison/GarrisonMgr.h | 9 | ||||
-rw-r--r-- | src/server/game/Handlers/CharacterHandler.cpp | 8 | ||||
-rw-r--r-- | src/server/shared/Database/Implementation/CharacterDatabase.cpp | 7 | ||||
-rw-r--r-- | src/server/shared/Database/Implementation/CharacterDatabase.h | 5 |
12 files changed, 404 insertions, 17 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 37b84dfc29d..d73924aa76d 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -827,6 +827,66 @@ LOCK TABLES `character_garrison_buildings` WRITE; UNLOCK TABLES; -- +-- Table structure for table `character_garrison_followers` +-- + +DROP TABLE IF EXISTS `character_garrison_followers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `character_garrison_followers` ( + `dbId` bigint(20) unsigned NOT NULL, + `guid` bigint(20) unsigned NOT NULL, + `followerId` int(10) unsigned NOT NULL, + `quality` int(10) unsigned NOT NULL DEFAULT '2', + `level` int(10) unsigned NOT NULL DEFAULT '90', + `itemLevelWeapon` int(10) unsigned NOT NULL DEFAULT '600', + `itemLevelArmor` int(10) unsigned NOT NULL DEFAULT '600', + `xp` int(10) unsigned NOT NULL DEFAULT '0', + `currentBuilding` int(10) unsigned NOT NULL DEFAULT '0', + `currentMission` int(10) unsigned NOT NULL DEFAULT '0', + `status` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`dbId`), + UNIQUE KEY `idx_guid_id` (`guid`,`followerId`), + CONSTRAINT `fk_foll_owner` FOREIGN KEY (`guid`) REFERENCES `characters` (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `character_garrison_followers` +-- + +LOCK TABLES `character_garrison_followers` WRITE; +/*!40000 ALTER TABLE `character_garrison_followers` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_garrison_followers` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `character_garrison_follower_abilities` +-- + +DROP TABLE IF EXISTS `character_garrison_follower_abilities`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `character_garrison_follower_abilities` ( + `dbId` bigint(20) unsigned NOT NULL, + `abilityId` int(10) unsigned NOT NULL, + `slot` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`dbId`,`abilityId`,`slot`), + CONSTRAINT `fk_foll_dbid` FOREIGN KEY (`dbId`) REFERENCES `character_garrison_followers` (`dbId`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `character_garrison_follower_abilities` +-- + +LOCK TABLES `character_garrison_follower_abilities` WRITE; +/*!40000 ALTER TABLE `character_garrison_follower_abilities` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_garrison_follower_abilities` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +-- -- Table structure for table `character_gifts` -- diff --git a/sql/updates/characters/2015_05_22_00_characters.sql b/sql/updates/characters/2015_05_22_00_characters.sql new file mode 100644 index 00000000000..35e72a5aa86 --- /dev/null +++ b/sql/updates/characters/2015_05_22_00_characters.sql @@ -0,0 +1,35 @@ +DROP TABLE IF EXISTS `character_garrison_follower_abilities`; +DROP TABLE IF EXISTS `character_garrison_followers`; + +-- +-- Table structure for table `character_garrison_followers` +-- + +CREATE TABLE `character_garrison_followers` ( + `dbId` bigint(20) unsigned NOT NULL, + `guid` bigint(20) unsigned NOT NULL, + `followerId` int(10) unsigned NOT NULL, + `quality` int(10) unsigned NOT NULL DEFAULT '2', + `level` int(10) unsigned NOT NULL DEFAULT '90', + `itemLevelWeapon` int(10) unsigned NOT NULL DEFAULT '600', + `itemLevelArmor` int(10) unsigned NOT NULL DEFAULT '600', + `xp` int(10) unsigned NOT NULL DEFAULT '0', + `currentBuilding` int(10) unsigned NOT NULL DEFAULT '0', + `currentMission` int(10) unsigned NOT NULL DEFAULT '0', + `status` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`dbId`), + UNIQUE KEY `idx_guid_id` (`guid`,`followerId`), + CONSTRAINT `fk_foll_owner` FOREIGN KEY (`guid`) REFERENCES `characters` (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Table structure for table `character_garrison_follower_abilities` +-- + +CREATE TABLE `character_garrison_follower_abilities` ( + `dbId` bigint(20) unsigned NOT NULL, + `abilityId` int(10) unsigned NOT NULL, + `slot` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`dbId`,`abilityId`,`slot`), + CONSTRAINT `fk_foll_dbid` FOREIGN KEY (`dbId`) REFERENCES `character_garrison_followers` (`dbId`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/sql/updates/world/2015_05_22_00_world.sql b/sql/updates/world/2015_05_22_00_world.sql new file mode 100644 index 00000000000..f82e738ea20 --- /dev/null +++ b/sql/updates/world/2015_05_22_00_world.sql @@ -0,0 +1,91 @@ +DROP TABLE IF EXISTS `garrison_follower_class_spec_abilities`; +CREATE TABLE `garrison_follower_class_spec_abilities` ( + `classSpecId` int(10) unsigned NOT NULL DEFAULT '0', + `abilityId` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`classSpecId`,`abilityId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `garrison_follower_class_spec_abilities` (`abilityId`,`classSpecId`) VALUES +(119,3), -- Anti-Magic Shell +(158,29),(158,30), -- Ascendance +(131,22), -- Avenging Wrath +(132,7),(132,8), -- Barkskin +(138,7), -- Berserk +(170,14),(170,15), -- Blink +(5,15),(5,16), -- Blizzard +(115,2),(115,4), -- Bone Shield +(139,5), -- Celestial Alignment +(106,29),(106,30),(106,31), -- Chain Heal +(154,29),(154,30),(154,31), -- Chain Lightning +(142,17),(142,18), -- Chi Wave +(125,20),(125,21), -- Cleanse +(120,35),(120,37),(120,38), -- Cleave +(169,14), -- Conjure Food +(166,12), -- Counter Shot +(171,14),(171,15),(171,16), -- Counterspell +(114,2),(114,3),(114,4), -- Dark Command +(182,5),(182,7),(182,8), -- Dash +(116,2),(116,3),(116,4), -- Death and Decay +(164,13), -- Deterrence +(145,18), -- Detox +(165,10),(165,12),(165,13), -- Disengage +(148,23),(148,24),(148,25), -- Dispel Magic +(129,20), -- Divine Plea +(124,21),(124,22), -- Divine Shield +(130,22), -- Divine Storm +(151,23),(151,25), -- Dominate Mind +(175,34), -- Drain Life +(118,2),(118,3),(118,4), -- Empower Rune Weapon +(183,19), -- Energizing Brew +(134,5),(134,8),(134,9), -- Entangling Roots +(159,26),(159,28), -- Evasion +(161,27),(161,28), -- Fan of Knives +(180,32),(180,34), -- Fear +(163,10), -- Feign Death +(167,10),(167,12),(167,13), -- Freezing Trap +(156,30), -- Ghost Wolf +(108,7),(108,8), -- Growl +(141,17), -- Guard +(102,35),(102,37),(102,38), -- Heroic Leap +(157,29),(157,30), -- Hex +(128,20), -- Holy Radiance +(137,5),(137,9), -- Hurricane +(168,16), -- Ice Block +(133,5),(133,7),(133,8),(133,9), -- Innervate +(105,26),(105,27),(105,28), -- Kick +(10,23),(10,24),(10,25), -- Leap of Faith +(146,18), -- Mana Tea +(162,26),(162,27), -- Marked for Death +(177,33),(177,34), -- Metamorphosis +(117,2),(117,3),(117,4), -- Mind Freeze +(150,24),(150,25), -- Mind Sear +(101,10),(101,12),(101,13), -- Multi-Shot +(136,9), -- Nature's Cure +(144,17),(144,19), -- Paralysis +(172,14),(172,15),(172,16), -- Polymorph +(152,25), -- Power Infusion +(11,23),(11,24), -- Prayer of Healing +(140,17),(140,19), -- Provoke +(121,35),(121,37),(121,38), -- Pummel +(107,31), -- Purify Spirit +(178,32),(178,33), -- Rain of Fire +(103,10),(103,12),(103,13), -- Rapid Fire +(126,20),(126,21),(126,22), -- Rebuke +(122,35),(122,37), -- Recklessness +(123,21), -- Reckoning +(127,20),(127,21),(127,22), -- Repentance +(143,17),(143,18),(143,19), -- Roll +(104,26),(104,27),(104,28), -- Sap +(149,23),(149,24), -- Shadowfiend +(6,37),(6,38), -- Shield Wall +(176,32), -- Singe Magic +(147,18),(147,19), -- Spear Hand Strike +(179,32),(179,33),(179,34), -- Spell Lock +(160,26),(160,27),(160,28), -- Sprint +(181,32),(181,33),(181,34), -- Summon Infernal +(100,35),(100,38), -- Taunt +(173,14),(173,15),(173,16), -- Time Warp +(174,33), -- Unending Resolve +(153,31), -- Water Shield +(135,9), -- Wild Growth +(155,29),(155,31); -- Wind Shear diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 419992f3408..55ae58fe342 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4590,6 +4590,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt64(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS); + stmt->setUInt64(0, guid); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); sWorld->DeleteCharacterInfo(playerguid); @@ -17344,8 +17348,6 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES)); - m_achievementMgr->CheckAllAchievementCriteria(this); - _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS)); _LoadCUFProfiles(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES)); @@ -17353,9 +17355,12 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) std::unique_ptr<Garrison> garrison = Trinity::make_unique<Garrison>(this); if (garrison->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_BLUEPRINTS), - holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS))) + holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS), + holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWERS), + holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWER_ABILITIES))) _garrison = std::move(garrison); + m_achievementMgr->CheckAllAchievementCriteria(this); return true; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index c7af9426623..f17b304409e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -987,6 +987,8 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_GARRISON, PLAYER_LOGIN_QUERY_LOAD_GARRISON_BLUEPRINTS, PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS, + PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWERS, + PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWER_ABILITIES, MAX_PLAYER_LOGIN_QUERY }; diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp index 3087de37472..8e27bbd930d 100644 --- a/src/server/game/Garrison/Garrison.cpp +++ b/src/server/game/Garrison/Garrison.cpp @@ -25,7 +25,8 @@ Garrison::Garrison(Player* owner) : _owner(owner), _siteLevel(nullptr), _followe { } -bool Garrison::LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings) +bool Garrison::LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings, + PreparedQueryResult followers, PreparedQueryResult abilities) { if (!garrison) return false; @@ -76,6 +77,60 @@ bool Garrison::LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blue } while (buildings->NextRow()); } + // 0 1 2 3 4 5 6 7 8 9 + // SELECT dbId, followerId, quality, level, itemLevelWeapon, itemLevelArmor, xp, currentBuilding, currentMission, status FROM character_garrison_followers WHERE guid = ? + if (followers) + { + std::unordered_map<uint64, Follower*> followersByDbId; + do + { + Field* fields = followers->Fetch(); + + uint32 followerId = fields[1].GetUInt32(); + if (!sGarrFollowerStore.LookupEntry(followerId)) + continue; + + Follower& follower = _followers[followerId]; + follower.PacketInfo.DbID = fields[0].GetUInt64(); + follower.PacketInfo.GarrFollowerID = followerId; + follower.PacketInfo.Quality = fields[2].GetUInt32(); + follower.PacketInfo.FollowerLevel = fields[3].GetUInt32(); + follower.PacketInfo.ItemLevelWeapon = fields[4].GetUInt32(); + follower.PacketInfo.ItemLevelArmor = fields[5].GetUInt32(); + follower.PacketInfo.Xp = fields[6].GetUInt32(); + follower.PacketInfo.CurrentBuildingID = fields[7].GetUInt32(); + follower.PacketInfo.CurrentMissionID = fields[8].GetUInt32(); + follower.PacketInfo.FollowerStatus = fields[9].GetUInt32(); + if (!sGarrBuildingStore.LookupEntry(follower.PacketInfo.CurrentBuildingID)) + follower.PacketInfo.CurrentBuildingID = 0; + + //if (!sGarrMissionStore.LookupEntry(follower.PacketInfo.CurrentMissionID)) + // follower.PacketInfo.CurrentMissionID = 0; + + followersByDbId[follower.PacketInfo.DbID] = &follower; + + } while (followers->NextRow()); + + if (abilities) + { + do + { + Field* fields = abilities->Fetch(); + uint64 dbId = fields[0].GetUInt64(); + GarrAbilityEntry const* ability = sGarrAbilityStore.LookupEntry(fields[1].GetUInt32()); + + if (!ability) + continue; + + auto itr = followersByDbId.find(dbId); + if (itr == followersByDbId.end()) + continue; + + itr->second->PacketInfo.AbilityID.push_back(ability); + } while (abilities->NextRow()); + } + } + return true; } @@ -93,6 +148,10 @@ void Garrison::SaveToDB(SQLTransaction& trans) stmt->setUInt64(0, _owner->GetGUID().GetCounter()); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS); + stmt->setUInt64(0, _owner->GetGUID().GetCounter()); + trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON); stmt->setUInt64(0, _owner->GetGUID().GetCounter()); stmt->setUInt32(1, _siteLevel->ID); @@ -121,6 +180,35 @@ void Garrison::SaveToDB(SQLTransaction& trans) trans->Append(stmt); } } + + for (auto const& p : _followers) + { + Follower const& follower = p.second; + uint8 index = 0; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWERS); + stmt->setUInt64(index++, follower.PacketInfo.DbID); + stmt->setUInt64(index++, _owner->GetGUID().GetCounter()); + stmt->setUInt32(index++, follower.PacketInfo.GarrFollowerID); + stmt->setUInt32(index++, follower.PacketInfo.Quality); + stmt->setUInt32(index++, follower.PacketInfo.FollowerLevel); + stmt->setUInt32(index++, follower.PacketInfo.ItemLevelWeapon); + stmt->setUInt32(index++, follower.PacketInfo.ItemLevelArmor); + stmt->setUInt32(index++, follower.PacketInfo.Xp); + stmt->setUInt32(index++, follower.PacketInfo.CurrentBuildingID); + stmt->setUInt32(index++, follower.PacketInfo.CurrentMissionID); + stmt->setUInt32(index++, follower.PacketInfo.FollowerStatus); + trans->Append(stmt); + + uint8 slot = 0; + for (GarrAbilityEntry const* ability : follower.PacketInfo.AbilityID) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES); + stmt->setUInt64(0, follower.PacketInfo.DbID); + stmt->setUInt32(1, ability->ID); + stmt->setUInt8(2, slot++); + trans->Append(stmt); + } + } } bool Garrison::Create(uint32 garrSiteId) diff --git a/src/server/game/Garrison/Garrison.h b/src/server/game/Garrison/Garrison.h index be92b9e14f3..dc48a43c5cc 100644 --- a/src/server/game/Garrison/Garrison.h +++ b/src/server/game/Garrison/Garrison.h @@ -105,7 +105,8 @@ public: explicit Garrison(Player* owner); - bool LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings); + bool LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings, + PreparedQueryResult followers, PreparedQueryResult abilities); void SaveToDB(SQLTransaction& trans); bool Create(uint32 garrSiteId); diff --git a/src/server/game/Garrison/GarrisonMgr.cpp b/src/server/game/Garrison/GarrisonMgr.cpp index 75f4289c385..f95acef952c 100644 --- a/src/server/game/Garrison/GarrisonMgr.cpp +++ b/src/server/game/Garrison/GarrisonMgr.cpp @@ -17,6 +17,7 @@ #include "GarrisonMgr.h" #include "Containers.h" +#include "DatabaseEnv.h" #include "Garrison.h" #include "ObjectDefines.h" #include "World.h" @@ -55,6 +56,9 @@ void GarrisonMgr::Initialize() } } } + + InitializeDbIdSequences(); + LoadFollowerClassSpecAbilities(); } GarrSiteLevelEntry const* GarrisonMgr::GetGarrSiteLevelEntry(uint32 garrSiteId, uint32 level) const @@ -185,13 +189,18 @@ std::list<GarrAbilityEntry const*> GarrisonMgr::RollFollowerAbilities(GarrFollow Trinity::Containers::RandomResizeList(abilityList, std::max<int32>(0, slots[0] - forcedAbilities.size())); Trinity::Containers::RandomResizeList(traitList, std::max<int32>(0, slots[1] - forcedTraits.size())); - // Add counters specified in GarrFollowerXAbility.db2 before generic classspec ones on follower creation + // Add abilities specified in GarrFollowerXAbility.db2 before generic classspec ones on follower creation if (initial) { forcedAbilities.splice(forcedAbilities.end(), abilityList); forcedTraits.splice(forcedTraits.end(), traitList); } + forcedAbilities.sort(); + abilityList.sort(); + forcedTraits.sort(); + traitList.sort(); + // check if we have a trait from exclusive category for (GarrAbilityEntry const* ability : forcedTraits) { @@ -204,18 +213,18 @@ std::list<GarrAbilityEntry const*> GarrisonMgr::RollFollowerAbilities(GarrFollow if (slots[0] > forcedAbilities.size() + abilityList.size()) { - std::list<GarrAbilityEntry const*> classSpecAbilities; // = GetDefaultClassSpecAbilities(follower, faction) - - abilityList.splice(abilityList.end(), classSpecAbilities); - abilityList.sort(); - abilityList.unique(); + std::list<GarrAbilityEntry const*> classSpecAbilities = GetClassSpecAbilities(follower, faction); + std::list<GarrAbilityEntry const*> classSpecAbilitiesTemp, classSpecAbilitiesTemp2; + classSpecAbilitiesTemp2.swap(abilityList); + std::set_difference(classSpecAbilities.begin(), classSpecAbilities.end(), forcedAbilities.begin(), forcedAbilities.end(), std::back_inserter(classSpecAbilitiesTemp)); + std::set_union(classSpecAbilitiesTemp.begin(), classSpecAbilitiesTemp.end(), classSpecAbilitiesTemp2.begin(), classSpecAbilitiesTemp2.end(), std::back_inserter(abilityList)); Trinity::Containers::RandomResizeList(abilityList, std::max<int32>(0, slots[0] - forcedAbilities.size())); } if (slots[1] > forcedTraits.size() + traitList.size()) { - std::list<GarrAbilityEntry const*> genericTraits; + std::list<GarrAbilityEntry const*> genericTraits, genericTraitsTemp; for (GarrAbilityEntry const* ability : _garrisonFollowerRandomTraits) { if (ability->Flags & GARRISON_ABILITY_HORDE_ONLY && faction != GARRISON_FACTION_INDEX_HORDE) @@ -227,9 +236,10 @@ std::list<GarrAbilityEntry const*> GarrisonMgr::RollFollowerAbilities(GarrFollow if (hasForcedExclusiveTrait && ability->Flags & GARRISON_ABILITY_FLAG_EXCLUSIVE) continue; - genericTraits.push_back(ability); + genericTraitsTemp.push_back(ability); } + std::set_difference(genericTraitsTemp.begin(), genericTraitsTemp.end(), forcedTraits.begin(), forcedTraits.end(), std::back_inserter(genericTraits)); genericTraits.splice(genericTraits.begin(), traitList); // "split" the list into two parts [nonexclusive, exclusive] to make selection later easier genericTraits.sort([](GarrAbilityEntry const* a1, GarrAbilityEntry const* a2) @@ -248,7 +258,7 @@ std::list<GarrAbilityEntry const*> GarrisonMgr::RollFollowerAbilities(GarrFollow if ((*itr)->Flags & GARRISON_ABILITY_FLAG_EXCLUSIVE) break; - while (traitList.size() < std::max<int32>(0, slots[1] - forcedTraits.size()) && !genericTraits.empty()) + while (traitList.size() < std::max<int32>(0, slots[1] - forcedTraits.size()) && total) { auto itr = genericTraits.begin(); std::advance(itr, urand(0, total-- - 1)); @@ -269,3 +279,75 @@ std::list<GarrAbilityEntry const*> GarrisonMgr::RollFollowerAbilities(GarrFollow return result; } + +std::list<GarrAbilityEntry const*> GarrisonMgr::GetClassSpecAbilities(GarrFollowerEntry const* follower, uint32 faction) const +{ + std::list<GarrAbilityEntry const*> abilities; + uint32 classSpecId; + switch (faction) + { + case GARRISON_FACTION_INDEX_HORDE: + classSpecId = follower->HordeGarrClassSpecID; + break; + case GARRISON_FACTION_INDEX_ALLIANCE: + classSpecId = follower->AllianceGarrClassSpecID; + break; + default: + return abilities; + } + + if (!sGarrClassSpecStore.LookupEntry(classSpecId)) + return abilities; + + auto itr = _garrisonFollowerClassSpecAbilities.find(classSpecId); + if (itr != _garrisonFollowerClassSpecAbilities.end()) + abilities = itr->second; + + return abilities; +} + +void GarrisonMgr::InitializeDbIdSequences() +{ + if (QueryResult result = CharacterDatabase.Query("SELECT MAX(dbId) FROM character_garrison_followers")) + _followerDbIdGenerator = (*result)[0].GetUInt64() + 1; +} + +void GarrisonMgr::LoadFollowerClassSpecAbilities() +{ + QueryResult result = WorldDatabase.Query("SELECT classSpecId, abilityId FROM garrison_follower_class_spec_abilities"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 garrison follower class spec abilities. DB table `garrison_follower_class_spec_abilities` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + uint32 classSpecId = fields[0].GetUInt32(); + uint32 abilityId = fields[1].GetUInt32(); + + if (!sGarrClassSpecStore.LookupEntry(classSpecId)) + { + TC_LOG_ERROR("sql.sql", "Non-existing GarrClassSpec.db2 entry %u was referenced in `garrison_follower_class_spec_abilities` by row (%u, %u).", classSpecId, classSpecId, abilityId); + continue; + } + + GarrAbilityEntry const* ability = sGarrAbilityStore.LookupEntry(abilityId); + if (!ability) + { + TC_LOG_ERROR("sql.sql", "Non-existing GarrAbility.db2 entry %u was referenced in `garrison_follower_class_spec_abilities` by row (%u, %u).", abilityId, classSpecId, abilityId); + continue; + } + + _garrisonFollowerClassSpecAbilities[classSpecId].push_back(ability); + ++count; + + } while (result->NextRow()); + + for (auto& pair : _garrisonFollowerClassSpecAbilities) + pair.second.sort(); + + TC_LOG_INFO("server.loading", ">> Loaded %u garrison follower class spec abilities.", count); +} diff --git a/src/server/game/Garrison/GarrisonMgr.h b/src/server/game/Garrison/GarrisonMgr.h index a54606dd9b1..68514f4a2f3 100644 --- a/src/server/game/Garrison/GarrisonMgr.h +++ b/src/server/game/Garrison/GarrisonMgr.h @@ -46,17 +46,22 @@ public: GarrBuildingEntry const* GetPreviousLevelBuilding(uint32 buildingType, uint32 currentLevel) const; uint64 GenerateFollowerDbId(); std::list<GarrAbilityEntry const*> RollFollowerAbilities(GarrFollowerEntry const* follower, uint32 quality, uint32 faction, bool initial) const; + std::list<GarrAbilityEntry const*> GetClassSpecAbilities(GarrFollowerEntry const* follower, uint32 faction) const; private: + void InitializeDbIdSequences(); + void LoadFollowerClassSpecAbilities(); + std::unordered_map<uint32 /*garrSiteId*/, std::vector<GarrSiteLevelPlotInstEntry const*>> _garrisonPlotInstBySiteLevel; std::unordered_map<uint32 /*mapId*/, std::unordered_map<uint32 /*garrPlotId*/, GameObjectsEntry const*>> _garrisonPlots; std::unordered_map<uint32 /*garrPlotId*/, std::unordered_set<uint32/*garrBuildingId*/>> _garrisonBuildingsByPlot; std::unordered_map<uint64 /*garrBuildingId | garrSiteLevelPlotInstId << 32*/, uint32 /*garrBuildingPlotInstId*/> _garrisonBuildingPlotInstances; std::unordered_map<uint32 /*buildingType*/, std::vector<GarrBuildingEntry const*>> _garrisonBuildingsByType; std::unordered_map<uint32 /*garrFollowerId*/, GarrAbilities> _garrisonFollowerAbilities[2]; - std::unordered_set<GarrAbilityEntry const*> _garrisonFollowerRandomTraits; + std::unordered_map<uint32 /*classSpecId*/, std::list<GarrAbilityEntry const*>> _garrisonFollowerClassSpecAbilities; + std::set<GarrAbilityEntry const*> _garrisonFollowerRandomTraits; - uint64 _followerDbIdGenerator; + uint64 _followerDbIdGenerator = UI64LIT(0); }; #define sGarrisonMgr GarrisonMgr::Instance() diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 56d865e5d06..65f05e4dbbb 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -248,6 +248,14 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GARRISON_BUILDINGS, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWERS); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWERS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWER_ABILITIES, stmt); + return res; } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index e9ce253d1cf..277ccd4569a 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -690,7 +690,12 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_BLUEPRINTS, "SELECT buildingId FROM character_garrison_blueprints WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_GARRISON_BLUEPRINTS, "INSERT INTO character_garrison_blueprints (guid, buildingId) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_GARRISON_BLUEPRINTS, "DELETE FROM character_garrison_blueprints WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_BUILDINGS, "SELECT plotInstanceId, buildingId, timeBuilt, active FROM character_garrison_buildings WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_BUILDINGS, "SELECT plotInstanceId, buildingId, timeBuilt, active FROM character_garrison_buildings WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_GARRISON_BUILDINGS, "INSERT INTO character_garrison_buildings (guid, plotInstanceId, buildingId, timeBuilt, active) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_GARRISON_BUILDINGS, "DELETE FROM character_garrison_buildings WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWERS, "SELECT dbId, followerId, quality, level, itemLevelWeapon, itemLevelArmor, xp, currentBuilding, currentMission, status FROM character_garrison_followers WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWERS, "INSERT INTO character_garrison_followers (dbId, guid, followerId, quality, level, itemLevelWeapon, itemLevelArmor, xp, currentBuilding, currentMission, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS, "DELETE gfab, gf FROM character_garrison_follower_abilities gfab INNER JOIN character_garrison_followers gf ON gfab.dbId = gf.dbId WHERE gf.guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES, "SELECT gfab.dbId, gfab.abilityId FROM character_garrison_follower_abilities gfab INNER JOIN character_garrison_followers gf ON gfab.dbId = gf.dbId WHERE guid = ? ORDER BY gfab.slot", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES, "INSERT INTO character_garrison_follower_abilities (dbId, abilityId, slot) VALUES (?, ?, ?)", CONNECTION_ASYNC); } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 53c42a0b852..fc9c930e6b1 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -608,6 +608,11 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_GARRISON_BUILDINGS, CHAR_INS_CHARACTER_GARRISON_BUILDINGS, CHAR_DEL_CHARACTER_GARRISON_BUILDINGS, + CHAR_SEL_CHARACTER_GARRISON_FOLLOWERS, + CHAR_INS_CHARACTER_GARRISON_FOLLOWERS, + CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS, + CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES, + CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES, MAX_CHARACTERDATABASE_STATEMENTS }; |