diff options
author | Meji <alvaro.megias@outlook.com> | 2023-01-29 17:42:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-29 17:42:23 +0100 |
commit | 03918978c166c9fb191117a0cae9ba1285bf0e37 (patch) | |
tree | 4eb31986b057483a11d39c826b3deb9dc0f4f35b | |
parent | b6cd49c34d4d02b41ced58b87103123a1532b56c (diff) |
Core/Players: Several currency fixes and improvements (#28676)
25 files changed, 599 insertions, 345 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index cffd7d63a96..2c14b47c119 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -720,6 +720,8 @@ CREATE TABLE `character_currency` ( `Quantity` int unsigned NOT NULL, `WeeklyQuantity` int unsigned NOT NULL, `TrackedQuantity` int unsigned NOT NULL, + `IncreasedCapQuantity` INT UNSIGNED NOT NULL DEFAULT '0', + `EarnedQuantity` INT UNSIGNED NOT NULL DEFAULT '0', `Flags` tinyint unsigned NOT NULL, PRIMARY KEY (`CharacterGuid`,`Currency`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; @@ -3692,7 +3694,8 @@ INSERT INTO `updates` VALUES ('2022_12_17_00_characters.sql','3E005BD6B9C60653749B0B3C19CBC497092B9CCB','ARCHIVED','2022-12-17 18:26:43',0), ('2022_12_20_00_characters.sql','75A37A085AF1B953926E4352E439C7916B290924','ARCHIVED','2022-12-20 03:10:07',0), ('2022_12_30_00_characters.sql','5F90C2BFFBB8F6CE0A3327A2CAABCD5CA3C2BA60','RELEASED','2022-12-30 22:50:16',0), -('2023_01_28_00_characters.sql','0280F79FD6EC93FFB3CC67B6499CEDA49D582BFC','RELEASED','2023-01-28 00:11:03',0); +('2023_01_28_00_characters.sql','0280F79FD6EC93FFB3CC67B6499CEDA49D582BFC','RELEASED','2023-01-28 00:11:03',0), +('2023_01_29_00_characters.sql','24FA9E0F616BF77AC588A25A8A8699903A19A5FE','RELEASED','2023-01-29 16:31:12',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/master/2023_01_29_00_characters.sql b/sql/updates/characters/master/2023_01_29_00_characters.sql new file mode 100644 index 00000000000..89522507989 --- /dev/null +++ b/sql/updates/characters/master/2023_01_29_00_characters.sql @@ -0,0 +1,3 @@ +ALTER TABLE `character_currency` +ADD `IncreasedCapQuantity` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `TrackedQuantity`, +ADD `EarnedQuantity` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `IncreasedCapQuantity`; diff --git a/sql/updates/world/master/2023_01_29_00_world.sql b/sql/updates/world/master/2023_01_29_00_world.sql new file mode 100644 index 00000000000..b0142da530d --- /dev/null +++ b/sql/updates/world/master/2023_01_29_00_world.sql @@ -0,0 +1,7 @@ +DELETE FROM `world_state` WHERE `ID` IN (19735, 20084, 20838, 22869, 22980); +INSERT INTO `world_state` (`ID`, `DefaultValue`, `MapIDs`, `AreaIDs`, `ScriptName`, `Comment`) VALUES +(19735, 79, -1, NULL, '', 'Covenant Renown (Currency) - Max quantity'), +(20084, 0, -1, NULL, '', 'Shadowlands PvP Weekly Reward Progress (Currency) - Max quantity'), +(20838, 3510, -1, NULL, '', 'Tower Knowledge (Currency) - Max quantity'), +(22869, 245000, -1, NULL, '', 'Conquest (Currency) - Max quantity'), +(22980, 3000, -1, NULL, '', 'Valor (Currency) - Max quantity'); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 4bf020a31ae..edd46912ba0 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -355,9 +355,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); // Currency - PrepareStatement(CHAR_SEL_PLAYER_CURRENCY, "SELECT Currency, Quantity, WeeklyQuantity, TrackedQuantity, Flags FROM character_currency WHERE CharacterGuid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_PLAYER_CURRENCY, "UPDATE character_currency SET Quantity = ?, WeeklyQuantity = ?, TrackedQuantity = ?, Flags = ? WHERE CharacterGuid = ? AND Currency = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_REP_PLAYER_CURRENCY, "REPLACE INTO character_currency (CharacterGuid, Currency, Quantity, WeeklyQuantity, TrackedQuantity, Flags) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_PLAYER_CURRENCY, "SELECT Currency, Quantity, WeeklyQuantity, TrackedQuantity, IncreasedCapQuantity, EarnedQuantity, Flags FROM character_currency WHERE CharacterGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_PLAYER_CURRENCY, "UPDATE character_currency SET Quantity = ?, WeeklyQuantity = ?, TrackedQuantity = ?, IncreasedCapQuantity = ?, EarnedQuantity = ?, Flags = ? WHERE CharacterGuid = ? AND Currency = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_PLAYER_CURRENCY, "REPLACE INTO character_currency (CharacterGuid, Currency, Quantity, WeeklyQuantity, TrackedQuantity, IncreasedCapQuantity, EarnedQuantity, Flags) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PLAYER_CURRENCY, "DELETE FROM character_currency WHERE CharacterGuid = ?", CONNECTION_ASYNC); // Account data diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index 812cddfe1d9..ca94accd24c 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -2330,7 +2330,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 break; } case ModifierTreeType::PlayerHasTrackedCurrencyEqualOrGreaterThan: // 121 - if (referencePlayer->GetTrackedCurrencyCount(reqValue) < secondaryAsset) + if (referencePlayer->GetCurrencyTrackedQuantity(reqValue) < secondaryAsset) return false; break; case ModifierTreeType::PlayerMapInstanceType: // 122 @@ -3023,7 +3023,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; } case ModifierTreeType::PlayerHasCurrencyEqual: // 209 - if (referencePlayer->GetCurrency(reqValue) != secondaryAsset) + if (referencePlayer->GetCurrencyQuantity(reqValue) != secondaryAsset) return false; break; case ModifierTreeType::MinimumAverageItemHighWaterMarkForSpec: // 210 NYI diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 4168bc0b9ec..9935969706b 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -3186,7 +3186,7 @@ bool ConditionMgr::IsPlayerMeetingCondition(Player const* player, PlayerConditio results.fill(true); for (std::size_t i = 0; i < condition->CurrencyID.size(); ++i) if (condition->CurrencyID[i]) - results[i] = player->GetCurrency(condition->CurrencyID[i]) >= condition->CurrencyCount[i]; + results[i] = player->GetCurrencyQuantity(condition->CurrencyID[i]) >= condition->CurrencyCount[i]; if (!PlayerConditionLogic(condition->CurrencyLogic, results)) return false; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 9511f1e0596..289f1be35fe 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1284,6 +1284,60 @@ struct CurrencyTypesEntry uint32 RechargingAmountPerCycle; uint32 RechargingCycleDurationMS; std::array<int32, 2> Flags; + + EnumFlag<CurrencyTypesFlags> GetFlags() const { return static_cast<CurrencyTypesFlags>(Flags[0]); } + EnumFlag<CurrencyTypesFlagsB> GetFlagsB() const { return static_cast<CurrencyTypesFlagsB>(Flags[1]); } + + // Helpers + int32 GetScaler() const + { + return GetFlags().HasFlag(CurrencyTypesFlags::_100_Scaler) ? 100 : 1; + } + + bool HasMaxEarnablePerWeek() const + { + return MaxEarnablePerWeek || GetFlags().HasFlag(CurrencyTypesFlags::ComputedWeeklyMaximum); + } + + bool HasMaxQuantity(bool onLoad = false, bool onUpdateVersion = false) const + { + if (onLoad && GetFlags().HasFlag(CurrencyTypesFlags::IgnoreMaxQtyOnLoad)) + return false; + + if (onUpdateVersion && GetFlags().HasFlag(CurrencyTypesFlags::UpdateVersionIgnoreMax)) + return false; + + return MaxQty || MaxQtyWorldStateID || GetFlags().HasFlag(CurrencyTypesFlags::DynamicMaximum); + } + + bool HasTotalEarned() const + { + return GetFlagsB().HasFlag(CurrencyTypesFlagsB::UseTotalEarnedForEarned); + } + + bool IsAlliance() const + { + return GetFlags().HasFlag(CurrencyTypesFlags::IsAllianceOnly); + } + + bool IsHorde() const + { + return GetFlags().HasFlag(CurrencyTypesFlags::IsHordeOnly); + } + + bool IsSuppressingChatLog(bool onUpdateVersion = false) const + { + if ((onUpdateVersion && GetFlags().HasFlag(CurrencyTypesFlags::SuppressChatMessageOnVersionChange)) || + GetFlags().HasFlag(CurrencyTypesFlags::SuppressChatMessages)) + return true; + + return false; + } + + bool IsTrackingQuantity() const + { + return GetFlags().HasFlag(CurrencyTypesFlags::TrackQuantity); + } }; struct CurveEntry diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index d211af84e69..401a0eff1d4 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -666,6 +666,62 @@ enum class ChrCustomizationReqFlag : int32 DEFINE_ENUM_FLAG(ChrCustomizationReqFlag); +enum CurrencyConsts +{ + CURRENCY_TYPE_ANCIENT_MANA = 1155, + CURRENCY_TYPE_AZERITE = 1553, + + CURRENCY_MAX_CAP_ANCIENT_MANA = 2000 +}; + +enum class CurrencyTypesFlags : uint32 +{ + Tradable = 0x00000001, // NYI + AppearsInLootWindow = 0x00000002, // NYI + ComputedWeeklyMaximum = 0x00000004, // NYI + _100_Scaler = 0x00000008, + NoLowLevelDrop = 0x00000010, // NYI + IgnoreMaxQtyOnLoad = 0x00000020, + LogOnWorldChange = 0x00000040, // NYI + TrackQuantity = 0x00000080, + ResetTrackedQuantity = 0x00000100, // NYI + UpdateVersionIgnoreMax = 0x00000200, + SuppressChatMessageOnVersionChange = 0x00000400, + SingleDropInLoot = 0x00000800, // NYI + HasWeeklyCatchup = 0x00001000, // NYI + DoNotCompressChat = 0x00002000, // NYI + DoNotLogAcquisitionToBi = 0x00004000, // NYI + NoRaidDrop = 0x00008000, // NYI + NotPersistent = 0x00010000, // NYI + Deprecated = 0x00020000, // NYI + DynamicMaximum = 0x00040000, + SuppressChatMessages = 0x00080000, + DoNotToast = 0x00100000, // NYI + DestroyExtraOnLoot = 0x00200000, // NYI + DontShowTotalInTooltip = 0x00400000, // NYI + DontCoalesceInLootWindow = 0x00800000, // NYI + AccountWide = 0x01000000, // NYI + AllowOverflowMailer = 0x02000000, // NYI + HideAsReward = 0x04000000, // NYI + HasWarmodeBonus = 0x08000000, // NYI + IsAllianceOnly = 0x10000000, + IsHordeOnly = 0x20000000, + LimitWarmodeBonusOncePerTooltip = 0x40000000, // NYI + DeprecatedCurrencyFlag = 0x80000000 // this flag itself is deprecated, not currency that has it +}; + +DEFINE_ENUM_FLAG(CurrencyTypesFlags); + +enum class CurrencyTypesFlagsB : uint32 +{ + UseTotalEarnedForEarned = 0x01, + ShowQuestXPGainInTooltip = 0x02, // NYI + NoNotificationMailOnOfflineProgress = 0x04, // NYI + BattlenetVirtualCurrency = 0x08 // NYI +}; + +DEFINE_ENUM_FLAG(CurrencyTypesFlagsB); + enum Curves { CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS = 1718, @@ -2199,15 +2255,6 @@ enum VehicleSeatFlagsB VEHICLE_SEAT_FLAG_B_VEHICLE_PLAYERFRAME_UI = 0x80000000 // Lua_UnitHasVehiclePlayerFrameUI - actually checked for flagsb &~ 0x80000000 }; -// CurrencyTypes.dbc -enum CurrencyTypes -{ - CURRENCY_TYPE_JUSTICE_POINTS = 395, - CURRENCY_TYPE_VALOR_POINTS = 396, - CURRENCY_TYPE_APEXIS_CRYSTALS = 823, - CURRENCY_TYPE_AZERITE = 1553 -}; - enum WorldMapTransformsFlags { WORLD_MAP_TRANSFORMS_FLAG_DUNGEON = 0x04 diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index d3fbe9cbe10..0a036d1c36d 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -308,22 +308,6 @@ enum ItemFlagsCustom ITEM_FLAGS_CU_FOLLOW_LOOT_RULES = 0x0004 // Item will always follow group/master/need before greed looting rules }; -enum CurrencyFlags -{ - CURRENCY_FLAG_TRADEABLE = 0x01, - // ... - CURRENCY_FLAG_HIGH_PRECISION = 0x08, - // ... - CURRENCY_FLAG_COUNT_SEASON_TOTAL = 0x80, -}; - -enum CurrencyCategory -{ - // ... - CURRENCY_CATEGORY_META_CONQUEST = 89, - // ... -}; - enum BAG_FAMILY_MASK { BAG_FAMILY_MASK_NONE = 0x00000000, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 788e4ba63da..43774309bea 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -503,8 +503,6 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac InitRunes(); SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Coinage), sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY)); - SetCreateCurrency(CURRENCY_TYPE_APEXIS_CRYSTALS, sWorld->getIntConfig(CONFIG_CURRENCY_START_APEXIS_CRYSTALS)); - SetCreateCurrency(CURRENCY_TYPE_JUSTICE_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_JUSTICE_POINTS)); // Played time m_Last_tick = GameTime::GetGameTime(); @@ -6809,7 +6807,7 @@ void Player::RewardPlayerWithRewardPack(RewardPackEntry const* rewardPackEntry) if (std::vector<RewardPackXCurrencyTypeEntry const*> const* rewardCurrencyTypes = sDB2Manager.GetRewardPackCurrencyTypesByRewardID(rewardPackEntry->ID)) for (RewardPackXCurrencyTypeEntry const* currency : *rewardCurrencyTypes) - ModifyCurrency(currency->CurrencyTypeID, currency->Quantity); + AddCurrency(currency->CurrencyTypeID, currency->Quantity /* TODO: CurrencyGainSource */); if (std::vector<RewardPackXItemEntry const*> const* rewardPackXItems = sDB2Manager.GetRewardPackItemsByRewardID(rewardPackEntry->ID)) for (RewardPackXItemEntry const* rewardPackXItem : *rewardPackXItems) @@ -6880,7 +6878,9 @@ void Player::_LoadCurrency(PreparedQueryResult result) cur.Quantity = fields[1].GetUInt32(); cur.WeeklyQuantity = fields[2].GetUInt32(); cur.TrackedQuantity = fields[3].GetUInt32(); - cur.Flags = fields[4].GetUInt8(); + cur.IncreasedCapQuantity = fields[4].GetUInt32(); + cur.EarnedQuantity = fields[5].GetUInt32(); + cur.Flags = CurrencyDbFlags(fields[6].GetUInt8()); _currencyStorage.insert(PlayerCurrenciesMap::value_type(currencyID, cur)); @@ -6905,7 +6905,9 @@ void Player::_SaveCurrency(CharacterDatabaseTransaction trans) stmt->setUInt32(2, itr->second.Quantity); stmt->setUInt32(3, itr->second.WeeklyQuantity); stmt->setUInt32(4, itr->second.TrackedQuantity); - stmt->setUInt8(5, itr->second.Flags); + stmt->setUInt32(5, itr->second.IncreasedCapQuantity); + stmt->setUInt32(6, itr->second.EarnedQuantity); + stmt->setUInt8(7, AsUnderlyingType(itr->second.Flags)); trans->Append(stmt); break; case PLAYERCURRENCY_CHANGED: @@ -6913,9 +6915,11 @@ void Player::_SaveCurrency(CharacterDatabaseTransaction trans) stmt->setUInt32(0, itr->second.Quantity); stmt->setUInt32(1, itr->second.WeeklyQuantity); stmt->setUInt32(2, itr->second.TrackedQuantity); - stmt->setUInt8(3, itr->second.Flags); - stmt->setUInt64(4, GetGUID().GetCounter()); - stmt->setUInt16(5, itr->first); + stmt->setUInt32(3, itr->second.IncreasedCapQuantity); + stmt->setUInt32(4, itr->second.EarnedQuantity); + stmt->setUInt8(5, AsUnderlyingType(itr->second.Flags)); + stmt->setUInt64(6, GetGUID().GetCounter()); + stmt->setUInt16(7, itr->first); trans->Append(stmt); break; default: @@ -6926,30 +6930,6 @@ void Player::_SaveCurrency(CharacterDatabaseTransaction trans) } } -void Player::SendNewCurrency(uint32 id) const -{ - PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); - if (itr == _currencyStorage.end()) - return; - - CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(id); - if (!entry) // should never happen - return; - - WorldPackets::Misc::SetupCurrency packet; - WorldPackets::Misc::SetupCurrency::Record record; - record.Type = entry->ID; - record.Quantity = itr->second.Quantity; - record.WeeklyQuantity = itr->second.WeeklyQuantity; - record.MaxWeeklyQuantity = GetCurrencyWeekCap(entry); - record.TrackedQuantity = itr->second.TrackedQuantity; - record.Flags = itr->second.Flags; - - packet.Data.push_back(record); - - SendDirectMessage(packet.Write()); -} - void Player::SendCurrencies() const { WorldPackets::Misc::SetupCurrency packet; @@ -6957,19 +6937,43 @@ void Player::SendCurrencies() const for (PlayerCurrenciesMap::const_iterator itr = _currencyStorage.begin(); itr != _currencyStorage.end(); ++itr) { - CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(itr->first); + CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(itr->first); + + if (!currency) + continue; - // not send init meta currencies. - if (!entry || entry->CategoryID == CURRENCY_CATEGORY_META_CONQUEST) + // Check faction + if ((currency->IsAlliance() && GetTeam() != ALLIANCE) || + (currency->IsHorde() && GetTeam() != HORDE)) continue; + // Check award condition + if (currency->AwardConditionID) + if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(currency->AwardConditionID)) + if (!ConditionMgr::IsPlayerMeetingCondition(this, playerCondition)) + continue; + WorldPackets::Misc::SetupCurrency::Record record; - record.Type = entry->ID; + record.Type = currency->ID; record.Quantity = itr->second.Quantity; - record.WeeklyQuantity = itr->second.WeeklyQuantity; - record.MaxWeeklyQuantity = GetCurrencyWeekCap(entry); - record.TrackedQuantity = itr->second.TrackedQuantity; - record.Flags = itr->second.Flags; + + if ((itr->second.WeeklyQuantity / currency->GetScaler()) > 0) + record.WeeklyQuantity = itr->second.WeeklyQuantity; + + if (currency->HasMaxEarnablePerWeek()) + record.MaxWeeklyQuantity = GetCurrencyWeeklyCap(currency); + + if (currency->IsTrackingQuantity()) + record.TrackedQuantity = itr->second.TrackedQuantity; + + if (currency->HasTotalEarned()) + record.TotalEarned = itr->second.EarnedQuantity; + + if (currency->HasMaxQuantity(true)) + record.MaxQuantity = GetCurrencyMaxQuantity(currency, true); + + record.Flags = AsUnderlyingType(itr->second.Flags); + record.Flags &= ~AsUnderlyingType(CurrencyDbFlags::UnusedFlags); packet.Data.push_back(record); } @@ -6983,176 +6987,228 @@ void Player::SendPvpRewards() const //GetSession()->SendPacket(&packet); } -uint32 Player::GetCurrency(uint32 id) const +void Player::SetCreateCurrency(uint32 id, uint32 amount) { - PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + PlayerCurrenciesMap::iterator itr = _currencyStorage.find(id); if (itr == _currencyStorage.end()) - return 0; - - return itr->second.Quantity; + { + itr = _currencyStorage.emplace(id, PlayerCurrency{}).first; + itr->second.state = PLAYERCURRENCY_NEW; + itr->second.Quantity = amount; + itr->second.WeeklyQuantity = 0; + itr->second.TrackedQuantity = 0; + itr->second.IncreasedCapQuantity = 0; + itr->second.EarnedQuantity = 0; + itr->second.Flags = CurrencyDbFlags(0); + } } -uint32 Player::GetCurrencyOnWeek(uint32 id) const +void Player::ModifyCurrency(uint32 id, int32 amount, CurrencyGainSource gainSource/* = CurrencyGainSource::Cheat*/, CurrencyDestroyReason destroyReason/* = CurrencyDestroyReason::Cheat*/) { - PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); - if (itr == _currencyStorage.end()) - return 0; + if (!amount) + return; - return itr->second.WeeklyQuantity; -} + CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id); + ASSERT(currency); -uint32 Player::GetTrackedCurrencyCount(uint32 id) const -{ - PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); - if (itr == _currencyStorage.end()) - return 0; + // Check faction + if ((currency->IsAlliance() && GetTeam() != ALLIANCE) || + (currency->IsHorde() && GetTeam() != HORDE)) + return; - return itr->second.TrackedQuantity; -} + // Check award condition + if (currency->AwardConditionID) + if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(currency->AwardConditionID)) + if (!ConditionMgr::IsPlayerMeetingCondition(this, playerCondition)) + return; -bool Player::HasCurrency(uint32 id, uint32 count) const -{ - PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); - return itr != _currencyStorage.end() && itr->second.Quantity >= count; -} + bool isGainOnRefund = [&]() -> bool + { + if (gainSource == CurrencyGainSource::ItemRefund || + gainSource == CurrencyGainSource::GarrisonBuildingRefund || + gainSource == CurrencyGainSource::PlayerTraitRefund) + return true; -void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bool ignoreMultipliers/* = false*/) -{ - if (!count) - return; + return false; + }(); - CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id); - ASSERT(currency); + if (amount > 0 && !isGainOnRefund && gainSource != CurrencyGainSource::Vendor) + { + amount *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_GAIN, id); + amount *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT, currency->CategoryID); + } - if (!ignoreMultipliers) - count *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_GAIN, id); + int32 scaler = currency->GetScaler(); // Currency that is immediately converted into reputation with that faction instead if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(currency->FactionID)) { - if (currency->Flags[0] & CURRENCY_FLAG_HIGH_PRECISION) - count /= 100; - GetReputationMgr().ModifyReputation(factionEntry, count, false, true); + amount /= scaler; + GetReputationMgr().ModifyReputation(factionEntry, amount, false, true); return; } + // Azerite if (id == CURRENCY_TYPE_AZERITE) { - if (count > 0) + if (amount > 0) if (Item* heartOfAzeroth = GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH, ItemSearchLocation::Everywhere)) - heartOfAzeroth->ToAzeriteItem()->GiveXP(uint64(count)); + heartOfAzeroth->ToAzeriteItem()->GiveXP(uint64(amount)); return; } - uint32 oldTotalCount = 0; - uint32 oldWeekCount = 0; - uint32 oldTrackedCount = 0; - PlayerCurrenciesMap::iterator itr = _currencyStorage.find(id); if (itr == _currencyStorage.end()) { - PlayerCurrency cur; - cur.state = PLAYERCURRENCY_NEW; - cur.Quantity = 0; - cur.WeeklyQuantity = 0; - cur.TrackedQuantity = 0; - cur.Flags = 0; - _currencyStorage[id] = cur; - itr = _currencyStorage.find(id); - } - else - { - oldTotalCount = itr->second.Quantity; - oldWeekCount = itr->second.WeeklyQuantity; - oldTrackedCount = itr->second.TrackedQuantity; + itr = _currencyStorage.emplace(id, PlayerCurrency{}).first; + itr->second.state = PLAYERCURRENCY_NEW; + itr->second.Quantity = 0; + itr->second.WeeklyQuantity = 0; + itr->second.TrackedQuantity = 0; + itr->second.IncreasedCapQuantity = 0; + itr->second.EarnedQuantity = 0; + itr->second.Flags = CurrencyDbFlags(0); } - // count can't be more then weekCap if used (weekCap > 0) - uint32 weekCap = GetCurrencyWeekCap(currency); - if (weekCap && count > int32(weekCap)) - count = weekCap; + // Weekly cap + uint32 weeklyCap = GetCurrencyWeeklyCap(currency); + if (weeklyCap && amount > 0 && (itr->second.WeeklyQuantity + amount) > weeklyCap) + if (!isGainOnRefund) // Ignore weekly cap for refund + amount = weeklyCap - itr->second.WeeklyQuantity; - // count can't be more then totalCap if used (totalCap > 0) - uint32 totalCap = GetCurrencyTotalCap(currency); - if (totalCap && count > int32(totalCap)) - count = totalCap; + // Max cap + uint32 maxCap = GetCurrencyMaxQuantity(currency, false, gainSource == CurrencyGainSource::UpdatingVersion); + if (maxCap && amount > 0 && (itr->second.Quantity + amount) > maxCap) + amount = maxCap - itr->second.Quantity; - int32 newTrackedCount = int32(oldTrackedCount) + (count > 0 ? count : 0); - if (newTrackedCount < 0) - newTrackedCount = 0; + // Underflow protection + if (amount < 0 && uint32(std::abs(amount)) > itr->second.Quantity) + amount = itr->second.Quantity * -1; - int32 newTotalCount = int32(oldTotalCount) + count; - if (newTotalCount < 0) - newTotalCount = 0; + if (!amount) + return; + + if (itr->second.state != PLAYERCURRENCY_NEW) + itr->second.state = PLAYERCURRENCY_CHANGED; - int32 newWeekCount = int32(oldWeekCount) + (count > 0 ? count : 0); - if (newWeekCount < 0) - newWeekCount = 0; + itr->second.Quantity += amount; - // if we get more then weekCap just set to limit - if (weekCap && int32(weekCap) < newWeekCount) + if (amount > 0 && !isGainOnRefund) // Ignore total values update for refund { - newWeekCount = int32(weekCap); - // weekCap - oldWeekCount always >= 0 as we set limit before! - newTotalCount = oldTotalCount + (weekCap - oldWeekCount); - } + if (weeklyCap) + itr->second.WeeklyQuantity += amount; - // if we get more then totalCap set to maximum; - if (totalCap && int32(totalCap) < newTotalCount) - { - newTotalCount = int32(totalCap); - newWeekCount = weekCap; + if (currency->IsTrackingQuantity()) + itr->second.TrackedQuantity += amount; + + if (currency->HasTotalEarned()) + itr->second.EarnedQuantity += amount; + + UpdateCriteria(CriteriaType::CurrencyGained, id, amount); } - if (uint32(newTotalCount) != oldTotalCount) - { - if (itr->second.state != PLAYERCURRENCY_NEW) - itr->second.state = PLAYERCURRENCY_CHANGED; + CurrencyChanged(id, amount); - CurrencyChanged(id, newTotalCount - itr->second.Quantity); + WorldPackets::Misc::SetCurrency packet; + packet.Type = currency->ID; + packet.Quantity = itr->second.Quantity; + packet.Flags = CurrencyGainFlags::None; // TODO: Check when flags are applied - itr->second.Quantity = newTotalCount; - itr->second.WeeklyQuantity = newWeekCount; - itr->second.TrackedQuantity = newTrackedCount; + if ((itr->second.WeeklyQuantity / currency->GetScaler()) > 0) + packet.WeeklyQuantity = itr->second.WeeklyQuantity; - if (count > 0) - UpdateCriteria(CriteriaType::CurrencyGained, id, count); + if (currency->HasMaxQuantity(false, gainSource == CurrencyGainSource::UpdatingVersion)) + packet.MaxQuantity = GetCurrencyMaxQuantity(currency); - WorldPackets::Misc::SetCurrency packet; - packet.Type = id; - packet.Quantity = newTotalCount; - packet.SuppressChatLog = !printLog; - packet.WeeklyQuantity = newWeekCount; - packet.TrackedQuantity = newTrackedCount; - packet.Flags = itr->second.Flags; - packet.QuantityChange = count; + if (currency->HasTotalEarned()) + packet.TotalEarned = itr->second.EarnedQuantity; - SendDirectMessage(packet.Write()); - } + packet.SuppressChatLog = currency->IsSuppressingChatLog(gainSource == CurrencyGainSource::UpdatingVersion); + packet.QuantityChange = amount; + + if (amount > 0) + packet.QuantityGainSource = gainSource; + else + packet.QuantityLostSource = destroyReason; + + // TODO: FirstCraftOperationID, LastSpendTime & Toasts + + SendDirectMessage(packet.Write()); +} + +void Player::AddCurrency(uint32 id, uint32 amount, CurrencyGainSource gainSource/* = CurrencyGainSource::Cheat*/) +{ + ModifyCurrency(id, amount, gainSource); +} + +void Player::RemoveCurrency(uint32 id, int32 amount, CurrencyDestroyReason destroyReason/* = CurrencyDestroyReason::Cheat*/) +{ + ModifyCurrency(id, -amount, {}, destroyReason); } -void Player::SetCreateCurrency(uint32 id, uint32 count, bool /*printLog*/ /*= true*/) +void Player::IncreaseCurrencyCap(uint32 id, uint32 amount) { + if (!amount) + return; + + CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id); + ASSERT(currency); + + // Check faction + if ((currency->IsAlliance() && GetTeam() != ALLIANCE) || + (currency->IsHorde() && GetTeam() != HORDE)) + return; + + // Check dynamic maximum flag + if (!currency->GetFlags().HasFlag(CurrencyTypesFlags::DynamicMaximum)) + return; + + // Ancient mana maximum cap + if (id == CURRENCY_TYPE_ANCIENT_MANA) + { + uint32 maxQuantity = GetCurrencyMaxQuantity(currency); + + if ((maxQuantity + amount) > CURRENCY_MAX_CAP_ANCIENT_MANA) + amount = CURRENCY_MAX_CAP_ANCIENT_MANA - maxQuantity; + } + PlayerCurrenciesMap::iterator itr = _currencyStorage.find(id); if (itr == _currencyStorage.end()) { PlayerCurrency cur; cur.state = PLAYERCURRENCY_NEW; - cur.Quantity = count; + cur.Quantity = 0; cur.WeeklyQuantity = 0; cur.TrackedQuantity = 0; - cur.Flags = 0; + cur.IncreasedCapQuantity = amount; + cur.EarnedQuantity = 0; + cur.Flags = CurrencyDbFlags(0); _currencyStorage[id] = cur; + itr = _currencyStorage.find(id); + } + else + { + itr->second.IncreasedCapQuantity += amount; } -} -uint32 Player::GetCurrencyWeekCap(uint32 id) const -{ - CurrencyTypesEntry const* entry = sCurrencyTypesStore.LookupEntry(id); - if (!entry) - return 0; + if (itr->second.state != PLAYERCURRENCY_NEW) + itr->second.state = PLAYERCURRENCY_CHANGED; + + WorldPackets::Misc::SetCurrency packet; + packet.Type = currency->ID; + packet.Quantity = itr->second.Quantity; + packet.Flags = CurrencyGainFlags::None; - return GetCurrencyWeekCap(entry); + if ((itr->second.WeeklyQuantity / currency->GetScaler()) > 0) + packet.WeeklyQuantity = itr->second.WeeklyQuantity; + + if (currency->IsTrackingQuantity()) + packet.TrackedQuantity = itr->second.TrackedQuantity; + + packet.MaxQuantity = GetCurrencyMaxQuantity(currency); + packet.SuppressChatLog = currency->IsSuppressingChatLog(); + + SendDirectMessage(packet.Write()); } void Player::ResetCurrencyWeekCap() @@ -7178,34 +7234,77 @@ void Player::ResetCurrencyWeekCap() SendDirectMessage(WorldPackets::Misc::ResetWeeklyCurrency().Write()); } -uint32 Player::GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const +uint32 Player::GetCurrencyQuantity(uint32 id) const { - return currency->MaxEarnablePerWeek; + PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + if (itr == _currencyStorage.end()) + return 0; + + return itr->second.Quantity; } -uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const +uint32 Player::GetCurrencyWeeklyQuantity(uint32 id) const { - uint32 cap = currency->MaxQty; + PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + if (itr == _currencyStorage.end()) + return 0; - switch (currency->ID) - { - case CURRENCY_TYPE_APEXIS_CRYSTALS: - { - uint32 apexiscap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS); - if (apexiscap > 0) - cap = apexiscap; - break; - } - case CURRENCY_TYPE_JUSTICE_POINTS: - { - uint32 justicecap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_JUSTICE_POINTS); - if (justicecap > 0) - cap = justicecap; - break; - } - } + return itr->second.WeeklyQuantity; +} + +uint32 Player::GetCurrencyTrackedQuantity(uint32 id) const +{ + PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + if (itr == _currencyStorage.end()) + return 0; + + return itr->second.TrackedQuantity; +} + +uint32 Player::GetCurrencyIncreasedCapQuantity(uint32 id) const +{ + PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + if (itr == _currencyStorage.end()) + return 0; - return cap; + return itr->second.IncreasedCapQuantity; +} + +uint32 Player::GetCurrencyMaxQuantity(CurrencyTypesEntry const* currency, bool onLoad/* = false*/, bool onUpdateVersion/* = false*/) const +{ + if (!currency->HasMaxQuantity(onLoad, onUpdateVersion)) + return 0; + + uint32 maxQuantity = currency->MaxQty; + if (currency->MaxQtyWorldStateID) + maxQuantity = sWorldStateMgr->GetValue(currency->MaxQtyWorldStateID, GetMap()); + + uint32 increasedCap = 0; + if (currency->GetFlags().HasFlag(CurrencyTypesFlags::DynamicMaximum)) + increasedCap = GetCurrencyIncreasedCapQuantity(currency->ID); + + return maxQuantity + increasedCap; +} + +uint32 Player::GetCurrencyWeeklyCap(uint32 id) const +{ + CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id); + if (!currency) + return 0; + + return GetCurrencyWeeklyCap(currency); +} + +uint32 Player::GetCurrencyWeeklyCap(CurrencyTypesEntry const* currency) const +{ + // TODO: CurrencyTypeFlags::ComputedWeeklyMaximum + return currency->MaxEarnablePerWeek; +} + +bool Player::HasCurrency(uint32 id, uint32 amount) const +{ + PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id); + return itr != _currencyStorage.end() && itr->second.Quantity >= amount; } void Player::SetInGuild(ObjectGuid::LowType guildId) @@ -14850,7 +14949,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew DestroyItemCount(obj.ObjectID, obj.Amount, true); break; case QUEST_OBJECTIVE_CURRENCY: - ModifyCurrency(obj.ObjectID, -int32(obj.Amount), false, true); + RemoveCurrency(obj.ObjectID, obj.Amount, CurrencyDestroyReason::QuestTurnin); break; } } @@ -14887,6 +14986,20 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew } } + CurrencyGainSource currencyGainSource = [&]() -> CurrencyGainSource + { + CurrencyGainSource gainSource = CurrencyGainSource::QuestReward; + + if (quest->IsDaily()) + gainSource = CurrencyGainSource::DailyQuestReward; + else if (quest->IsWeekly()) + gainSource = CurrencyGainSource::WeeklyQuestReward; + else if (quest->IsWorldQuest()) + gainSource = CurrencyGainSource::WorldQuestReward; + + return gainSource; + }(); + switch (rewardType) { case LootItemType::Item: @@ -14918,7 +15031,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew if (sCurrencyTypesStore.HasRecord(rewardId) && quest->GetRewChoiceItemsCount()) for (uint32 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) if (quest->RewardChoiceItemId[i] && quest->RewardChoiceItemType[i] == LootItemType::Currency && quest->RewardChoiceItemId[i] == rewardId) - ModifyCurrency(quest->RewardChoiceItemId[i], quest->RewardChoiceItemCount[i]); + AddCurrency(quest->RewardChoiceItemId[i], quest->RewardChoiceItemCount[i], currencyGainSource); break; } @@ -14928,7 +15041,7 @@ void Player::RewardQuest(Quest const* quest, LootItemType rewardType, uint32 rew for (uint8 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i) if (quest->RewardCurrencyId[i]) - ModifyCurrency(quest->RewardCurrencyId[i], quest->RewardCurrencyCount[i]); + AddCurrency(quest->RewardCurrencyId[i], quest->RewardCurrencyCount[i], currencyGainSource); if (uint32 skill = quest->GetRewardSkillId()) UpdateSkillPro(skill, 1000, quest->GetRewardSkillPoints()); @@ -15999,7 +16112,7 @@ void Player::AdjustQuestObjectiveProgress(Quest const* quest) else if (obj.Type == QUEST_OBJECTIVE_HAVE_CURRENCY) { uint32 reqCurrencyCount = obj.Amount; - uint32 curCurrencyCount = GetCurrency(obj.ObjectID); + uint32 curCurrencyCount = GetCurrencyQuantity(obj.ObjectID); SetQuestObjectiveData(obj, std::min(reqCurrencyCount, curCurrencyCount)); } } @@ -16352,7 +16465,7 @@ void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int3 switch (objectiveType) { case QUEST_OBJECTIVE_CURRENCY: - objectiveIsNowComplete = GetCurrency(objectId) + addCount >= objective.Amount; + objectiveIsNowComplete = GetCurrencyQuantity(objectId) + addCount >= objective.Amount; break; case QUEST_OBJECTIVE_LEARNSPELL: objectiveIsNowComplete = addCount != 0; @@ -22225,7 +22338,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c continue; if (iece->CurrencyID[i]) - ModifyCurrency(iece->CurrencyID[i], -int32(iece->CurrencyCount[i] * stacks), true, true); + RemoveCurrency(iece->CurrencyID[i], iece->CurrencyCount[i] * stacks, CurrencyDestroyReason::Vendor); } } @@ -22391,7 +22504,7 @@ bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorSlot, return false; } - ModifyCurrency(currency, count, true, true); + AddCurrency(currency, count, CurrencyGainSource::Vendor); if (iece) { for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) @@ -22410,7 +22523,7 @@ bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorSlot, if (iece->Flags & (ITEM_EXT_COST_CURRENCY_REQ_IS_SEASON_EARNED_1 << i)) continue; - ModifyCurrency(iece->CurrencyID[i], -int32(iece->CurrencyCount[i]) * stacks, false, true); + RemoveCurrency(iece->CurrencyID[i], iece->CurrencyCount[i] * stacks, CurrencyDestroyReason::Vendor); } } @@ -27483,7 +27596,7 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits:: ModifyMoney(-amount); break; case TraitCurrencyType::CurrencyTypesBased: - ModifyCurrency(traitCurrency->CurrencyTypesID, -amount); + RemoveCurrency(traitCurrency->CurrencyTypesID, amount /* TODO: CurrencyDestroyReason */); break; default: break; @@ -27855,7 +27968,7 @@ void Player::RefundItem(Item* item) uint32 count = iece->CurrencyCount[i]; uint32 currencyid = iece->CurrencyID[i]; if (count && currencyid) - ModifyCurrency(currencyid, count, true, true); + AddCurrency(currencyid, count, CurrencyGainSource::ItemRefund); } // Grant back money @@ -29003,9 +29116,9 @@ uint64 TraitMgr::PlayerDataAccessor::GetMoney() const return _player->GetMoney(); } -int32 TraitMgr::PlayerDataAccessor::GetCurrency(int32 currencyId) const +int32 TraitMgr::PlayerDataAccessor::GetCurrencyQuantity(int32 currencyId) const { - return _player->GetCurrency(currencyId); + return _player->GetCurrencyQuantity(currencyId); } int32 TraitMgr::PlayerDataAccessor::GetLevel() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 85737fe83e6..cf26c304388 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -297,7 +297,9 @@ struct PlayerCurrency uint32 Quantity; uint32 WeeklyQuantity; uint32 TrackedQuantity; - uint8 Flags; + uint32 IncreasedCapQuantity; + uint32 EarnedQuantity; + CurrencyDbFlags Flags; }; typedef std::unordered_map<uint32, PlayerSpellState> PlayerTalentMap; @@ -1434,36 +1436,29 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void AddRefundReference(ObjectGuid it); void DeleteRefundReference(ObjectGuid it); - /// send initialization of new currency for client - void SendNewCurrency(uint32 id) const; - /// send full data about all currencies to client + /// Send full data about all currencies to client void SendCurrencies() const; - /// send conquest currency points and their cap week/arena + /// Send conquest currency points and their cap week/arena void SendPvpRewards() const; - /// return count of currency witch has plr - uint32 GetCurrency(uint32 id) const; - /// return count of currency gaind on current week - uint32 GetCurrencyOnWeek(uint32 id) const; - /// return week cap by currency id - uint32 GetCurrencyWeekCap(uint32 id) const; - /// return tracked currency count by currency id - uint32 GetTrackedCurrencyCount(uint32 id) const; - /// return presence related currency - bool HasCurrency(uint32 id, uint32 count) const; - /// initialize currency count for custom initialization at create character - void SetCreateCurrency(uint32 id, uint32 count, bool printLog = true); + /// Initialize currency amount for custom initialization at create character + void SetCreateCurrency(uint32 id, uint32 amount); + /// Modify currency amount + void ModifyCurrency(uint32 id, int32 amount, CurrencyGainSource gainSource = CurrencyGainSource::Cheat, CurrencyDestroyReason destroyReason = CurrencyDestroyReason::Cheat); + void AddCurrency(uint32 id, uint32 amount, CurrencyGainSource gainSource = CurrencyGainSource::Cheat); + void RemoveCurrency(uint32 id, int32 amount, CurrencyDestroyReason destroyReason = CurrencyDestroyReason::Cheat); + /// Increase currency cap + void IncreaseCurrencyCap(uint32 id, uint32 amount); + /// Reset weekly quantity void ResetCurrencyWeekCap(); - /** - * @name ModifyCurrency - * @brief Change specific currency and send result to client - - * @param id currency entry from CurrencyTypes.dbc - * @param count integer value for adding/removing curent currency - * @param printLog used on SMSG_SET_CURRENCY - * @param ignore gain multipliers - */ - void ModifyCurrency(uint32 id, int32 count, bool printLog = true, bool ignoreMultipliers = false); + uint32 GetCurrencyQuantity(uint32 id) const; + uint32 GetCurrencyWeeklyQuantity(uint32 id) const; + uint32 GetCurrencyTrackedQuantity(uint32 id) const; + uint32 GetCurrencyIncreasedCapQuantity(uint32 id) const; + uint32 GetCurrencyMaxQuantity(CurrencyTypesEntry const* currency, bool onLoad = false, bool onUpdateVersion = false) const; + uint32 GetCurrencyWeeklyCap(uint32 id) const; + uint32 GetCurrencyWeeklyCap(CurrencyTypesEntry const* currency) const; + bool HasCurrency(uint32 id, uint32 amount) const; void SetInvSlot(uint32 slot, ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::InvSlots, slot), guid); } @@ -3003,22 +2998,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> PlayerCurrenciesMap _currencyStorage; - /** - * @name GetCurrencyWeekCap - * @brief return week cap for selected currency - - * @param CurrencyTypesEntry for which to retrieve weekly cap - */ - uint32 GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const; - - /* - * @name GetCurrencyTotalCap - * @brief return total cap for selected currency - - * @param CurrencyTypesEntry for which to retrieve total cap - */ - uint32 GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const; - VoidStorageItem* _voidStorageItems[VOID_STORAGE_MAX_SLOT]; std::vector<Item*> m_itemUpdateQueue; diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp index 9657ea76447..031133adfca 100644 --- a/src/server/game/Garrison/Garrison.cpp +++ b/src/server/game/Garrison/Garrison.cpp @@ -407,7 +407,7 @@ void Garrison::PlaceBuilding(uint32 garrPlotInstanceId, uint32 garrBuildingId) if (GameObject* go = plot->CreateGameObject(map, GetFaction())) map->AddToMap(go); - _owner->ModifyCurrency(building->CurrencyTypeID, -building->CurrencyQty, false, true); + _owner->RemoveCurrency(building->CurrencyTypeID, building->CurrencyQty, CurrencyDestroyReason::Garrison); _owner->ModifyMoney(-building->GoldCost * GOLD, false); if (oldBuildingId) @@ -447,7 +447,7 @@ void Garrison::CancelBuildingConstruction(uint32 garrPlotInstanceId) GarrBuildingEntry const* constructing = sGarrBuildingStore.AssertEntry(buildingRemoved.GarrBuildingID); // Refund construction/upgrade cost - _owner->ModifyCurrency(constructing->CurrencyTypeID, constructing->CurrencyQty, false, true); + _owner->AddCurrency(constructing->CurrencyTypeID, constructing->CurrencyQty, CurrencyGainSource::GarrisonBuildingRefund); _owner->ModifyMoney(constructing->GoldCost * GOLD, false); if (constructing->UpgradeLevel > 1) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 7146bab0105..f996e5d2380 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -6561,7 +6561,113 @@ enum VoidTransferError VOID_TRANSFER_ERROR_TRANSFER_UNKNOWN = 9 }; -#define CURRENCY_PRECISION 100 +enum class CurrencyDbFlags : uint8 +{ + None = 0x00, + IgnoreMaxQtyOnload = 0x01, + Reuse1 = 0x02, + InBackpack = 0x04, + UnusedInUI = 0x08, + Reuse2 = 0x10, + + UnusedFlags = (IgnoreMaxQtyOnload | Reuse1 | Reuse2), + ClientFlags = (0x1F & ~UnusedFlags) +}; + +DEFINE_ENUM_FLAG(CurrencyDbFlags); + +enum class CurrencyDestroyReason : uint32 +{ + Cheat = 0, + Spell = 1, + VersionUpdate = 2, + QuestTurnin = 3, + Vendor = 4, + Trade = 5, + Capped = 6, + Garrison = 7, + DroppedToCorpse = 8, + BonusRoll = 9, + FactionConversion = 10, + FulfillCraftingOrder = 11, + Last = 12 +}; + +enum class CurrencyGainSource : uint32 +{ + ConvertOldItem = 0, + ConvertOldPvPCurrency = 1, + ItemRefund = 2, + QuestReward = 3, + Cheat = 4, + Vendor = 5, + PvPKillCredit = 6, + PvPMetaCredit = 7, + PvPScriptedAward = 8, + Loot = 9, + UpdatingVersion = 10, + LFGReward = 11, + Trade = 12, + Spell = 13, + ItemDeletion = 14, + RatedBattleground = 15, + RandomBattleground = 16, + Arena = 17, + ExceededMaxQty = 18, + PvPCompletionBonus = 19, + Script = 20, + GuildBankWithdrawal = 21, + Pushloot = 22, + GarrisonBuilding = 23, + PvPDrop = 24, + GarrisonFollowerActivation = 25, + GarrisonBuildingRefund = 26, + GarrisonMissionReward = 27, + GarrisonResourceOverTime = 28, + QuestRewardIgnoreCaps = 29, + GarrisonTalent = 30, + GarrisonWorldQuestBonus = 31, + PvPHonorReward = 32, + BonusRoll = 33, + AzeriteRespec = 34, + WorldQuestReward = 35, + WorldQuestRewardIgnoreCaps = 36, + FactionConversion = 37, + DailyQuestReward = 38, + DailyQuestWarModeReward = 39, + WeeklyQuestReward = 40, + WeeklyQuestWarModeReward = 41, + AccountCopy = 42, + WeeklyRewardChest = 43, + GarrisonTalentTreeReset = 44, + DailyReset = 45, + AddConduitToCollection = 46, + Barbershop = 47, + ConvertItemsToCurrencyValue = 48, + PvPTeamContribution = 49, + Transmogrify = 50, + AuctionDeposit = 51, + PlayerTrait = 52, + PhBuffer_53 = 53, + PhBuffer_54 = 54, + RenownRepGain = 55, + CraftingOrder = 56, + CatalystBalancing = 57, + CatalystCraft = 58, + ProfessionInitialAward = 59, + PlayerTraitRefund = 60, + Last = 61 +}; + +enum class CurrencyGainFlags : uint32 +{ + None = 0x00, + BonusAward = 0x01, + DroppedFromDeath = 0x02, + FromAccountServer = 0x04 +}; + +DEFINE_ENUM_FLAG(CurrencyGainFlags); enum PartyResult { diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index a90f451d2c9..fb7cbedf0ce 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -268,7 +268,7 @@ int32 ReputationMgr::GetRenownLevel(FactionEntry const* renownFactionEntry) cons return 0; if (CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(renownFactionEntry->RenownCurrencyID)) - return _player->GetCurrency(currency->ID); + return _player->GetCurrencyQuantity(currency->ID); return 0; } @@ -291,7 +291,7 @@ int32 ReputationMgr::GetRenownMaxLevel(FactionEntry const* renownFactionEntry) c return 0; if (CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(renownFactionEntry->RenownCurrencyID)) - return currency->MaxQty; + return _player->GetCurrencyMaxQuantity(currency); return 0; } @@ -518,7 +518,7 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, in if (itr != _factions.end()) { // Ignore renown reputation already raised to the maximum level - if (HasMaximumRenownReputation(factionEntry)) + if (HasMaximumRenownReputation(factionEntry) && standing > 0) { itr->second.needSend = false; itr->second.needSave = false; @@ -583,8 +583,9 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, in itr->second.VisualStandingIncrease = reputationChange; + // If the reputation is decreased by command, we will send CurrencyDestroyReason::Cheat if (oldRenownLevel != newRenownLevel) - _player->ModifyCurrency(currency->ID, newRenownLevel - oldRenownLevel, false); + _player->ModifyCurrency(currency->ID, newRenownLevel - oldRenownLevel, CurrencyGainSource::RenownRepGain, CurrencyDestroyReason::Cheat); } } diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index dc07825fdf5..b8d00d817eb 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -50,14 +50,19 @@ WorldPacket const* WorldPackets::Misc::SetCurrency::Write() _worldPacket << int32(Type); _worldPacket << int32(Quantity); _worldPacket << uint32(Flags); + _worldPacket << uint32(Toasts.size()); + + for (WorldPackets::Item::UiEventToast const& toast : Toasts) + _worldPacket << toast; + _worldPacket.WriteBit(WeeklyQuantity.has_value()); _worldPacket.WriteBit(TrackedQuantity.has_value()); _worldPacket.WriteBit(MaxQuantity.has_value()); _worldPacket.WriteBit(TotalEarned.has_value()); _worldPacket.WriteBit(SuppressChatLog); _worldPacket.WriteBit(QuantityChange.has_value()); - _worldPacket.WriteBit(QuantityLostSource.has_value()); _worldPacket.WriteBit(QuantityGainSource.has_value()); + _worldPacket.WriteBit(QuantityLostSource.has_value()); _worldPacket.WriteBit(FirstCraftOperationID.has_value()); _worldPacket.WriteBit(LastSpendTime.has_value()); _worldPacket.FlushBits(); @@ -77,12 +82,12 @@ WorldPacket const* WorldPackets::Misc::SetCurrency::Write() if (QuantityChange) _worldPacket << int32(*QuantityChange); - if (QuantityLostSource) - _worldPacket << int32(*QuantityLostSource); - if (QuantityGainSource) _worldPacket << int32(*QuantityGainSource); + if (QuantityLostSource) + _worldPacket << int32(*QuantityLostSource); + if (FirstCraftOperationID) _worldPacket << uint32(*FirstCraftOperationID); @@ -112,7 +117,7 @@ WorldPacket const* WorldPackets::Misc::SetupCurrency::Write() _worldPacket.WriteBit(data.MaxQuantity.has_value()); _worldPacket.WriteBit(data.TotalEarned.has_value()); _worldPacket.WriteBit(data.LastSpendTime.has_value()); - _worldPacket.WriteBits(data.Flags, 5); + _worldPacket.WriteBits(uint8(data.Flags), 5); _worldPacket.FlushBits(); if (data.WeeklyQuantity) diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index f4b089371b7..fe65386f2d6 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -31,7 +31,6 @@ #include <array> #include <map> -enum MountStatusFlags : uint8; enum UnitStandStateType : uint8; enum WeatherState : uint32; @@ -104,15 +103,15 @@ namespace WorldPackets int32 Type = 0; int32 Quantity = 0; - uint32 Flags = 0; + CurrencyGainFlags Flags = CurrencyGainFlags(0); std::vector<Item::UiEventToast> Toasts; Optional<int32> WeeklyQuantity; Optional<int32> TrackedQuantity; Optional<int32> MaxQuantity; Optional<int32> TotalEarned; Optional<int32> QuantityChange; - Optional<int32> QuantityGainSource; - Optional<int32> QuantityLostSource; + Optional<CurrencyGainSource> QuantityGainSource; + Optional<CurrencyDestroyReason> QuantityLostSource; Optional<uint32> FirstCraftOperationID; Optional<Timestamp<>> LastSpendTime; bool SuppressChatLog = false; @@ -141,7 +140,7 @@ namespace WorldPackets Optional<int32> MaxQuantity; Optional<int32> TotalEarned; Optional<Timestamp<>> LastSpendTime; - uint8 Flags = 0; // 0 = none, + uint8 Flags = 0; }; SetupCurrency() : ServerPacket(SMSG_SETUP_CURRENCY, 22) { } diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index 455cd2d688b..3b4b4b34f74 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -442,7 +442,7 @@ enum AuraType : uint32 SPELL_AURA_MOD_MONEY_GAIN = 348, // Modifies gold gains from source: [Misc = 0, Quests][Misc = 1, Loot] SPELL_AURA_MOD_CURRENCY_GAIN = 349, SPELL_AURA_350 = 350, - SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT = 351, // NYI + SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT = 351, SPELL_AURA_352 = 352, SPELL_AURA_MOD_CAMOUFLAGE = 353, // NYI SPELL_AURA_MOD_HEALING_DONE_PCT_VERSUS_TARGET_HEALTH = 354, // Restoration Shaman mastery - mod healing based on target's health (less = more healing) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a894a3037e9..1d968d07e67 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5349,7 +5349,7 @@ void Spell::TakeReagents() } for (SpellReagentsCurrencyEntry const* reagentsCurrency : m_spellInfo->ReagentsCurrency) - p_caster->ModifyCurrency(reagentsCurrency->CurrencyTypesID, -int32(reagentsCurrency->CurrencyCount), false, true); + p_caster->RemoveCurrency(reagentsCurrency->CurrencyTypesID, reagentsCurrency->CurrencyCount, CurrencyDestroyReason::Spell); } void Spell::HandleThreatSpells() diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 0283ef0de1b..acaf5bd2487 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -256,6 +256,7 @@ class TC_GAME_API Spell void EffectHeal(); void EffectBind(); void EffectTeleportToReturnPoint(); + void EffectIncreaseCurrencyCap(); void EffectHealthLeech(); void EffectQuestComplete(); void EffectCreateItem(); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 3af28b58daa..a9d38f7578c 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -100,7 +100,7 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF &Spell::EffectBind, // 11 SPELL_EFFECT_BIND &Spell::EffectNULL, // 12 SPELL_EFFECT_PORTAL &Spell::EffectTeleportToReturnPoint, // 13 SPELL_EFFECT_TELEPORT_TO_RETURN_POINT - &Spell::EffectNULL, // 14 SPELL_EFFECT_INCREASE_CURRENCY_CAP + &Spell::EffectIncreaseCurrencyCap, // 14 SPELL_EFFECT_INCREASE_CURRENCY_CAP &Spell::EffectTeleportUnitsWithVisualLoadingScreen, // 15 SPELL_EFFECT_TELEPORT_WITH_SPELL_VISUAL_KIT_LOADING_SCREEN &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL @@ -4973,7 +4973,7 @@ void Spell::EffectGiveCurrency() if (!sCurrencyTypesStore.LookupEntry(effectInfo->MiscValue)) return; - unitTarget->ToPlayer()->ModifyCurrency(effectInfo->MiscValue, damage); + unitTarget->ToPlayer()->ModifyCurrency(effectInfo->MiscValue, damage, CurrencyGainSource::Spell, CurrencyDestroyReason::Spell); } void Spell::EffectCastButtons() @@ -5081,6 +5081,18 @@ void Spell::EffectTeleportToReturnPoint() player->TeleportTo(*dest, unitTarget == m_caster ? TELE_TO_SPELL | TELE_TO_NOT_LEAVE_COMBAT : 0); } +void Spell::EffectIncreaseCurrencyCap() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + if (damage <= 0) + return; + + if (Player* player = unitTarget->ToPlayer()) + player->IncreaseCurrencyCap(effectInfo->MiscValue, damage); +} + void Spell::EffectSummonRaFFriend() { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) diff --git a/src/server/game/Spells/TraitMgr.cpp b/src/server/game/Spells/TraitMgr.cpp index 6bfe9e49b6d..4586dd865f7 100644 --- a/src/server/game/Spells/TraitMgr.cpp +++ b/src/server/game/Spells/TraitMgr.cpp @@ -389,7 +389,7 @@ void FillOwnedCurrenciesMap(WorldPackets::Traits::TraitConfig const& traitConfig break; } case TraitCurrencyType::CurrencyTypesBased: - currencies[currency->ID] += player.GetCurrency(currency->CurrencyTypesID); + currencies[currency->ID] += player.GetCurrencyQuantity(currency->CurrencyTypesID); break; case TraitCurrencyType::TraitSourced: if (std::vector<TraitCurrencySourceEntry const*>* currencySources = Trinity::Containers::MapGetValuePtr(_traitCurrencySourcesByCurrency, currency->ID)) diff --git a/src/server/game/Spells/TraitMgr.h b/src/server/game/Spells/TraitMgr.h index aceccbfe916..951d4e39e90 100644 --- a/src/server/game/Spells/TraitMgr.h +++ b/src/server/game/Spells/TraitMgr.h @@ -63,7 +63,7 @@ struct PlayerDataAccessor /*implicit*/ PlayerDataAccessor(Player const* player) : _player(player) { } uint64 GetMoney() const; - int32 GetCurrency(int32 currencyId) const; + int32 GetCurrencyQuantity(int32 currencyId) const; int32 GetLevel() const; bool IsQuestRewarded(int32 questId) const; bool HasAchieved(int32 achievementId) const; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3bc759b04f0..199173df338 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1091,34 +1091,6 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_CURRENCY_RESET_INTERVAL] = 7; } - m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS] = sConfigMgr->GetIntDefault("Currency.StartApexisCrystals", 0); - if (int32(m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS]) < 0) - { - TC_LOG_ERROR("server.loading", "Currency.StartApexisCrystals ({}) must be >= 0, set to default 0.", m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS]); - m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS] = 0; - } - m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] = sConfigMgr->GetIntDefault("Currency.MaxApexisCrystals", 20000); - if (int32(m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS]) < 0) - { - TC_LOG_ERROR("server.loading", "Currency.MaxApexisCrystals ({}) can't be negative. Set to default 20000.", m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS]); - m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] = 20000; - } - m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] *= 100; //precision mod - - m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS] = sConfigMgr->GetIntDefault("Currency.StartJusticePoints", 0); - if (int32(m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS]) < 0) - { - TC_LOG_ERROR("server.loading", "Currency.StartJusticePoints ({}) must be >= 0, set to default 0.", m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS]); - m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS] = 0; - } - m_int_configs[CONFIG_CURRENCY_MAX_JUSTICE_POINTS] = sConfigMgr->GetIntDefault("Currency.MaxJusticePoints", 4000); - if (int32(m_int_configs[CONFIG_CURRENCY_MAX_JUSTICE_POINTS]) < 0) - { - TC_LOG_ERROR("server.loading", "Currency.MaxJusticePoints ({}) can't be negative. Set to default 4000.", m_int_configs[CONFIG_CURRENCY_MAX_JUSTICE_POINTS]); - m_int_configs[CONFIG_CURRENCY_MAX_JUSTICE_POINTS] = 4000; - } - m_int_configs[CONFIG_CURRENCY_MAX_JUSTICE_POINTS] *= 100; //precision mod - m_int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL] = sConfigMgr->GetIntDefault("RecruitAFriend.MaxLevel", 85); if (m_int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL] > m_int_configs[CONFIG_MAX_PLAYER_LEVEL]) { diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index c21c559db55..ded5f48b7e5 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -995,7 +995,7 @@ public: if (!amount) return false; - target->ModifyCurrency(currencyId, amount, true, true); + target->ModifyCurrency(currencyId, amount, CurrencyGainSource::Cheat, CurrencyDestroyReason::Cheat); return true; } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index fdcdbaeb2a1..ea48a7decc3 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -4257,38 +4257,6 @@ Currency.ResetDay = 3 Currency.ResetHour = 6 # -# Currency.StartApexisCrystals -# Amount of Apexis Crystals that new players will start with -# Default: 0 (with precision) -# - -Currency.StartApexisCrystals = 0 - -# -# Currency.MaxApexisCrystals -# Amount Apexis Crystals a player can have -# Default: 20000 -# - -Currency.MaxApexisCrystals = 20000 - -# -# Currency.StartJusticePoints -# Amount of justice points that new players will start with -# Default: 0 (with precision) -# - -Currency.StartJusticePoints = 0 - -# -# Currency.MaxJusticePoints -# Amount justice points a player can have -# Default: 4000 -# - -Currency.MaxJusticePoints = 4000 - -# ################################################################################################### ################################################################################################### |