diff options
author | Shauren <shauren.trinity@gmail.com> | 2015-05-17 18:06:28 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2015-05-17 18:06:28 +0200 |
commit | 92bfa759facd47c1e375876c1df4604091811d16 (patch) | |
tree | 731f58769764f0f6852e62de2ae3c1adb05fa61f | |
parent | 051e3b631db74886479c58485fad46b9a326a966 (diff) |
Core/Garrisons: Initial work on garrison followers
-rw-r--r-- | sql/updates/hotfixes/2015_05_17_00_hotfixes.sql | 205 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 10 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 54 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2fmt.h | 8 | ||||
-rw-r--r-- | src/server/game/Garrison/Garrison.cpp | 31 | ||||
-rw-r--r-- | src/server/game/Garrison/Garrison.h | 30 | ||||
-rw-r--r-- | src/server/game/Garrison/GarrisonMgr.cpp | 133 | ||||
-rw-r--r-- | src/server/game/Garrison/GarrisonMgr.h | 13 | ||||
-rw-r--r-- | src/server/game/Server/Packets/GarrisonPackets.cpp | 12 | ||||
-rw-r--r-- | src/server/game/Server/Packets/GarrisonPackets.h | 19 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 1 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 14 | ||||
-rw-r--r-- | src/server/shared/Database/Implementation/HotfixDatabase.cpp | 19 | ||||
-rw-r--r-- | src/server/shared/Database/Implementation/HotfixDatabase.h | 11 |
16 files changed, 553 insertions, 13 deletions
diff --git a/sql/updates/hotfixes/2015_05_17_00_hotfixes.sql b/sql/updates/hotfixes/2015_05_17_00_hotfixes.sql new file mode 100644 index 00000000000..29d18a31703 --- /dev/null +++ b/sql/updates/hotfixes/2015_05_17_00_hotfixes.sql @@ -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; + diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 35978810001..01c65315d35 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -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); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 720cd01531c..a79b3d1c1fa 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -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; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index cad9ca32e69..80c79a94b96 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -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 diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index 19622dcc3d2..7c915b37326 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -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"; diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp index 35c07e1590c..3087de37472 100644 --- a/src/server/game/Garrison/Garrison.cpp +++ b/src/server/game/Garrison/Garrison.cpp @@ -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()); } diff --git a/src/server/game/Garrison/Garrison.h b/src/server/game/Garrison/Garrison.h index 1b12ac62263..348998b276c 100644 --- a/src/server/game/Garrison/Garrison.h +++ b/src/server/game/Garrison/Garrison.h @@ -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__ diff --git a/src/server/game/Garrison/GarrisonMgr.cpp b/src/server/game/Garrison/GarrisonMgr.cpp index f81baa6ddf1..bb2d44d7141 100644 --- a/src/server/game/Garrison/GarrisonMgr.cpp +++ b/src/server/game/Garrison/GarrisonMgr.cpp @@ -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; +} diff --git a/src/server/game/Garrison/GarrisonMgr.h b/src/server/game/Garrison/GarrisonMgr.h index 89fd90f25c3..f037c0867a0 100644 --- a/src/server/game/Garrison/GarrisonMgr.h +++ b/src/server/game/Garrison/GarrisonMgr.h @@ -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() diff --git a/src/server/game/Server/Packets/GarrisonPackets.cpp b/src/server/game/Server/Packets/GarrisonPackets.cpp index 8dff6ff86d4..456149bbdbb 100644 --- a/src/server/game/Server/Packets/GarrisonPackets.cpp +++ b/src/server/game/Server/Packets/GarrisonPackets.cpp @@ -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; +} diff --git a/src/server/game/Server/Packets/GarrisonPackets.h b/src/server/game/Server/Packets/GarrisonPackets.h index 288074284b5..5fc386ab72b 100644 --- a/src/server/game/Server/Packets/GarrisonPackets.h +++ b/src/server/game/Server/Packets/GarrisonPackets.h @@ -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; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 07632e48b40..8c559fe80c7 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -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); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 7af7644d924..3ca8550b3ce 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -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; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 40a8c853bb0..9e4f0608a38 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -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); +} diff --git a/src/server/shared/Database/Implementation/HotfixDatabase.cpp b/src/server/shared/Database/Implementation/HotfixDatabase.cpp index 1afb118a06e..a7043b1d5ff 100644 --- a/src/server/shared/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/shared/Database/Implementation/HotfixDatabase.cpp @@ -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); diff --git a/src/server/shared/Database/Implementation/HotfixDatabase.h b/src/server/shared/Database/Implementation/HotfixDatabase.h index 5d5f00dbf9e..4adf555dc2f 100644 --- a/src/server/shared/Database/Implementation/HotfixDatabase.h +++ b/src/server/shared/Database/Implementation/HotfixDatabase.h @@ -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, |