Core/Garrisons: Initial work on garrison followers

This commit is contained in:
Shauren
2015-05-17 18:06:28 +02:00
parent 051e3b631d
commit 92bfa759fa
16 changed files with 553 additions and 13 deletions

View File

@@ -0,0 +1,205 @@
--
-- Table structure for table `garr_ability`
--
DROP TABLE IF EXISTS `garr_ability`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_ability` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Flags` int(10) unsigned NOT NULL DEFAULT '0',
`Name` text,
`Description` text,
`IconFileDataID` int(10) unsigned NOT NULL DEFAULT '0',
`OtherFactionGarrAbilityID` int(10) unsigned NOT NULL DEFAULT '0',
`GarrAbilityCategoryID` int(10) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_ability`
--
LOCK TABLES `garr_ability` WRITE;
/*!40000 ALTER TABLE `garr_ability` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_ability` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_ability_locale`
--
DROP TABLE IF EXISTS `garr_ability_locale`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_ability_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`Description_lang` text,
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_ability_locale`
--
LOCK TABLES `garr_ability_locale` WRITE;
/*!40000 ALTER TABLE `garr_ability_locale` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_ability_locale` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_class_spec`
--
DROP TABLE IF EXISTS `garr_class_spec`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_class_spec` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`NameMale` text,
`NameFemale` text,
`NameGenderless` text,
`ClassAtlasID` int(10) unsigned NOT NULL DEFAULT '0',
`GarrFollItemSetID` int(10) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_class_spec`
--
LOCK TABLES `garr_class_spec` WRITE;
/*!40000 ALTER TABLE `garr_class_spec` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_class_spec` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_class_spec_locale`
--
DROP TABLE IF EXISTS `garr_class_spec_locale`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_class_spec_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`NameMale_lang` text,
`NameFemale_lang` text,
`NameGenderless_lang` text,
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_class_spec_locale`
--
LOCK TABLES `garr_class_spec_locale` WRITE;
/*!40000 ALTER TABLE `garr_class_spec_locale` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_class_spec_locale` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_follower`
--
DROP TABLE IF EXISTS `garr_follower`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_follower` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`HordeCreatureID` int(10) unsigned NOT NULL DEFAULT '0',
`AllianceCreatureID` int(10) unsigned NOT NULL DEFAULT '0',
`HordeUiAnimRaceInfoID` int(10) unsigned NOT NULL DEFAULT '0',
`AllianceUiAnimRaceInfoID` int(10) unsigned NOT NULL DEFAULT '0',
`Quality` int(10) unsigned NOT NULL DEFAULT '0',
`HordeGarrClassSpecID` int(10) unsigned NOT NULL DEFAULT '0',
`AllianceGarrClassSpecID` int(10) unsigned NOT NULL DEFAULT '0',
`HordeGarrFollItemSetID` int(10) unsigned NOT NULL DEFAULT '0',
`AllianceGarrFollItemSetID` int(10) unsigned NOT NULL DEFAULT '0',
`Level` int(10) unsigned NOT NULL DEFAULT '0',
`ItemLevelWeapon` int(10) unsigned NOT NULL DEFAULT '0',
`ItemLevelArmor` int(10) unsigned NOT NULL DEFAULT '0',
`Unknown1` int(10) unsigned NOT NULL DEFAULT '0',
`Flags` int(10) unsigned NOT NULL DEFAULT '0',
`HordeSourceText` text,
`AllianceSourceText` text,
`Unknown2` int(11) NOT NULL DEFAULT '0',
`Unknown3` int(11) NOT NULL DEFAULT '0',
`HordePortraitIconID` int(10) unsigned NOT NULL DEFAULT '0',
`AlliancePortraitIconID` int(10) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_follower`
--
LOCK TABLES `garr_follower` WRITE;
/*!40000 ALTER TABLE `garr_follower` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_follower` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_follower_locale`
--
DROP TABLE IF EXISTS `garr_follower_locale`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_follower_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`HordeSourceText_lang` text,
`AllianceSourceText_lang` text,
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_follower_locale`
--
LOCK TABLES `garr_follower_locale` WRITE;
/*!40000 ALTER TABLE `garr_follower_locale` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_follower_locale` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `garr_follower_x_ability`
--
DROP TABLE IF EXISTS `garr_follower_x_ability`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `garr_follower_x_ability` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`GarrFollowerID` int(10) unsigned NOT NULL DEFAULT '0',
`GarrAbilityID` int(10) unsigned NOT NULL DEFAULT '0',
`FactionIndex` int(10) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `garr_follower_x_ability`
--
LOCK TABLES `garr_follower_x_ability` WRITE;
/*!40000 ALTER TABLE `garr_follower_x_ability` DISABLE KEYS */;
/*!40000 ALTER TABLE `garr_follower_x_ability` ENABLE KEYS */;
UNLOCK TABLES;

View File

@@ -29,8 +29,12 @@ DB2Storage<BroadcastTextEntry> sBroadcastTextStore("BroadcastText.d
DB2Storage<CurrencyTypesEntry> sCurrencyTypesStore("CurrencyTypes.db2", CurrencyTypesFormat, HOTFIX_SEL_CURRENCY_TYPES);
DB2Storage<CurvePointEntry> sCurvePointStore("CurvePoint.db2", CurvePointFormat, HOTFIX_SEL_CURVE_POINT);
DB2Storage<GameObjectsEntry> sGameObjectsStore("GameObjects.db2", GameObjectsFormat, HOTFIX_SEL_GAMEOBJECTS);
DB2Storage<GarrAbilityEntry> sGarrAbilityStore("GarrAbility.db2", GarrAbilityFormat, HOTFIX_SEL_GARR_ABILITY);
DB2Storage<GarrBuildingEntry> sGarrBuildingStore("GarrBuilding.db2", GarrBuildingFormat, HOTFIX_SEL_GARR_BUILDING);
DB2Storage<GarrBuildingPlotInstEntry> sGarrBuildingPlotInstStore("GarrBuildingPlotInst.db2", GarrBuildingPlotInstFormat, HOTFIX_SEL_GARR_BUILDING_PLOT_INST);
DB2Storage<GarrClassSpecEntry> sGarrClassSpecStore("GarrClassSpec.db2", GarrClassSpecFormat, HOTFIX_SEL_GARR_CLASS_SPEC);
DB2Storage<GarrFollowerEntry> sGarrFollowerStore("GarrFollower.db2", GarrFollowerFormat, HOTFIX_SEL_GARR_FOLLOWER);
DB2Storage<GarrFollowerXAbilityEntry> sGarrFollowerXAbilityStore("GarrFollowerXAbility.db2", GarrFollowerXAbilityFormat, HOTFIX_SEL_GARR_FOLLOWER_X_ABILITY);
DB2Storage<GarrPlotBuildingEntry> sGarrPlotBuildingStore("GarrPlotBuilding.db2", GarrPlotBuildingFormat, HOTFIX_SEL_GARR_PLOT_BUILDING);
DB2Storage<GarrPlotEntry> sGarrPlotStore("GarrPlot.db2", GarrPlotFormat, HOTFIX_SEL_GARR_PLOT);
DB2Storage<GarrPlotInstanceEntry> sGarrPlotInstanceStore("GarrPlotInstance.db2", GarrPlotInstanceFormat, HOTFIX_SEL_GARR_PLOT_INSTANCE);
@@ -141,8 +145,12 @@ void DB2Manager::LoadStores(std::string const& dataPath)
LOAD_DB2(sCurrencyTypesStore);
LOAD_DB2(sCurvePointStore);
LOAD_DB2(sGameObjectsStore);
LOAD_DB2(sGarrBuildingPlotInstStore);
LOAD_DB2(sGarrAbilityStore);
LOAD_DB2(sGarrBuildingStore);
LOAD_DB2(sGarrBuildingPlotInstStore);
LOAD_DB2(sGarrClassSpecStore);
LOAD_DB2(sGarrFollowerStore);
LOAD_DB2(sGarrFollowerXAbilityStore);
LOAD_DB2(sGarrPlotBuildingStore);
LOAD_DB2(sGarrPlotInstanceStore);
LOAD_DB2(sGarrPlotStore);

View File

@@ -25,8 +25,12 @@
extern DB2Storage<BroadcastTextEntry> sBroadcastTextStore;
extern DB2Storage<CurrencyTypesEntry> sCurrencyTypesStore;
extern DB2Storage<GameObjectsEntry> sGameObjectsStore;
extern DB2Storage<GarrAbilityEntry> sGarrAbilityStore;
extern DB2Storage<GarrBuildingEntry> sGarrBuildingStore;
extern DB2Storage<GarrBuildingPlotInstEntry> sGarrBuildingPlotInstStore;
extern DB2Storage<GarrClassSpecEntry> sGarrClassSpecStore;
extern DB2Storage<GarrFollowerEntry> sGarrFollowerStore;
extern DB2Storage<GarrFollowerXAbilityEntry> sGarrFollowerXAbilityStore;
extern DB2Storage<GarrPlotBuildingEntry> sGarrPlotBuildingStore;
extern DB2Storage<GarrPlotEntry> sGarrPlotStore;
extern DB2Storage<GarrPlotInstanceEntry> sGarrPlotInstanceStore;

View File

@@ -100,6 +100,17 @@ struct GameObjectsEntry
LocalizedString* Name; // 23
};
struct GarrAbilityEntry
{
uint32 ID; // 0
uint32 Flags; // 1
LocalizedString* Name; // 2
LocalizedString* Description; // 3
uint32 IconFileDataID; // 4
uint32 OtherFactionGarrAbilityID; // 5
uint32 GarrAbilityCategoryID; // 6
};
struct GarrBuildingEntry
{
uint32 ID; // 0
@@ -137,6 +148,49 @@ struct GarrBuildingPlotInstEntry
DBCPosition2D LandmarkOffset; // 4-5
};
struct GarrClassSpecEntry
{
uint32 ID; // 0
LocalizedString* NameMale; // 1
LocalizedString* NameFemale; // 2
LocalizedString* NameGenderless; // 3
uint32 ClassAtlasID; // 4 UiTextureAtlasMember.db2 ref
uint32 GarrFollItemSetID; // 5
};
struct GarrFollowerEntry
{
uint32 ID; // 0
uint32 HordeCreatureID; // 1
uint32 AllianceCreatureID; // 2
uint32 HordeUiAnimRaceInfoID; // 3
uint32 AllianceUiAnimRaceInfoID; // 4
uint32 Quality; // 5
uint32 HordeGarrClassSpecID; // 6
uint32 AllianceGarrClassSpecID; // 7
uint32 HordeGarrFollItemSetID; // 8
uint32 AllianceGarrFollItemSetID; // 9
uint32 Level; // 10
uint32 ItemLevelWeapon; // 11
uint32 ItemLevelArmor; // 12
uint32 Unknown1; // 13
uint32 Flags; // 14
LocalizedString* HordeSourceText; // 15
LocalizedString* AllianceSourceText; // 16
int32 Unknown2; // 17
int32 Unknown3; // 18
uint32 HordePortraitIconID; // 19
uint32 AlliancePortraitIconID; // 20
};
struct GarrFollowerXAbilityEntry
{
uint32 ID; // 0
uint32 GarrFollowerID; // 1
uint32 GarrAbilityID; // 2
uint32 FactionIndex; // 3
};
struct GarrPlotEntry
{
uint32 ID; // 0

View File

@@ -24,10 +24,14 @@ char const BroadcastTextFormat[] = "nissiiiiiiiii";
char const CurrencyTypesFormat[] = "nisssiiiiiis";
char const CurvePointFormat[] = "niiff";
char const GameObjectsFormat[] = "niiffffffffiiiiiiiiiiiis";
char const GarrAbilityFormat[] = "nissiii";
char const GarrBuildingFormat[] = "niiiiissssiiiiiiiiiiiiii";
char const GarrPlotFormat[] = "niiisiiii";
char const GarrPlotBuildingFormat[] = "nii";
char const GarrBuildingPlotInstFormat[] = "niiiff";
char const GarrClassSpecFormat[] = "nsssii";
char const GarrFollowerFormat[] = "niiiiiiiiiiiiiissiiii";
char const GarrFollowerXAbilityFormat[] = "niii";
char const GarrPlotBuildingFormat[] = "nii";
char const GarrPlotFormat[] = "niiisiiii";
char const GarrPlotInstanceFormat[] = "nis";
char const GarrSiteLevelFormat[] = "niiiiffiiii";
char const GarrSiteLevelPlotInstFormat[] = "niiffi";

View File

@@ -344,6 +344,34 @@ void Garrison::CancelBuildingConstruction(uint32 garrPlotInstanceId)
_owner->SendDirectMessage(buildingRemoved.Write());
}
void Garrison::AddFollower(uint32 garrFollowerId)
{
WorldPackets::Garrison::GarrisonAddFollowerResult addFollowerResult;
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(garrFollowerId);
if (_followers.count(garrFollowerId) || !followerEntry)
{
addFollowerResult.Result = GARRISON_GENERIC_UNKNOWN_ERROR;
_owner->SendDirectMessage(addFollowerResult.Write());
return;
}
Follower& follower = _followers[garrFollowerId];
follower.PacketInfo.DbID = sGarrisonMgr.GenerateFollowerDbId();
follower.PacketInfo.GarrFollowerID = garrFollowerId;
follower.PacketInfo.Quality = followerEntry->Quality; // TODO: handle magic upgrades
follower.PacketInfo.FollowerLevel = followerEntry->Level;
follower.PacketInfo.ItemLevelWeapon = followerEntry->ItemLevelWeapon;
follower.PacketInfo.ItemLevelArmor = followerEntry->ItemLevelArmor;
follower.PacketInfo.Xp = 0;
follower.PacketInfo.CurrentBuildingID = 0;
follower.PacketInfo.CurrentMissionID = 0;
follower.PacketInfo.AbilityID = sGarrisonMgr.RollFollowerAbilities(followerEntry, follower.PacketInfo.Quality, GetFaction(), true);
follower.PacketInfo.FollowerStatus = 0;
addFollowerResult.Follower = follower.PacketInfo;
_owner->SendDirectMessage(addFollowerResult.Write());
}
void Garrison::SendInfo()
{
WorldPackets::Garrison::GetGarrisonInfoResult garrisonInfo;
@@ -359,6 +387,9 @@ void Garrison::SendInfo()
garrisonInfo.Buildings.push_back(plot.BuildingInfo.PacketInfo.get_ptr());
}
for (auto const& p : _followers)
garrisonInfo.Followers.push_back(&p.second.PacketInfo);
_owner->SendDirectMessage(garrisonInfo.Write());
}

View File

@@ -32,6 +32,20 @@ enum GarrisonBuildingFlags
GARRISON_BUILDING_FLAG_NEEDS_PLAN = 0x1
};
enum GarrisonFollowerFlags
{
GARRISON_FOLLOWER_FLAG_UNIQUE = 0x1
};
enum GarrisonAbilityFlags
{
GARRISON_ABILITY_FLAG_TRAIT = 0x01,
GARRISON_ABILITY_CANNOT_ROLL = 0x02,
GARRISON_ABILITY_HORDE_ONLY = 0x04,
GARRISON_ABILITY_ALLIANCE_ONLY = 0x08,
GARRISON_ABILITY_FLAG_CANNOT_REMOVE = 0x10
};
enum GarrisonError
{
GARRISON_SUCCESS = 0,
@@ -44,7 +58,9 @@ enum GarrisonError
GARRISON_ERROR_BLUEPRINT_NOT_KNOWN = 22,
GARRISON_ERROR_BUILDING_EXISTS = 24,
GARRISON_ERROR_NOT_ENOUGH_CURRENCY = 46,
GARRISON_ERROR_NOT_ENOUGH_GOLD = 47
GARRISON_ERROR_NOT_ENOUGH_GOLD = 47,
GARRISON_GENERIC_UNKNOWN_ERROR = 255 // custom value for packets whose handlers only check if error != 0
};
enum GarrisonFollowerStatus
@@ -81,6 +97,11 @@ public:
Building BuildingInfo;
};
struct Follower
{
WorldPackets::Garrison::GarrisonFollower PacketInfo;
};
explicit Garrison(Player* owner);
bool LoadFromDB(PreparedQueryResult garrison, PreparedQueryResult blueprints, PreparedQueryResult buildings);
@@ -93,15 +114,21 @@ public:
void Leave() const;
GarrisonFactionIndex GetFaction() const;
// Plots
std::vector<Plot*> GetPlots();
Plot* GetPlot(uint32 garrPlotInstanceId);
Plot const* GetPlot(uint32 garrPlotInstanceId) const;
// Buildings
void LearnBlueprint(uint32 garrBuildingId);
void UnlearnBlueprint(uint32 garrBuildingId);
void PlaceBuilding(uint32 garrPlotInstanceId, uint32 garrBuildingId);
void CancelBuildingConstruction(uint32 garrPlotInstanceId);
// Followers
void AddFollower(uint32 garrFollowerId);
void SendInfo();
void SendRemoteInfo() const;
void SendBlueprintAndSpecializationData();
@@ -120,6 +147,7 @@ private:
std::unordered_map<uint32 /*garrPlotInstanceId*/, Plot> _plots;
std::unordered_set<uint32 /*garrBuildingId*/> _knownBuildings;
std::unordered_map<uint64 /*dbId*/, Follower> _followers;
};
#endif // Garrison_h__

View File

@@ -16,7 +16,10 @@
*/
#include "GarrisonMgr.h"
#include "Containers.h"
#include "Garrison.h"
#include "ObjectDefines.h"
#include "World.h"
void GarrisonMgr::Initialize()
{
@@ -35,6 +38,23 @@ void GarrisonMgr::Initialize()
for (GarrBuildingEntry const* building : sGarrBuildingStore)
_garrisonBuildingsByType[building->Type].push_back(building);
for (GarrFollowerXAbilityEntry const* followerAbility : sGarrFollowerXAbilityStore)
{
if (GarrAbilityEntry const* ability = sGarrAbilityStore.LookupEntry(followerAbility->GarrAbilityID))
{
if (!(ability->Flags & GARRISON_ABILITY_CANNOT_ROLL) && ability->Flags & GARRISON_ABILITY_FLAG_TRAIT)
_garrisonFollowerRandomTraits.insert(ability);
if (followerAbility->FactionIndex < 2)
{
if (ability->Flags & GARRISON_ABILITY_FLAG_TRAIT)
_garrisonFollowerAbilities[followerAbility->FactionIndex][followerAbility->GarrFollowerID].Traits.insert(ability);
else
_garrisonFollowerAbilities[followerAbility->FactionIndex][followerAbility->GarrFollowerID].Counters.insert(ability);
}
}
}
}
GarrSiteLevelEntry const* GarrisonMgr::GetGarrSiteLevelEntry(uint32 garrSiteId, uint32 level) const
@@ -96,3 +116,116 @@ GarrBuildingEntry const* GarrisonMgr::GetPreviousLevelBuilding(uint32 buildingTy
return nullptr;
}
uint64 GarrisonMgr::GenerateFollowerDbId()
{
if (_followerDbIdGenerator >= std::numeric_limits<uint64>::max())
{
TC_LOG_ERROR("misc", "Garrison follower db id oberflow! Can't continue, shutting down server. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _followerDbIdGenerator++;
}
uint32 const AbilitiesForQuality[][2] =
{
// Counters, Traits
{ 0, 0 },
{ 1, 0 },
{ 1, 1 }, // Uncommon
{ 1, 2 }, // Rare
{ 2, 3 }, // Epic
{ 2, 3 } // Legendary
};
std::list<uint32> GarrisonMgr::RollFollowerAbilities(GarrFollowerEntry const* follower, uint32 quality, uint32 faction, bool initial) const
{
ASSERT(faction < 2);
std::list<uint32> result;
int32 slots[2] = { AbilitiesForQuality[quality][0], AbilitiesForQuality[quality][1] };
GarrAbilities const* abilities = nullptr;
auto itr = _garrisonFollowerAbilities[faction].find(follower->ID);
if (itr != _garrisonFollowerAbilities[faction].end())
abilities = &itr->second;
std::list<uint32> abilityList, forcedAbilities, traitList, forcedTraits;
if (abilities)
{
for (GarrAbilityEntry const* ability : abilities->Counters)
{
if (ability->Flags & GARRISON_ABILITY_HORDE_ONLY && faction != GARRISON_FACTION_INDEX_HORDE)
continue;
else if (ability->Flags & GARRISON_ABILITY_ALLIANCE_ONLY && faction != GARRISON_FACTION_INDEX_ALLIANCE)
continue;
if (ability->Flags & GARRISON_ABILITY_FLAG_CANNOT_REMOVE)
forcedAbilities.push_back(ability->ID);
else
abilityList.push_back(ability->ID);
}
for (GarrAbilityEntry const* ability : abilities->Traits)
{
if (ability->Flags & GARRISON_ABILITY_HORDE_ONLY && faction != GARRISON_FACTION_INDEX_HORDE)
continue;
else if (ability->Flags & GARRISON_ABILITY_ALLIANCE_ONLY && faction != GARRISON_FACTION_INDEX_ALLIANCE)
continue;
if (ability->Flags & GARRISON_ABILITY_FLAG_CANNOT_REMOVE)
forcedTraits.push_back(ability->ID);
else
traitList.push_back(ability->ID);
}
}
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
if (initial)
{
forcedAbilities.splice(forcedAbilities.end(), abilityList);
forcedTraits.splice(forcedTraits.end(), traitList);
}
if (slots[0] > forcedAbilities.size() + abilityList.size())
{
std::list<uint32> classSpecAbilities; // = GetDefaultClassSpecAbilities(follower, faction)
abilityList.splice(abilityList.end(), classSpecAbilities);
abilityList.sort();
abilityList.unique();
Trinity::Containers::RandomResizeList(abilityList, std::max<int32>(0, slots[0] - forcedAbilities.size()));
}
if (slots[1] > forcedTraits.size() + traitList.size())
{
std::list<uint32> genericTraits;
for (GarrAbilityEntry const* ability : _garrisonFollowerRandomTraits)
{
if (ability->Flags & GARRISON_ABILITY_HORDE_ONLY && faction != GARRISON_FACTION_INDEX_HORDE)
continue;
else if (ability->Flags & GARRISON_ABILITY_ALLIANCE_ONLY && faction != GARRISON_FACTION_INDEX_ALLIANCE)
continue;
genericTraits.push_back(ability->ID);
}
traitList.splice(traitList.end(), genericTraits);
traitList.sort();
traitList.unique();
Trinity::Containers::RandomResizeList(traitList, std::max<int32>(0, slots[1] - forcedTraits.size()));
}
result.splice(result.end(), forcedAbilities);
result.splice(result.end(), abilityList);
result.splice(result.end(), forcedTraits);
result.splice(result.end(), traitList);
return result;
}

View File

@@ -18,9 +18,14 @@
#ifndef GarrisonMgr_h__
#define GarrisonMgr_h__
#include "DB2Stores.h"
#include <unordered_set>
#include "DB2Stores.h"
struct GarrAbilities
{
std::unordered_set<GarrAbilityEntry const*> Counters;
std::unordered_set<GarrAbilityEntry const*> Traits;
};
class GarrisonMgr
{
@@ -39,6 +44,8 @@ public:
bool IsPlotMatchingBuilding(uint32 garrPlotId, uint32 garrBuildingId) const;
uint32 GetGarrBuildingPlotInst(uint32 garrBuildingId, uint32 garrSiteLevelPlotInstId) const;
GarrBuildingEntry const* GetPreviousLevelBuilding(uint32 buildingType, uint32 currentLevel) const;
uint64 GenerateFollowerDbId();
std::list<uint32> RollFollowerAbilities(GarrFollowerEntry const* follower, uint32 quality, uint32 faction, bool initial) const;
private:
std::unordered_map<uint32 /*garrSiteId*/, std::vector<GarrSiteLevelPlotInstEntry const*>> _garrisonPlotInstBySiteLevel;
@@ -46,6 +53,10 @@ private:
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;
uint64 _followerDbIdGenerator;
};
#define sGarrisonMgr GarrisonMgr::Instance()

View File

@@ -60,8 +60,8 @@ ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Garrison::GarrisonFollowe
data << uint32(follower.CurrentMissionID);
data << uint32(follower.AbilityID.size());
data << uint32(follower.FollowerStatus);
if (!follower.AbilityID.empty())
data.append(follower.AbilityID.data(), follower.AbilityID.size());
for (uint32 abilityId : follower.AbilityID)
data << uint32(abilityId);
return data;
}
@@ -237,3 +237,11 @@ WorldPacket const* WorldPackets::Garrison::GarrisonPlotRemoved::Write()
return &_worldPacket;
}
WorldPacket const* WorldPackets::Garrison::GarrisonAddFollowerResult::Write()
{
_worldPacket << uint32(Result);
_worldPacket << Follower;
return &_worldPacket;
}

View File

@@ -74,7 +74,7 @@ namespace WorldPackets
uint32 Xp = 0;
uint32 CurrentBuildingID = 0;
uint32 CurrentMissionID = 0;
std::vector<uint32> AbilityID;
std::list<uint32> AbilityID;
uint32 FollowerStatus;
};
@@ -102,9 +102,9 @@ namespace WorldPackets
uint32 FactionIndex = 0;
uint32 NumFollowerActivationsRemaining = 0;
std::vector<GarrisonPlotInfo*> Plots;
std::vector<GarrisonBuildingInfo*> Buildings;
std::vector<GarrisonFollower*> Followers;
std::vector<GarrisonMission*> Missions;
std::vector<GarrisonBuildingInfo const*> Buildings;
std::vector<GarrisonFollower const*> Followers;
std::vector<GarrisonMission const*> Missions;
std::vector<int32> ArchivedMissions;
};
@@ -267,6 +267,17 @@ namespace WorldPackets
uint32 GarrPlotInstanceID = 0;
};
class GarrisonAddFollowerResult final : public ServerPacket
{
public:
GarrisonAddFollowerResult() : ServerPacket(SMSG_GARRISON_ADD_FOLLOWER_RESULT, 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 5 * 4 + 4) { }
WorldPacket const* Write() override;
GarrisonFollower Follower;
uint32 Result = 0;
};
}
}

View File

@@ -1109,7 +1109,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_SPEED_SET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_TIME_SET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_TIME_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ADD_FOLLOWER_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ADD_FOLLOWER_RESULT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ADD_MISSION_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_ASSIGN_FOLLOWER_TO_BUILDING_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GARRISON_BUILDING_ACTIVATED, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);

View File

@@ -407,6 +407,7 @@ class Spell
void EffectDestroyItem(SpellEffIndex effIndex);
void EffectLearnGarrisonBuilding(SpellEffIndex effIndex);
void EffectCreateGarrison(SpellEffIndex effIndex);
void EffectAddGarrisonFollower(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;

View File

@@ -294,7 +294,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //217 SPELL_EFFECT_UPGRADE_GARRISON
&Spell::EffectNULL, //218 SPELL_EFFECT_218
&Spell::EffectNULL, //219 SPELL_EFFECT_219
&Spell::EffectNULL, //220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER
&Spell::EffectAddGarrisonFollower, //220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER
&Spell::EffectNULL, //221 SPELL_EFFECT_221
&Spell::EffectNULL, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
&Spell::EffectNULL, //223 SPELL_EFFECT_CHANGE_ITEM_BONUSES
@@ -5826,3 +5826,15 @@ void Spell::EffectCreateGarrison(SpellEffIndex effIndex)
unitTarget->ToPlayer()->CreateGarrison(GetEffect(effIndex)->MiscValue);
}
void Spell::EffectAddGarrisonFollower(SpellEffIndex effIndex)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->AddFollower(GetEffect(effIndex)->MiscValue);
}

View File

@@ -52,6 +52,11 @@ void HotfixDatabaseConnection::DoPrepareStatements()
"PhaseUseFlags, PhaseID, PhaseGroupID, Type, Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Name FROM gameobjects ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_GAMEOBJECTS, "SELECT ID, Name_lang FROM gameobjects_locale WHERE locale = ?", CONNECTION_SYNCH);
// GarrAbility.db2
PrepareStatement(HOTFIX_SEL_GARR_ABILITY, "SELECT ID, Flags, Name, Description, IconFileDataID, OtherFactionGarrAbilityID, "
"GarrAbilityCategoryID FROM garr_ability ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_GARR_ABILITY, "SELECT ID, Name_lang, Description_lang FROM garr_ability_locale WHERE locale = ?", CONNECTION_SYNCH);
// GarrBuilding.db2
PrepareStatement(HOTFIX_SEL_GARR_BUILDING, "SELECT ID, HordeGameObjectID, AllianceGameObjectID, Unknown, Type, Level, NameAlliance, NameHorde, Description, "
"Tooltip, BuildDuration, CostCurrencyID, CostCurrencyAmount, HordeTexPrefixKitID, AllianceTexPrefixKitID, IconFileDataID, BonusAmount, Flags, "
@@ -64,6 +69,20 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PrepareStatement(HOTFIX_SEL_GARR_BUILDING_PLOT_INST, "SELECT ID, GarrBuildingID, UiTextureAtlasMemberID, GarrSiteLevelPlotInstID, "
"LandmarkOffsetX, LandmarkOffsetY FROM garr_building_plot_inst ORDER BY ID DESC", CONNECTION_SYNCH);
// GarrClassSpec.db2
PrepareStatement(HOTFIX_SEL_GARR_CLASS_SPEC, "SELECT ID, NameMale, NameFemale, NameGenderless, ClassAtlasID, GarrFollItemSetID "
"FROM garr_class_spec ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_GARR_CLASS_SPEC, "SELECT ID, NameMale_lang, NameFemale_lang, NameGenderless_lang FROM garr_class_spec_locale WHERE locale = ?", CONNECTION_SYNCH);
// GarrFollower.db2
PrepareStatement(HOTFIX_SEL_GARR_FOLLOWER, "SELECT ID, HordeCreatureID, AllianceCreatureID, HordeUiAnimRaceInfoID, AllianceUiAnimRaceInfoID, Quality, "
"HordeGarrClassSpecID, AllianceGarrClassSpecID, HordeGarrFollItemSetID, AllianceGarrFollItemSetID, Level, ItemLevelWeapon, ItemLevelArmor, Unknown1, Flags, "
"HordeSourceText, AllianceSourceText, Unknown2, Unknown3, HordePortraitIconID, AlliancePortraitIconID FROM garr_follower ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_GARR_FOLLOWER, "SELECT ID, HordeSourceText_lang, AllianceSourceText_lang FROM garr_follower_locale WHERE locale = ?", CONNECTION_SYNCH);
// GarrFollowerXAbility.db2
PrepareStatement(HOTFIX_SEL_GARR_FOLLOWER_X_ABILITY, "SELECT ID, GarrFollowerID, GarrAbilityID, FactionIndex FROM garr_follower_x_ability ORDER BY ID DESC", CONNECTION_SYNCH);
// GarrPlot.db2
PrepareStatement(HOTFIX_SEL_GARR_PLOT, "SELECT ID, GarrPlotUICategoryID, PlotType, Flags, Name, MinCount, MaxCount, "
"AllianceConstructionGameObjectID, HordeConstructionGameObjectID FROM garr_plot ORDER BY ID DESC", CONNECTION_SYNCH);

View File

@@ -57,11 +57,22 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_GAMEOBJECTS,
HOTFIX_SEL_GAMEOBJECTS_LOCALE,
HOTFIX_SEL_GARR_ABILITY,
HOTFIX_SEL_GARR_ABILITY_LOCALE,
HOTFIX_SEL_GARR_BUILDING,
HOTFIX_SEL_GARR_BUILDING_LOCALE,
HOTFIX_SEL_GARR_BUILDING_PLOT_INST,
HOTFIX_SEL_GARR_CLASS_SPEC,
HOTFIX_SEL_GARR_CLASS_SPEC_LOCALE,
HOTFIX_SEL_GARR_FOLLOWER,
HOTFIX_SEL_GARR_FOLLOWER_LOCALE,
HOTFIX_SEL_GARR_FOLLOWER_X_ABILITY,
HOTFIX_SEL_GARR_PLOT,
HOTFIX_SEL_GARR_PLOT_LOCALE,