From e9876286b940f2048e5fa1391de102efd2567605 Mon Sep 17 00:00:00 2001 From: luis Date: Sat, 14 Mar 2026 07:08:14 -0300 Subject: [PATCH] initial trading post it have mem leak and dragonriding base --- .../master/2026_03_14_00_characters.sql | 35 ++ .../master/2026_03_14_00_hotfixes.sql | 18 + .../Playerbot/Lifecycle/BotSpawner.cpp | 6 + .../Implementation/CharacterDatabase.cpp | 45 ++ .../Implementation/CharacterDatabase.h | 15 + .../Implementation/HotfixDatabase.cpp | 6 + .../Database/Implementation/HotfixDatabase.h | 3 + .../Database/Implementation/WorldDatabase.cpp | 3 + .../Database/Implementation/WorldDatabase.h | 4 + src/server/game/DataStores/DB2LoadInfo.h | 18 + src/server/game/DataStores/DB2Stores.cpp | 23 + src/server/game/DataStores/DB2Stores.h | 2 + src/server/game/DataStores/DB2Structure.h | 13 + src/server/game/Entities/Player/Player.cpp | 11 +- src/server/game/Entities/Player/Player.h | 1 + src/server/game/Entities/Unit/Unit.cpp | 4 +- .../game/PerkProgram/PerkProgramData.cpp | 167 +++++++ src/server/game/PerkProgram/PerkProgramData.h | 75 +++ .../game/PerkProgram/PerkProgramMgr.cpp | 430 ++++++++++++++++++ src/server/game/PerkProgram/PerkProgramMgr.h | 113 +++++ .../game/Spells/Auras/SpellAuraEffects.cpp | 29 +- src/server/game/Spells/SpellMgr.cpp | 11 +- .../scripts/Spells/spell_dragonriding.cpp | 56 ++- 23 files changed, 1072 insertions(+), 16 deletions(-) create mode 100644 sql/updates/characters/master/2026_03_14_00_characters.sql create mode 100644 sql/updates/hotfixes/master/2026_03_14_00_hotfixes.sql create mode 100644 src/server/game/PerkProgram/PerkProgramData.cpp create mode 100644 src/server/game/PerkProgram/PerkProgramData.h create mode 100644 src/server/game/PerkProgram/PerkProgramMgr.cpp create mode 100644 src/server/game/PerkProgram/PerkProgramMgr.h diff --git a/sql/updates/characters/master/2026_03_14_00_characters.sql b/sql/updates/characters/master/2026_03_14_00_characters.sql new file mode 100644 index 000000000..b322b3ec9 --- /dev/null +++ b/sql/updates/characters/master/2026_03_14_00_characters.sql @@ -0,0 +1,35 @@ +-- Perks Program - Purchases +DROP TABLE IF EXISTS `character_perks_purchases`; +CREATE TABLE `character_perks_purchases` ( + `guid` INT UNSIGNED NOT NULL, + `vendor_item_id` INT UNSIGNED NOT NULL, + `purchase_time` INT UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`guid`, `vendor_item_id`), + INDEX `idx_guid` (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Perks Program - Frozen Items +DROP TABLE IF EXISTS `account_perks_frozen_items`; +CREATE TABLE `account_perks_frozen_items` ( + `account_id` INT UNSIGNED NOT NULL, + `vendor_item_id` INT UNSIGNED NOT NULL, + `frozen_time` INT UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`account_id`, `vendor_item_id`), + INDEX `idx_account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Perks Program - Tracked Activities +DROP TABLE IF EXISTS `character_perks_tracked_activities`; +CREATE TABLE `character_perks_tracked_activities` ( + `guid` INT UNSIGNED NOT NULL, + `activity_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`guid`, `activity_id`), + INDEX `idx_guid` (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Perks Program - Activity +DROP TABLE IF EXISTS `perk_program_activity`; +CREATE TABLE `perk_program_activity` ( + `ActivityID` INT UNSIGNED NOT NULL, + PRIMARY KEY (`ActivityID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/sql/updates/hotfixes/master/2026_03_14_00_hotfixes.sql b/sql/updates/hotfixes/master/2026_03_14_00_hotfixes.sql new file mode 100644 index 000000000..bbf5491c1 --- /dev/null +++ b/sql/updates/hotfixes/master/2026_03_14_00_hotfixes.sql @@ -0,0 +1,18 @@ + +DROP TABLE IF EXISTS `perks_vendor_item`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `perks_vendor_item` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `PerksVendorCategoryID` int NOT NULL DEFAULT '0', + `Unknown010001` int unsigned NOT NULL DEFAULT '0', + `ItemID` int unsigned NOT NULL DEFAULT '0', + `Unknown020001` int unsigned NOT NULL DEFAULT '0', + `CreatureDisplayInfoID` int unsigned NOT NULL DEFAULT '0', + `Cost` int unsigned NOT NULL DEFAULT '0', + `UiModelSceneID` int unsigned NOT NULL DEFAULT '0', + `UiGroupInfo` int unsigned NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/src/modules/Playerbot/Lifecycle/BotSpawner.cpp b/src/modules/Playerbot/Lifecycle/BotSpawner.cpp index a48b0274e..57159cdf8 100644 --- a/src/modules/Playerbot/Lifecycle/BotSpawner.cpp +++ b/src/modules/Playerbot/Lifecycle/BotSpawner.cpp @@ -2364,6 +2364,12 @@ ObjectGuid BotSpawner::CreateBotCharacter(uint32 accountId, uint8 race, uint8 cl } } + // CRITICAL FIX: Clear item update queue before save to prevent Item.cpp:1234 crash + // The crash occurs when invalid Item pointers (corrupted/dangling) remain in the + // player's item update queue. This can happen during character creation when + // items are added but not fully initialized. + newChar->GetItemUpdateQueue().clear(); + // Save to database using async-safe PlayerbotCharacterDBInterface CharacterDatabaseTransaction characterTransaction = sPlayerbotCharDB->BeginTransaction(); LoginDatabaseTransaction loginTransaction = LoginDatabase.BeginTransaction(); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 08485966e..04ad31b75 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -965,6 +965,51 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_PLAYER_INITIATIVE_FAVOR, "SELECT COALESCE(SUM(amount), 0) FROM neighborhood_initiative_contributions WHERE initiativeDbId = ? AND playerGuid = ?", CONNECTION_SYNCH); + + // Perks Program - Purchases + PrepareStatement(CHAR_SEL_PERKS_PURCHASES, + "SELECT vendor_item_id, purchase_time FROM character_perks_purchases WHERE guid = ?", + CONNECTION_SYNCH); + PrepareStatement(CHAR_INS_PERKS_PURCHASE, + "INSERT INTO character_perks_purchases (guid, vendor_item_id, purchase_time) VALUES (?, ?, ?)", + CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PERKS_PURCHASE, + "DELETE FROM character_perks_purchases WHERE guid = ? AND vendor_item_id = ?", + CONNECTION_ASYNC); + + // Perks Program - Frozen Items + PrepareStatement(CHAR_SEL_ACCOUNT_PERKS_FROZEN_ITEMS, + "SELECT vendor_item_id FROM account_perks_frozen_items WHERE account_id = ?", + CONNECTION_SYNCH); + PrepareStatement(CHAR_INS_PERKS_FROZEN_ITEM, + "INSERT IGNORE INTO account_perks_frozen_items (account_id, vendor_item_id, frozen_time) VALUES (?, ?, ?)", + CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PERKS_FROZEN_ITEM, + "DELETE FROM account_perks_frozen_items WHERE account_id = ? AND vendor_item_id = ?", + CONNECTION_ASYNC); + + // Perks Program - Tracked Activities + PrepareStatement(CHAR_SEL_PERKS_TRACKED_ACTIVITIES, + "SELECT activity_id FROM character_perks_tracked_activities WHERE guid = ?", + CONNECTION_SYNCH); + PrepareStatement(CHAR_INS_PERKS_TRACKED_ACTIVITY, + "INSERT IGNORE INTO character_perks_tracked_activities (guid, activity_id) VALUES (?, ?)", + CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PERKS_TRACKED_ACTIVITY, + "DELETE FROM character_perks_tracked_activities WHERE guid = ? AND activity_id = ?", + CONNECTION_ASYNC); + + // Perks Program - Activity + PrepareStatement(CHAR_SEL_PERK_PROGRAM_ACTIVITY, + "SELECT ActivityID FROM perk_program_activity", + CONNECTION_SYNCH); + PrepareStatement(CHAR_DEL_PERK_PROGRAM_ACTIVITY, + "DELETE FROM perk_program_activity", + CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_PERK_PROGRAM_ACTIVITY, + "INSERT INTO perk_program_activity (ActivityID) VALUES (?)", + CONNECTION_ASYNC); + // WowCommunity end } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 42997710f..51f75b2a0 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -799,6 +799,21 @@ enum CharacterDatabaseStatements : uint32 CHAR_SEL_PLAYER_INITIATIVE_FAVOR, //WowCommunity end + // Perks Program + CHAR_SEL_PERKS_PURCHASES, + CHAR_INS_PERKS_PURCHASE, + CHAR_DEL_PERKS_PURCHASE, + CHAR_SEL_ACCOUNT_PERKS_FROZEN_ITEMS, + CHAR_INS_PERKS_FROZEN_ITEM, + CHAR_DEL_PERKS_FROZEN_ITEM, + CHAR_SEL_PERKS_TRACKED_ACTIVITIES, + CHAR_INS_PERKS_TRACKED_ACTIVITY, + CHAR_DEL_PERKS_TRACKED_ACTIVITY, + CHAR_SEL_PERK_PROGRAM_ACTIVITY, + CHAR_DEL_PERK_PROGRAM_ACTIVITY, + CHAR_REP_PERK_PROGRAM_ACTIVITY, + //WowCommunity end + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 22caa7a89..9c0a023cd 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -2664,6 +2664,12 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_DRIVE_CAPABILITY_TIER, "SELECT ID, Acceleration, MaxSpeed, DriveCapabilityID, OrderIndex" " FROM drive_capability_tier WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_DRIVE_CAPABILITY_TIER, "SELECT MAX(ID) + 1 FROM drive_capability_tier", CONNECTION_SYNCH); + + // PerksVendorItem.db2 + PrepareStatement(HOTFIX_SEL_PERKS_VENDOR_ITEM, "SELECT ID, PerksVendorCategoryID, Unknown010001, ItemID, Unknown020001, CreatureDisplayInfoID, " + "Cost, UiModelSceneID, UiGroupInfo FROM perks_vendor_item WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PERKS_VENDOR_ITEM, "SELECT MAX(ID) + 1 FROM perks_vendor_item", CONNECTION_SYNCH); + //WowCommunity end } diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 4ba460125..e07a6723e 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -483,6 +483,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_HOLIDAYS, HOTFIX_SEL_HOLIDAYS_MAX_ID, + HOTFIX_SEL_PERKS_VENDOR_ITEM, + HOTFIX_SEL_PERKS_VENDOR_ITEM_MAX_ID, + HOTFIX_SEL_IMPORT_PRICE_ARMOR, HOTFIX_SEL_IMPORT_PRICE_ARMOR_MAX_ID, diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 2bebc2081..80494dabc 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -87,6 +87,9 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WARBAND_REPUTATION_FACTIONS, "SELECT factionId FROM warband_reputation_faction", CONNECTION_SYNCH); + // Perks Program + PrepareStatement(WORLD_SEL_PERKS_VENDOR_ITEMS, "SELECT Id, ItemID, MountSpellID, PetSpellID, TransmogSet, ItemModifiedAppearanceID, ToyItem, Cost, Mon FROM perk_programs_vendor_items", CONNECTION_SYNCH); + } WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags) diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index b33b8e3d6..05496e093 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -92,6 +92,10 @@ enum WorldDatabaseStatements : uint32 WORLD_SEL_QUEST_GIVER_SPAWNS, // SELECT c.guid, c.id, c.position_x, c.position_y, c.position_z, c.map, ct.faction, COALESCE(c.zoneId, 0) as zoneId FROM creature c INNER JOIN creature_template ct ON c.id = ct.entry WHERE ct.npcflag & 2 != 0 WORLD_SEL_WARBAND_REPUTATION_FACTIONS, + // Perks Program Vendor Items + WORLD_SEL_PERKS_VENDOR_ITEMS, + // WowCommunity end + MAX_WORLDDATABASE_STATEMENTS }; diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 321914ebc..02f3547a5 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -4327,6 +4327,24 @@ struct PerksActivityLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 7, &PerksActivityMeta::Instance, HOTFIX_SEL_PERKS_ACTIVITY }; }; +struct PerksVendorItemLoadInfo +{ + static constexpr DB2FieldMeta Fields[9] = + { + {.IsSigned = false, .Type = FT_INT, .Name = "ID" }, + {.IsSigned = true, .Type = FT_BYTE, .Name = "PerksVendorCategoryID" }, + {.IsSigned = true, .Type = FT_INT, .Name = "Unknown010001" }, + {.IsSigned = true, .Type = FT_INT, .Name = "ItemID" }, + {.IsSigned = true, .Type = FT_INT, .Name = "Unknown020001" }, + {.IsSigned = true, .Type = FT_INT, .Name = "CreatureDisplayInfoID" }, + {.IsSigned = true, .Type = FT_INT, .Name = "Cost" }, + {.IsSigned = true, .Type = FT_INT, .Name = "UiModelSceneID" }, + {.IsSigned = true, .Type = FT_INT, .Name = "UiGroupInfo" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 9, &PerksVendorItemMeta::Instance, HOTFIX_SEL_PERKS_VENDOR_ITEM }; +}; + struct PhaseLoadInfo { static constexpr DB2FieldMeta Fields[2] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 9c68bea90..ddaba0b4b 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -261,6 +261,7 @@ DB2Storage sPathStore("Path.db2", &PathLoad DB2Storage sPathNodeStore("PathNode.db2", &PathNodeLoadInfo::Instance); DB2Storage sPathPropertyStore("PathProperty.db2", &PathPropertyLoadInfo::Instance); DB2Storage sPerksActivityStore("PerksActivity.db2", &PerksActivityLoadInfo::Instance); +DB2Storage sPerksVendorItemStore("PerksVendorItem.db2", &PerksVendorItemLoadInfo::Instance); DB2Storage sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance); DB2Storage sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", &PhaseXPhaseGroupLoadInfo::Instance); DB2Storage sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance); @@ -547,6 +548,7 @@ typedef std::tuple WMOAreaTableKey; typedef std::map WMOAreaTableLookupContainer; typedef std::pair HotfixBlobKey; typedef std::map> HotfixBlobMap; +typedef std::unordered_map> PerkProgramActivityXTag; using AllowedHotfixOptionalData = std::pair const& data) /*validator*/>; namespace @@ -648,6 +650,7 @@ namespace std::unordered_map> _pvpStatIdsByMap; //WowCommunity std::unordered_map> _warbandScenePlacementsByScene; + PerkProgramActivityXTag _activityXTag; //WowCommunity end } @@ -994,6 +997,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sPathNodeStore); LOAD_DB2(sPathPropertyStore); LOAD_DB2(sPerksActivityStore); + LOAD_DB2(sPerksVendorItemStore); LOAD_DB2(sPhaseStore); LOAD_DB2(sPhaseXPhaseGroupStore); LOAD_DB2(sPlayerConditionStore); @@ -3721,4 +3725,23 @@ std::vector const* DB2Manager::GetWarbandScen { return Trinity::Containers::MapGetValuePtr(_warbandScenePlacementsByScene, static_cast(warbandSceneId)); } + +std::vector DB2Manager::GetRandomActivitiesXTag() +{ + std::vector activities; + + for (PerksActivityEntry const* activity : sPerksActivityStore) + { + if (activity) + activities.push_back(activity); + } + + if (activities.empty()) + return {}; + + int32 count = std::min(5, static_cast(activities.size())); + Trinity::Containers::RandomResize(activities, count); + + return activities; +} //WowCommunity diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 82f2f0fb1..e06400094 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -203,6 +203,7 @@ TC_GAME_API extern DB2Storage sMythicPlusS TC_GAME_API extern DB2Storage sOverrideSpellDataStore; TC_GAME_API extern DB2Storage sParagonReputationStore; TC_GAME_API extern DB2Storage sPerksActivityStore; +TC_GAME_API extern DB2Storage sPerksVendorItemStore; TC_GAME_API extern DB2Storage sPhaseStore; TC_GAME_API extern DB2Storage sPlayerConditionStore; TC_GAME_API extern DB2Storage sPlayerDataElementAccountStore; @@ -660,6 +661,7 @@ public: std::unordered_set const* GetPVPStatIDsForMap(uint32 mapId) const; //WowCommunity std::vector const* GetWarbandScenePlacements(uint32 warbandSceneId) const; + std::vector GetRandomActivitiesXTag(); //WowCommunity private: friend class DB2HotfixGeneratorBase; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index e25280287..9ae2716a8 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -3249,6 +3249,19 @@ struct PerksActivityEntry int32 Priority; }; +struct PerksVendorItemEntry +{ + int32 ID; + int8 PerksVendorCategoryID; + int32 Unknown010001; + int32 ItemID; + int32 Unknown020001; + int32 CreatureDisplayInfoID; + int32 Cost; + int32 UiModelSceneID; + int32 UiGroupInfo; +}; + struct PhaseEntry { uint32 ID; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 49476661a..6557ec83d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -19461,9 +19461,6 @@ void Player::_LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effe } while (auraResult->NextRow()); } - - // TODO: finish dragonriding - this forces old flight mode - AddAura(404468, this); } void Player::_LoadGlyphAuras() @@ -32571,4 +32568,12 @@ void Player::SendCtrOptions() const SendDirectMessage(ctrOptions.Write()); } +void Player::AddMoveImpulse(Position direction) +{ + WorldPackets::Movement::MoveAddImpulse impulse; + impulse.MoverGUID = GetGUID(); + impulse.SequenceIndex = m_movementCounter++; + impulse.Direction = direction; + SendMessageToSet(impulse.Write(), true); +} //WowCommunity diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7a805a4c2..3ac2a64a6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2032,6 +2032,7 @@ class TC_GAME_API Player final : public Unit, public GridObject void ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::TraitConfig const& newConfig, bool applyTraits, bool consumeCurrencies); void RenameTraitConfig(int32 editedConfigId, std::string&& newName); void DeleteTraitConfig(int32 deletedConfigId); + void AddMoveImpulse(Position direction); void ApplyTraitConfig(int32 configId, bool apply); void ApplyTraitEntry(int32 traitNodeEntryId, int32 rank, int32 grantedRanks, bool apply); void SetActiveCombatTraitConfigID(int32 traitConfigId) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ActiveCombatTraitConfigID), traitConfigId); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 582237e0d..c899d3b3a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8417,7 +8417,9 @@ MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const { if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_GROUND && !(mountFlags.HasFlag(AreaMountFlags::AllowGroundMounts))) continue; - if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLYING && !(mountFlags.HasFlag(AreaMountFlags::AllowFlyingMounts))) + // Allow flying everywhere for players (private server - area flags incomplete in DB2) + if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLYING && !(mountFlags.HasFlag(AreaMountFlags::AllowFlyingMounts)) + && GetTypeId() != TYPEID_PLAYER) continue; if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLOAT && !(mountFlags.HasFlag(AreaMountFlags::AllowSurfaceSwimmingMounts))) continue; diff --git a/src/server/game/PerkProgram/PerkProgramData.cpp b/src/server/game/PerkProgram/PerkProgramData.cpp new file mode 100644 index 000000000..116693587 --- /dev/null +++ b/src/server/game/PerkProgram/PerkProgramData.cpp @@ -0,0 +1,167 @@ +///* +// * Copyright (C) WowCommunity Project +// * +// * This SourceCode is NOT free a software. Please hold everything Private +// * and read our Terms +// */ +// +//#include "PerkProgramData.h" +//#include "PerkProgramMgr.h" +//#include "Containers.h" +//#include "DatabaseEnv.h" +//#include "ObjectMgr.h" +//#include "DB2Stores.h" +//#include +//#include "GameTime.h" +// +//PerkProgramDataStoreMgr::PerkProgramDataStoreMgr() {}; +// +//PerkProgramDataStoreMgr::~PerkProgramDataStoreMgr() {}; +// +//PerkProgramDataStoreMgr* PerkProgramDataStoreMgr::instance() +//{ +// static PerkProgramDataStoreMgr instance; +// return &instance; +//} +// +//namespace +//{ +// std::unordered_map _vendorTemplates; +// std::vector m_activeActivity; +//} +// +//void PerkProgramDataStoreMgr::Initialize() +//{ +// LoadPerkVendorItem(); +// LoadActivities(); +//} +// +//void PerkProgramDataStoreMgr::LoadPerkVendorItem() +//{ +// TC_LOG_INFO("server.loading", "Loading PerkPrograms Vendor Items Templates ..."); +// _vendorTemplates.clear(); +// +// if (!sPerksVendorItemStore.GetNumRows()) +// { +// TC_LOG_WARN("server.loading", ">> PerksVendorItem store is empty, skipping vendor items"); +// return; +// } +// +// for (PerksVendorItemEntry const* itemVendor : sPerksVendorItemStore) +// { +// if (!itemVendor) +// continue; +// +// PerkProgramData::PerkVendorItem vendorItem; +// vendorItem.Id = itemVendor->ID; +// vendorItem.ItemID = itemVendor->ItemID; +// vendorItem.MountSpellID = itemVendor->Unknown010001; +// vendorItem.PetSpellID = itemVendor->Unknown020001; +// vendorItem.TransmogSet = 0; +// vendorItem.ItemModifiedAppearanceID = 0; +// vendorItem.ToyItem = 0; +// vendorItem.Cost = itemVendor->Cost; +// vendorItem.CategoryId = itemVendor->PerksVendorCategoryID; +// +// _vendorTemplates.insert(std::make_pair(itemVendor->ID, vendorItem)); +// } +// +// TC_LOG_INFO("server.loading", ">> Loaded {} PerkPrograms Vendor Items Templates", uint32(_vendorTemplates.size())); +//} +// +//std::unordered_map PerkProgramDataStoreMgr::GetVendorItems() +//{ +// return _vendorTemplates; +//} +// +//PerkProgramData::PerkVendorItem const* PerkProgramDataStoreMgr::GetVendorItem(uint32 entry) +//{ +// auto itr = _vendorTemplates.find(entry); +// if (itr != _vendorTemplates.end()) +// return &itr->second; +// +// return nullptr; +//} +// +//PerkProgramData::PerkVendorItem const* PerkProgramDataStoreMgr::GetRandomVendorItem() +//{ +// if (_vendorTemplates.empty()) +// return nullptr; +// +// auto it = _vendorTemplates.begin(); +// std::advance(it, rand() % _vendorTemplates.size()); +// return &it->second; +//} +// +//std::vector PerkProgramDataStoreMgr::GetActivities() +//{ +// return m_activeActivity; +//} +// +//uint32 PerkProgramDataStoreMgr::GetActivityCriteriatree(uint32 activityId) +//{ +// for (auto const& ac : m_activeActivity) +// { +// if (ac.ActivityID == activityId) +// return ac.CriteriaTree; +// } +// return 0; +//} +// +//uint32 PerkProgramDataStoreMgr::GetActivityCurrencyAmount(uint32 activityId) +//{ +// for (auto const& ac : m_activeActivity) +// { +// if (ac.ActivityID == activityId) +// return ac.CurrencyAmount; +// } +// return 0; +//} +// +//void PerkProgramDataStoreMgr::LoadActivities() +//{ +// CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PERK_PROGRAM_ACTIVITY); +// PreparedQueryResult result = CharacterDatabase.Query(stmt); +// m_activeActivity.clear(); +// if (!result) +// { +// GenerateRandomActivities(false); +// return; +// } +// do +// { +// Field* fields = result->Fetch(); +// uint32 activityId = fields[0].GetUInt32(); +// PerksActivityEntry const* entry = sPerksActivityStore.LookupEntry(activityId); +// if (!entry) +// continue; +// PerkProgramData::ActivityTemplate ac; +// ac.ActivityID = entry->ID; +// ac.CriteriaTree = entry->CriteriaTreeID; +// ac.CurrencyAmount = entry->ThresholdContributionAmount; +// ac.Priority = entry->Priority; +// m_activeActivity.push_back(ac); +// +// } while (result->NextRow()); +//} +// +//void PerkProgramDataStoreMgr::GenerateRandomActivities(bool /*clearOld*/) +//{ +// CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PERK_PROGRAM_ACTIVITY); +// CharacterDatabase.Execute(stmt); +// m_activeActivity.clear(); +// +// for (PerksActivityEntry const* db : sDB2Manager.GetRandomActivitiesXTag()) +// { +// PerkProgramData::ActivityTemplate ac; +// ac.ActivityID = db->ID; +// ac.CriteriaTree = db->CriteriaTreeID; +// ac.CurrencyAmount = db->ThresholdContributionAmount; +// ac.Priority = db->Priority; +// m_activeActivity.push_back(ac); +// +// stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_PERK_PROGRAM_ACTIVITY); +// stmt->setUInt32(0, ac.ActivityID); +// CharacterDatabase.Execute(stmt); +// } +//} diff --git a/src/server/game/PerkProgram/PerkProgramData.h b/src/server/game/PerkProgram/PerkProgramData.h new file mode 100644 index 000000000..dfe832367 --- /dev/null +++ b/src/server/game/PerkProgram/PerkProgramData.h @@ -0,0 +1,75 @@ +///* +// * Copyright (C) WowCommunity Project +// * +// * This SourceCode is NOT free a software. Please hold everything Private +// * and read our Terms +// */ +// +//#ifndef _PERK_PROGRAM_DATA_STORE_H +//#define _PERK_PROGRAM_DATA_STORE_H +// +//#include "PerksProgramPacketsCommon.h" +//#include "PacketUtilities.h" +//#include "DatabaseEnv.h" +//#include +//#include +//#include +// +//class PerkProgramManager; +// +//namespace PerkProgramData +//{ +// struct PerkVendorItem +// { +// uint32 Id; +// uint32 ItemID; +// uint32 MountSpellID; +// uint32 PetSpellID; +// uint32 TransmogSet; +// uint32 ItemModifiedAppearanceID; +// uint32 ToyItem; +// time_t AvailableUntil; +// uint32 Cost; +// uint32 CategoryId; +// +// }; +// +// struct ActivityTemplate +// { +// uint32 ActivityID; +// uint32 CriteriaTree; +// uint32 Priority; +// uint32 CurrencyAmount; +// }; +//}; +// +//class TC_GAME_API PerkProgramDataStoreMgr +//{ +// PerkProgramDataStoreMgr(); +// ~PerkProgramDataStoreMgr(); +// +//public: +// static PerkProgramDataStoreMgr* instance(); +// +// void Initialize(); +// +// std::unordered_map GetVendorItems(); +// PerkProgramData::PerkVendorItem const* GetVendorItem(uint32 entry); +// PerkProgramData::PerkVendorItem const* GetRandomVendorItem(); +// +// std::vector GetActivities(); +// uint32 GetActivityCriteriatree(uint32 activityId); +// uint32 GetActivityCurrencyAmount(uint32 activityId); +// +//private: +// +// void LoadPerkVendorItem(); +// void LoadActivities(); +// void GenerateRandomActivities(bool clearOld); +// time_t m_activityTime; +// +//}; +// +//#define sPerkProgramDataStore PerkProgramDataStoreMgr::instance() +// +//#endif diff --git a/src/server/game/PerkProgram/PerkProgramMgr.cpp b/src/server/game/PerkProgram/PerkProgramMgr.cpp new file mode 100644 index 000000000..a70c47a89 --- /dev/null +++ b/src/server/game/PerkProgram/PerkProgramMgr.cpp @@ -0,0 +1,430 @@ +///* +// * Copyright (C) WowCommunity Project +// * +// * This SourceCode is NOT free a software. Please hold everything Private +// * and read our Terms +// */ +// +//#include "Common.h" +//#include "CollectionMgr.h" +//#include "ObjectMgr.h" +//#include "PerkProgramMgr.h" +//#include "WorldSession.h" +//#include "Guild.h" +//#include "GuildMgr.h" +//#include "Player.h" +//#include "Creature.h" +//#include "PerkProgramData.h" +//#include "DatabaseEnv.h" +//#include "ScriptMgr.h" +//#include "AccountMgr.h" +//#include +//#include "SpellInfo.h" +//#include "SpellMgr.h" +//#include "Language.h" +//#include "SpellPackets.h" +//#include "Chat.h" +//#include "DB2Stores.h" +//#include "Pet.h" +//#include "Item.h" +//#include "MiscPackets.h" +//#include "Unit.h" +//#include "GameTime.h" +//#include "PerksProgramPackets.h" +// +//using namespace PerkPrograms; +// +//PerkProgramManager::PerkProgramManager(WorldSession* session) +//{ +// _session = session; +// if (session->GetPlayer()) +// m_player = session->GetPlayer(); +// +// m_currencyAmount = 0; +//} +// +//PerkProgramManager::~PerkProgramManager() +//{ +//}; +// +//void PerkProgramManager::SendVendorItems(Creature* vendor, std::vector purchasedItems) +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_AVAILABLE_ITEMS; +// +// uint32 cameraId = 0; +// +// switch (vendor->GetEntry()) +// { +// case PerkPrograms::VENDOR_HORDE: +// cameraId = PerkPrograms::CAMERA_HORDE; +// break; +// case PerkPrograms::VENDOR_ALLIANCE: +// cameraId = PerkPrograms::CAMERA_ALLIANCE; +// break; +// default: +// cameraId = 0; +// break; +// } +// perks.Params.VendorItem.VendorGuid = vendor->GetGUID(); +// +// if (cameraId != 0) +// { +// std::list list; +// _session->GetPlayer()->GetCreatureListWithEntryInGrid(list, cameraId, 100); +// +// for (auto cr1 : list) +// { +// perks.Params.VendorItem.ModelSceneCameraGuid = cr1->GetGUID(); +// break; +// } +// } +// +// for (auto items : sPerkProgramDataStore->GetVendorItems()) +// { +// WorldPackets::PerksProgram::PerksVendorItem vendorItem; +// vendorItem.VendorItemID = items.second.Id; +// vendorItem.MountID = items.second.MountSpellID; +// vendorItem.BattlePetSpeciesID = items.second.PetSpellID; +// vendorItem.ItemModifiedAppearanceID = items.second.ItemModifiedAppearanceID; +// vendorItem.ToyID = items.second.ToyItem; +// vendorItem.TransmogSetID = items.second.TransmogSet; +// vendorItem.Price = items.second.Cost; +// vendorItem.CategoryType = items.second.CategoryId; +// vendorItem.Disabled = false; +// vendorItem.AvailableUntil = items.second.AvailableUntil; +// perks.Params.VendorItem.FrozenPerksVendorItems.emplace_back(vendorItem); +// +// } +// +// _session->SendPacket(perks.Write()); +//} +// +//bool PerkProgramManager::IsAccountWideItem(uint32 item) +//{ +// bool level = false; +// if (auto i = sPerkProgramDataStore->GetVendorItem(item)) +// { +// switch (i->CategoryId) +// { +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TOYS: +// case PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS: +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG_SETS: +// level = true; +// break; +// default: +// break; +// } +// } +// return level; +//} +// +//bool PerkProgramManager::IsItemAlreadyOwned(PerkProgramData::PerkVendorItem const* vendorItem) +//{ +// if (!vendorItem) +// return true; +// +// switch (vendorItem->CategoryId) +// { +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TOYS: +// return m_player->GetSession()->GetCollectionMgr()->HasToy(vendorItem->ToyItem); +// // case PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS: +// // return m_player->GetMountCollection()->Has(vendorItem->MountSpellID); +// // case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG_SETS: +// // return m_player->GetSession()->GetCollectionMgr()->HasTransmogSet(vendorItem->TransmogSet); +// // case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG: +// // return m_player->GetSession()->GetCollectionMgr()->HasItemAppearance(vendorItem->ItemModifiedAppearanceID); +// // case PerkPrograms::PERKS_VENDOR_CATEGORY_PETS: +// // return m_player->GetBattlePetCollection()->HasPet(vendorItem->PetSpellID); +// default: +// break; +// } +// +// return false; +//} +// +//void PerkProgramManager::GrantItem(PerkProgramData::PerkVendorItem const* vendorItem) +//{ +// if (!vendorItem) +// return; +// +// switch (vendorItem->CategoryId) +// { +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TOYS: +// { +// m_player->GetSession()->GetCollectionMgr()->AddToy(vendorItem->ToyItem, false, false); +// break; +// } +// case PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS: +// { +// m_player->LearnSpell(vendorItem->MountSpellID, false); +// break; +// } +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG_SETS: +// { +// m_player->GetSession()->GetCollectionMgr()->AddTransmogSet(vendorItem->TransmogSet); +// break; +// } +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG: +// { +// m_player->GetSession()->GetCollectionMgr()->AddItemAppearance(vendorItem->ItemModifiedAppearanceID); +// break; +// } +// case PerkPrograms::PERKS_VENDOR_CATEGORY_PETS: +// { +// // m_player->AddBattlePet(vendorItem->PetSpellID); +// break; +// } +// case PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES: +// { +// m_player->AddItem(vendorItem->ItemID, 1); +// break; +// } +// default: +// break; +// } +//} +// +//void PerkProgramManager::RemoveGrantedItem(PerkProgramData::PerkVendorItem const* vendorItem) +//{ +// if (!vendorItem) +// return; +// +// switch (vendorItem->CategoryId) +// { +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TOYS: +// break; +// case PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS: +// m_player->RemoveSpell(vendorItem->MountSpellID, false, true); +// break; +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG_SETS: +// break; +// case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG: +// break; +// case PerkPrograms::PERKS_VENDOR_CATEGORY_PETS: +// break; +// case PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES: +// m_player->DestroyItemCount(vendorItem->ItemID, 1, true); +// break; +// default: +// break; +// } +//} +// +//void PerkProgramManager::BuyItem(uint32 itemId, ObjectGuid /*vendor*/) +//{ +// auto const* vendorItem = sPerkProgramDataStore->GetVendorItem(itemId); +// if (!vendorItem) +// { +// SendPerksProgramError(1); +// return; +// } +// +// if (m_currencyAmount < vendorItem->Cost) +// { +// SendPerksProgramError(2); +// return; +// } +// +// if (IsItemAlreadyOwned(vendorItem)) +// { +// SendPerksProgramError(3); +// return; +// } +// +// m_currencyAmount -= vendorItem->Cost; +// m_player->ModifyCurrency(PERKS_PROGRAM_CURRENCY, -static_cast(vendorItem->Cost)); +// +// GrantItem(vendorItem); +// +// RecordPurchase(itemId, GameTime::GetGameTime()); +// +// SendBuyItemResult(itemId); +// SendCurrencyRefresh(); +//} +// +//void PerkProgramManager::SendBuyItemResult(int32 itemId) +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_BUY_ITEM; +// perks.Params.BuyItem.VendorItemID = itemId; +// +// WorldPackets::PerksProgram::JamTimePair time; +// time.BuyTime = GameTime::GetGameTime(); +// time.VendorItemID = itemId; +// perks.Params.BuyItem.TimePair.emplace_back(time); +// +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::SendCurrencyRefresh() +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_CURRENCY_REFRESH; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::SendPerksProgramError(uint32 errorCode) +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_ERROR; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::RecordPurchase(uint32 itemId, time_t purchaseTime) +//{ +// m_recentPurchases.push_back(std::make_pair(itemId, purchaseTime)); +//} +// +//void PerkProgramManager::RefundItem(int32 itemId) +//{ +// auto it = std::find_if(m_recentPurchases.begin(), m_recentPurchases.end(), +// [itemId](std::pair const& p) { return p.first == static_cast(itemId); }); +// +// if (it == m_recentPurchases.end()) +// { +// SendPerksProgramError(4); +// return; +// } +// +// if (GameTime::GetGameTime() - it->second > REFUND_WINDOW_SECONDS) +// { +// SendPerksProgramError(5); +// return; +// } +// +// auto const* vendorItem = sPerkProgramDataStore->GetVendorItem(itemId); +// if (!vendorItem) +// return; +// +// RemoveGrantedItem(vendorItem); +// +// m_currencyAmount += vendorItem->Cost; +// m_player->ModifyCurrency(PERKS_PROGRAM_CURRENCY, static_cast(vendorItem->Cost)); +// +// m_recentPurchases.erase(it); +// +// SendRefundSuccess(itemId); +// SendCurrencyRefresh(); +//} +// +//void PerkProgramManager::SendRefundSuccess(int32 itemId) +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_REFUND_SUCCESS; +// perks.Params.BuyItem.VendorItemID = itemId; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::CartCheckout(std::vector const& vendorItemIDs) +//{ +// uint32 totalCost = 0; +// +// for (uint32 itemId : vendorItemIDs) +// { +// auto const* item = sPerkProgramDataStore->GetVendorItem(itemId); +// if (!item) +// { +// SendPerksProgramError(1); +// return; +// } +// totalCost += item->Cost; +// } +// +// if (m_currencyAmount < totalCost) +// { +// SendPerksProgramError(2); +// return; +// } +// +// for (uint32 itemId : vendorItemIDs) +// { +// auto const* item = sPerkProgramDataStore->GetVendorItem(itemId); +// if (!item) +// continue; +// +// if (IsItemAlreadyOwned(item)) +// continue; +// +// m_currencyAmount -= item->Cost; +// m_player->ModifyCurrency(PERKS_PROGRAM_CURRENCY, -static_cast(item->Cost)); +// +// GrantItem(item); +// RecordPurchase(itemId, GameTime::GetGameTime()); +// } +// +// SendCartCheckoutSuccess(); +// SendCurrencyRefresh(); +//} +// +//void PerkProgramManager::SendCartCheckoutSuccess() +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_PURCHASE_CART; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::SendPerksProgramStatus() +//{ +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_CURRENCY_REFRESH; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::SendPendingRewards() +//{ +//} +// +//void PerkProgramManager::SetFrozenItem(int32 itemId, bool clear) +//{ +// if (clear) +// { +// m_frozenItems.clear(); +// } +// else +// { +// auto const* item = sPerkProgramDataStore->GetVendorItem(itemId); +// if (item) +// { +// m_frozenItems.clear(); +// m_frozenItems.push_back(*item); +// } +// } +// +// WorldPackets::PerksProgram::PerksProgramResult perks; +// perks.Type = PerkPrograms::RESULT_FROZEN_ITEM; +// _session->SendPacket(perks.Write()); +//} +// +//void PerkProgramManager::AddTrackedActivity(uint32 activityId) +//{ +// if (std::find(m_trackedActivities.begin(), m_trackedActivities.end(), activityId) == m_trackedActivities.end()) +// { +// m_trackedActivities.push_back(activityId); +// } +//} +// +//void PerkProgramManager::RemoveTrackedActivity(uint32 activityId) +//{ +// auto it = std::find(m_trackedActivities.begin(), m_trackedActivities.end(), activityId); +// if (it != m_trackedActivities.end()) +// { +// m_trackedActivities.erase(it); +// } +//} +// +//void PerkProgramManager::SendPerksProgramActivities() +//{ +// WorldPackets::PerksProgram::PerksProgramActivityUpdate activity; +// +// activity.TimeUntilStart = GameTime::GetGameTime(); +// // activity.TimeUntilEnd = sWorld->GetNextPerksProgramReset(); +// +// for (auto ac : sPerkProgramDataStore->GetActivities()) +// { +// activity.ActivityIDs.push_back(ac.ActivityID); +// +// } +// _session->SendPacket(activity.Write()); +// +//} diff --git a/src/server/game/PerkProgram/PerkProgramMgr.h b/src/server/game/PerkProgram/PerkProgramMgr.h new file mode 100644 index 000000000..1b26e5a34 --- /dev/null +++ b/src/server/game/PerkProgram/PerkProgramMgr.h @@ -0,0 +1,113 @@ +///* +// * Copyright (C) WowCommunity Project +// * +// * This SourceCode is NOT free a software. Please hold everything Private +// * and read our Terms +// */ +// +//#ifndef __TRINITY_PERKPROGRAMMGR_H +//#define __TRINITY_PERKPROGRAMMGR_H +// +//#include "Common.h" +//#include "PerksProgramPacketsCommon.h" +//#include "PerkProgramData.h" +// +//class WorldSession; +//class Player; +//class Creature; +// +//#define PERKS_PROGRAM_CURRENCY 2032 +// +//namespace PerkPrograms +//{ +// enum PerksVendorCategoryType : uint32 +// { +// PERKS_VENDOR_CATEGORY_NONE = 0, +// PERKS_VENDOR_CATEGORY_MOUNTS = 1, +// PERKS_VENDOR_CATEGORY_PETS = 2, +// PERKS_VENDOR_CATEGORY_TOYS = 3, +// PERKS_VENDOR_CATEGORY_TRANSMOG = 4, +// PERKS_VENDOR_CATEGORY_TRANSMOG_SETS = 5, +// PERKS_VENDOR_CATEGORY_CONSUMABLES = 6, +// }; +// +// enum ItemCollectionType : uint32 +// { +// ITEM_COLLECTION_NONE = 0, +// ITEM_COLLECTION_APPEARANCE = 1, +// ITEM_COLLECTION_MOUNT = 2, +// ITEM_COLLECTION_TOY = 3, +// ITEM_COLLECTION_PET = 4, +// ITEM_COLLECTION_HEIRLOOM = 5, +// ITEM_COLLECTION_ILLUSION = 6, +// ITEM_COLLECTION_ENSEMBLE = 7, +// }; +// +// enum NpcVendorAndCameras : uint32 +// { +// CAMERA_HORDE = 183978, +// CAMERA_ALLIANCE = 1, +// VENDOR_HORDE = 185473, +// VENDOR_ALLIANCE = 3, +// }; +// +// enum ResultType : uint32 +// { +// RESULT_NONE = 0, +// RESULT_BUY_ITEM = 2, +// RESULT_AVAILABLE_ITEMS = 5, +// RESULT_PURCHASE_CART = 6, +// RESULT_REFUND_SUCCESS = 7, +// RESULT_ERROR = 10, +// RESULT_CURRENCY_REFRESH = 11, +// RESULT_FROZEN_ITEM = 12, +// RESULT_ACTIVITY_UPDATE = 13, +// }; +// +//} +// +//class TC_GAME_API PerkProgramManager +//{ +// +//public: +// explicit PerkProgramManager(WorldSession* session); +// ~PerkProgramManager(); +// +// void SendVendorItems(Creature* vendor, std::vector purchasedItems); +// void BuyItem(uint32 itemId, ObjectGuid vendor); +// bool IsAccountWideItem(uint32 item); +// +// void SetCurrencyAmount(uint32 amount) { m_currencyAmount = amount; } +// uint32 GetCurrencyAmount() { return m_currencyAmount; } +// +// void SendPerksProgramActivities(); +// void SendPerksProgramStatus(); +// void SendPendingRewards(); +// void SetFrozenItem(int32 itemId, bool clear); +// void RefundItem(int32 itemId); +// void CartCheckout(std::vector const& itemIds); +// void AddTrackedActivity(uint32 activityId); +// void RemoveTrackedActivity(uint32 activityId); +// +//private: +// void GrantItem(PerkProgramData::PerkVendorItem const* vendorItem); +// void RemoveGrantedItem(PerkProgramData::PerkVendorItem const* vendorItem); +// bool IsItemAlreadyOwned(PerkProgramData::PerkVendorItem const* vendorItem); +// void SendBuyItemResult(int32 itemId); +// void SendRefundSuccess(int32 itemId); +// void SendCartCheckoutSuccess(); +// void SendCurrencyRefresh(); +// void SendPerksProgramError(uint32 errorCode); +// void RecordPurchase(uint32 itemId, time_t purchaseTime); +// +// WorldSession* _session; +// Player* m_player; +// uint32 m_currencyAmount; +// std::vector m_frozenItems; +// std::vector> m_recentPurchases; +// std::vector m_trackedActivities; +// +// static constexpr uint32 REFUND_WINDOW_SECONDS = 2 * 60 * 60; +//}; +// +//#endif diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 1d4f96912..85227c359 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2826,6 +2826,28 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo target->SetDriveCapabilityID(mountCapability->DriveCapabilityID, false); target->CastSpell(target, mountCapability->ModSpellAuraID, this); } + + // Private server: always enable flying for players with riding skills + if (Player* player = target->ToPlayer()) + { + if (player->HasSpell(90265) || player->HasSpell(34091) || player->HasSpell(34090)) + { + target->SetCanFly(true); + if (player->HasAura(404468)) // Steady Flight mode + { + target->SetCanAdvFly(false); + } + else // Skyriding mode (default) + { + target->SetCanAdvFly(true); + target->SetCanDoubleJump(true); + target->SetFlightCapabilityID(1, true); + // Cast Vigor aura - required by CasterAuraSpell check for active abilities + if (!player->HasAura(372773)) + player->CastSpell(player, 372773, true); + } + } + } } } else @@ -2848,8 +2870,13 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo if (MountCapabilityEntry const* mountCapability = sMountCapabilityStore.LookupEntry(GetAmount())) target->RemoveAurasDueToSpell(mountCapability->ModSpellAuraID, target->GetGUID()); + // Clear all flight flags on dismount + target->SetCanFly(false); + target->SetCanAdvFly(false); + target->SetCanDoubleJump(false); target->SetFlightCapabilityID(0, true); - target->SetDriveCapabilityID(0, true); + // Remove Vigor aura on dismount + target->RemoveAura(372773); } } diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index fb62ef341..340ed9b92 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3353,6 +3353,11 @@ void SpellMgr::LoadSpellInfoCustomAttributes() const_cast(spellInfo).AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED; } + // Dragonriding flight style auras - applied fresh on login, do not save + for (uint32 spellId : { 404464u, 404468u, 372773u }) + for (SpellInfo const& spellInfo : _GetSpellInfo(spellId)) + const_cast(spellInfo).AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED; + TC_LOG_INFO("server.loading", ">> Loaded SpellInfo custom attributes in {} ms", GetMSTimeDiffToNow(oldMSTime)); } @@ -5176,12 +5181,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx4 |= SPELL_ATTR4_AURA_IS_BUFF; }); - // TODO: temporary, remove with dragonriding - ApplySpellFix({ 404468 }, [](SpellInfo* spellInfo) - { - spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED; - }); - // Sigil of Flame ApplySpellFix({ 204598 }, [](SpellInfo* spellInfo) { diff --git a/src/server/scripts/Spells/spell_dragonriding.cpp b/src/server/scripts/Spells/spell_dragonriding.cpp index 2b867d2f7..da402a1dd 100644 --- a/src/server/scripts/Spells/spell_dragonriding.cpp +++ b/src/server/scripts/Spells/spell_dragonriding.cpp @@ -34,13 +34,58 @@ enum DragonridingSpells SPELL_DRAGONRIDING_WHIRLING_SURGE = 361584, SPELL_DRAGONRIDING_LAUNCH_BOOST = 392752, SPELL_DRAGONRIDING_LIFT_OFF = 374763, + SPELL_STEADY_FLIGHT = 404468, + SPELL_SKYRIDING = 404464, + SPELL_SKYRIDING_BASICS = 376777, + SPELL_SWITCH_FLIGHT_STYLE = 436854, + SPELL_VIGOR = 372773, + SPELL_LIFT_OFF_3 = 386451, }; -// Blizzlike impulse values from sniff data: -// Launch Boost: (0, 0, 45.0) ? pure upward, magnitude 45.0, sent on spell hit (before periodic aura) -// Whirling Surge: magnitude 5.0 per tick ? facing+pitch oriented, 5-6 ticks, periodic aura (3s duration) -// Skyward Ascent: horizontal 12.25 + Z 49.0 ? magnitude ~50.51, single impulse -// Surge Forward: magnitude 30.0 ? facing+pitch oriented, single burst (estimated; not directly observable in sniff) +// Spell 436854 - Switch Flight Style +class spell_switch_flight : public SpellScript +{ + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + + bool hadSkyriding = caster->HasAura(SPELL_SKYRIDING); + + caster->RemoveAura(SPELL_SKYRIDING); + caster->RemoveAura(SPELL_STEADY_FLIGHT); + + if (hadSkyriding) + { + caster->CastSpell(caster, SPELL_STEADY_FLIGHT, true); + caster->RemoveAura(SPELL_VIGOR); + if (caster->IsMounted()) + { + caster->SetCanAdvFly(false); + caster->SetCanDoubleJump(false); + } + } + else + { + caster->CastSpell(caster, SPELL_SKYRIDING, true); + if (!caster->HasAura(SPELL_VIGOR)) + caster->CastSpell(caster, SPELL_VIGOR, true); + if (caster->IsMounted()) + { + caster->SetCanAdvFly(true); + caster->SetCanDoubleJump(true); + caster->SetFlightCapabilityID(1, true); + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_switch_flight::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; static void SendFacingImpulse(Unit* caster, float speed) { @@ -188,6 +233,7 @@ class spell_dragonriding_launch_boost_aura : public AuraScript void AddSC_dragonriding_spell_scripts() { + RegisterSpellScript(spell_switch_flight); RegisterSpellAndAuraScriptPair(spell_dragonriding_whirling_surge, spell_dragonriding_whirling_surge_aura); RegisterSpellAndAuraScriptPair(spell_dragonriding_launch_boost, spell_dragonriding_launch_boost_aura); RegisterSpellScript(spell_dragonriding_surge_forward);