aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2015-05-22 00:17:48 +0200
committerShauren <shauren.trinity@gmail.com>2015-05-22 00:17:48 +0200
commitb3a754cd7bef73c4f29a3fa819d2bac9f173685a (patch)
tree60d1978a3393adbafd9dfe86a7e5ab550306e480
parent1d29328769d505caf4541b0d712972195dda8b70 (diff)
Core/Garrisons: Implemented follower class/spec abilities and saving them to database
-rw-r--r--sql/base/characters_database.sql60
-rw-r--r--sql/updates/characters/2015_05_22_00_characters.sql35
-rw-r--r--sql/updates/world/2015_05_22_00_world.sql91
-rw-r--r--src/server/game/Entities/Player/Player.cpp11
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Garrison/Garrison.cpp90
-rw-r--r--src/server/game/Garrison/Garrison.h3
-rw-r--r--src/server/game/Garrison/GarrisonMgr.cpp100
-rw-r--r--src/server/game/Garrison/GarrisonMgr.h9
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp8
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp7
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.h5
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
};