added many methods related to increasing a players rank

This commit is contained in:
2024-12-12 23:42:16 -05:00
parent 81b7a87ced
commit 738194fbfa
2 changed files with 344 additions and 8 deletions

View File

@@ -3,8 +3,11 @@
#include "WorldDatabase.h"
#include "Player.h"
#include "MpLogger.h"
#include "MythicPlus.h"
#include "MpConstants.h"
#include <string_view>
#include <tuple>
/**
* Table schema for upgrade ranks populated by go script:
@@ -85,7 +88,7 @@ int32 AdvancementMgr::LoadAdvencementRanks() {
uint32 chanceCost2 = fields[15].Get<uint32>();
uint32 chanceCost3 = fields[16].Get<uint32>();
// Should add validator... but let's do it without and trust the o-DB-wan-kenobee
// Should add validator... but let's do it without and trust in the o-DB-Wan-kenobe
MpAdvancements advancement = static_cast<MpAdvancements>(advancementId);
// List of all ranks keyed by rank, advancementId
@@ -93,7 +96,7 @@ int32 AdvancementMgr::LoadAdvencementRanks() {
.rank = upgradeRank,
.advancementId = advancement,
.materialCost = std::unordered_map<uint32, uint32>(),
.rollCost = std::make_tuple(chanceCost1, chanceCost2, chanceCost3),
.rollCost = {chanceCost1, chanceCost2, chanceCost3},
.lowRange = std::make_pair(minIncrease1, maxIncrease1),
.midRange = std::make_pair(minIncrease2, maxIncrease2),
.highRange = std::make_pair(minIncrease3, maxIncrease3)
@@ -132,7 +135,7 @@ int32 AdvancementMgr::LoadPlayerAdvancements(Player* player) {
upgradeRank,
diceSpent
FROM mp_player_advancements
WHERE guid = ?
WHERE guid = {}
)";
QueryResult result = CharacterDatabase.Query(query, player->GetGUID().GetCounter());
@@ -151,12 +154,277 @@ int32 AdvancementMgr::LoadPlayerAdvancements(Player* player) {
uint32 diceSpent = fields[4].Get<uint32>();
MpAdvancements advancement = static_cast<MpAdvancements>(advancementId);
MpPlayerRank playerRank = MpPlayerRank();
playerRank.rank = upgradeRank;
playerRank.advancementId = advancement;
playerRank.diceSpent = diceSpent;
playerRank.bonus = bonus;
// List of all ranks keyed by rank, advancementId
_playerAdvancements[guid][advancement] = upgradeRank;
_playerAdvancements[guid][advancement] = playerRank;
} while (result->NextRow());
return result->GetRowCount();
}
/**
* Load Material Types from the database into memory
*/
int32 AdvancementMgr::LoadPlayerAdvancements(Player* player) {
constexpr std::string_view query = R"(
SELECT
guid,
advancementId,
bonus,
upgradeRank,
diceSpent
FROM mp_player_advancements
WHERE guid = {}
)";
QueryResult result = CharacterDatabase.Query(query, player->GetGUID().GetCounter());
// If the player does not have any upgrades just return not a problem until they purchase one.
if(!result) {
return 0;
}
do {
Field* fields = result->Fetch();
uint32 guid = fields[0].Get<uint32>();
uint32 advancementId = fields[1].Get<uint32>();
float bonus = fields[2].Get<float>();
uint32 upgradeRank = fields[3].Get<uint32>();
uint32 diceSpent = fields[4].Get<uint32>();
MpAdvancements advancement = static_cast<MpAdvancements>(advancementId);
MpPlayerRank playerRank = MpPlayerRank();
playerRank.rank = upgradeRank;
playerRank.advancementId = advancement;
playerRank.diceSpent = diceSpent;
playerRank.bonus = bonus;
// List of all ranks keyed by rank, advancementId
_playerAdvancements[guid][advancement] = playerRank;
} while (result->NextRow());
return result->GetRowCount();
}
MpAdvancementRank* AdvancementMgr::GetAdvancementRank(uint32 rank, MpAdvancements advancement)
{
auto key = std::make_pair(rank, advancement);
if (_advancementRanks.contains(key))
{
return &_advancementRanks.at(key);
}
else
{
MpLogger::error("Advancment Id {} and rank {} could not be found", rank, advancement);
return nullptr;
}
}
MpPlayerRank* AdvancementMgr::GetPlayerAdvancementRank(Player* player, MpAdvancements advancement)
{
if(!player) {
MpLogger::error("Could not retrieve player advancement for null player {}", player->GetName());
return nullptr;
}
if (_playerAdvancements.contains(player->GetGUID().GetCounter()) && _playerAdvancements[player->GetGUID().GetCounter()].contains(advancement))
{
return &_playerAdvancements[player->GetGUID().GetCounter()][advancement];
}
return nullptr;
}
bool AdvancementMgr::UpgradeAdvancement(Player* player, MpAdvancements advancement, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
// Validators to make sure inputs are correct to perform the upgrade
if(!player) {
MpLogger::error("Could not upgrade advancement for player, player was nullpointer");
return false;
}
if(diceCostLevel < 1 || diceCostLevel > 3) {
MpLogger::error("Invalid dice cost level valid vales (1,2,3) received {} for player {}", diceCostLevel, player->GetName());
return false;
}
if(itemEntry1 == 0) {
MpLogger::error("Material1 can not be 0 can not perform advancement upgrade for player {} Advancment {}", player->GetName(), advancement);
return false;
}
MpPlayerRank* playerRank = GetPlayerAdvancementRank(player, advancement);
// IF there is not create the base struct and add to the player map for this advancement
if(!playerRank) {
MpPlayerRank newPlayerRank;
newPlayerRank.advancementId = advancement;
auto& playerAdvMap = _playerAdvancements[player->GetGUID().GetCounter()];
playerAdvMap.emplace(advancement, newPlayerRank);
playerRank = &playerAdvMap.at(advancement);
}
if(playerRank->rank == MP_MAX_ADVANCEMENT_RANK) {
MpLogger::error("Player {} has reached the maximum rank for advancement {}", player->GetName(), advancement);
return false;
}
uint32 newRank = playerRank->rank + 1;
MpAdvancementRank* advancementRank = GetAdvancementRank(newRank, advancement);
if(!advancementRank->IsValid()) {
MpLogger::error("Advancement {} rank {} could not be found", advancement, newRank);
return false;
}
// If the player has the items needed to upgrade this advancement, then remove the items from the player inventory and apply the upgrade
if(!_PlayerHasItems(player, advancementRank, diceCostLevel, itemEntry1, itemEntry2, itemEntry3)) {
MpLogger::info("Player {} does not have the required items to upgrade advancement {}", player->GetName(), advancement);
return false;
}
// Charge the player the cost of the upgrade
_ChargeItemCost(player, advancementRank, diceCostLevel, itemEntry1, itemEntry2, itemEntry3);
// Finally get the bonus to apply for the player
float roll = _RollAdvancement(advancementRank, diceCostLevel);
// Update the player advancement rank in memory and database
playerRank->rank = newRank;
playerRank->diceSpent += advancementRank->rollCost[diceCostLevel];
playerRank->bonus += roll;
return true;
}
bool AdvancementMgr::ResetPlayerAdvancements(Player* player)
{
return true;
}
/******************
*
* Private Methods
*
******************/
void AdvancementMgr::_ResetPlayerAdvancement(Player* player, MpAdvancements advancement)
{
}
// Roll them stats DnD style.
float AdvancementMgr::_RollAdvancement(MpAdvancementRank* advancementRank, uint32 diceCostLevel)
{
uint32 min, max;
switch (diceCostLevel)
{
case 1:
min = advancementRank->lowRange.first;
max = advancementRank->lowRange.second;
break;
case 2:
min = advancementRank->midRange.first;
max = advancementRank->midRange.second;
break;
case 3:
min = advancementRank->highRange.first;
max = advancementRank->highRange.second;
break;
default:
MpLogger::error("Invalid dice cost level valid vales (1,2,3) received {} for rank roll", diceCostLevel, advancementRank->rank);
break;
}
return frand(min, max);
}
// Checks the players inventory to validate they have the required items to perform an upgrade based on the set cost for the passed in level.
bool AdvancementMgr::_PlayerHasItems(Player* player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
// Check if player has the required dice to upgrade the advancement if not do nothing.
uint32 diceCost = advancementRank->materialCost.at(diceCostLevel);
if(!player->HasItemCount(MpConstants::ANCIENT_DICE, diceCost)) {
MpLogger::info("Player {} does not have enough dice to upgrade advancement {}", player->GetName(), advancementRank->advancementId);
return false;
}
// Validate the passed in item for materialId 1 is valid and the player has enough to purchase.
if(itemEntry1 > 0) {
if(!advancementRank->HasMaterial(itemEntry1)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry1, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry1, advancementRank->materialCost[itemEntry1])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry1, advancementRank->advancementId);
return false;
}
}
if(itemEntry2 > 0) {
if(!advancementRank->HasMaterial(itemEntry2)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry2, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry2, advancementRank->materialCost[itemEntry2])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry2, advancementRank->advancementId);
return false;
}
}
if(itemEntry3 > 0) {
if(!advancementRank->HasMaterial(itemEntry3)) {
MpLogger::error("Material1 {} is not a valid material for advancement {}", itemEntry3, advancementRank->advancementId);
return false;
}
if(!player->HasItemCount(itemEntry3, advancementRank->materialCost[itemEntry3])) {
MpLogger::info("Player {} does not have enough material {} to upgrade advancement {}", player->GetName(), itemEntry3, advancementRank->advancementId);
return false;
}
}
return true;
}
// Remove all items required for the upgrade.
void AdvancementMgr::_ChargeItemCost(Player *player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3)
{
uint32 diceCost = advancementRank->materialCost[diceCostLevel];
Item* item = player->GetItemByEntry(MpConstants::ANCIENT_DICE);
item->SetCount(item->GetCount() - diceCost);
item->SendUpdateToPlayer(player);
// Remove the material from the player inventory
if(itemEntry1 > 0) {
item = player->GetItemByEntry(itemEntry1);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry1]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
if(itemEntry2 > 0) {
item = player->GetItemByEntry(itemEntry2);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry2]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
if(itemEntry3 > 0) {
item = player->GetItemByEntry(itemEntry3);
item->SetCount(item->GetCount() - advancementRank->materialCost[itemEntry3]);
item->SendUpdateToPlayer(player); // Update the client with the new dice count
}
return;
}

View File

@@ -18,7 +18,8 @@ enum MpAdvancements {
MP_ADV_RESIST_NATURE = 6,
MP_ADV_RESIST_FROST = 7,
MP_ADV_RESIST_SHADOW = 8,
MP_ADV_RESIST_ARCANE = 9
MP_ADV_RESIST_ARCANE = 9,
MP_ADV_MAX = 10
};
/**
@@ -31,14 +32,43 @@ struct MpAdvancementRank
MpAdvancements advancementId;
std::unordered_map<uint32 /*item_entry*/,uint32 /*quantity*/> materialCost;
std::tuple<uint32 /*low-cost*/, uint32 /*mid-cost*/, uint32 /*high-cost*/> rollCost;
std::array<int, 3> rollCost; // 0 = low, 1 = mid, 2 = high
// Range of status based on bet dice roll.
std::pair<uint32 /*min*/, uint32 /*max*/> lowRange;
std::pair<uint32 /*min*/, uint32 /*max*/> midRange;
std::pair<uint32 /*min*/, uint32 /*max*/> highRange;
// Used to validate this struct is set correctly
bool IsValid() {
return (rank > 0 && advancementId >= 0 && advancementId < MP_ADV_MAX);
}
// Check if the map has an the item entry for the passed in material
bool HasMaterial(uint32 itemEntry) {
return materialCost.contains(itemEntry);
}
};
// Struct is used for tracking player advancement bonuses for improving stats
struct MpPlayerRank
{
uint32 rank;
MpAdvancements advancementId;
uint32 diceSpent;
float bonus;
MpPlayerRank() : rank(0), diceSpent(0), bonus(0.0f) {}
};
/**
* This singleton class is responsible for managing the advancement system
* used to improve player stats enough to challenge harder difficulties on existing dungeons.
*
* Advancements are purchased by players using based on the mp_material_type table with a "bet"
* on dice roll. This enables players to increase their stats in a random way that is not
* guaranteed to be successful. (Similar to how DND stats rolls work on character creation. )
*/
class AdvancementMgr
{
@@ -46,7 +76,10 @@ class AdvancementMgr
std::map<std::pair<uint32 /*rank*/, MpAdvancements>, MpAdvancementRank> _advancementRanks;
// Map of player advancements [player_guid][advancement_id] = rank
std::unordered_map<uint32 /*player_guid*/, std::unordered_map<MpAdvancements, uint32 /*rank*/>> _playerAdvancements;
std::unordered_map<uint32 /*player_guid*/, std::unordered_map<MpAdvancements, MpPlayerRank>> _playerAdvancements;
// Map of different material types used fo advancing stats for players
std::unordered_map<uint32 /*material_id*/, std::vector<uint32> /* item entries */> _materialTypes;
public:
static AdvancementMgr* instance() {
@@ -54,15 +87,50 @@ public:
return &instance;
}
// Loads advancement information from the database into memory when players are logged in or server starts.
int32 LoadAdvencementRanks();
int32 LoadMaterialTypes();
int32 LoadPlayerAdvancements(Player* player);
// Methods for looking up advancment rank data
MpAdvancementRank* GetAdvancementRank(uint32 rank, MpAdvancements advancement);
// Methods for updating and setting data related to current player advancements
MpPlayerRank* AdvancementMgr::GetPlayerAdvancementRank(Player* player, MpAdvancements advancement);
/**
* This upgrades a player Advancement on the server side, which will handle the following actions:
* 1. Validating player has enough dice and materials to upgrade the advancement
* 2. Rolling the dice to see what bonus is rewarded
* 3. Removing the dice and materials from the player inventory
* 4. Updating the player advancement rank in memory and database
*
* Since different materials can be used for each advancement, at the moment only support one material type from the list. supporting
* mixed materials is more complicated and the UI to support it is much more complex, while this is not as nice it is much simpler to implement.
* That means all materials have to be selected and passed in at the time of making this call.
*/
bool UpgradeAdvancement(Player* player, MpAdvancements advancement, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
// Used to reset all advancements for a specific player
bool ResetPlayerAdvancements(Player* player);
private:
AdvancementMgr() {}
~AdvancementMgr() {}
};
// Will reset all the player advancements and refund the spent dice and material with a penalty for the reset.
void _ResetPlayerAdvancement(Player* player, MpAdvancements advancement);
// Rolls the dice to see how much a bonus is given based on the dice spend level
float _RollAdvancement(MpAdvancementRank* advancementRank, uint32 diceCostLevel);
// Determines if a player has required items to upgrade
bool _PlayerHasItems(Player* player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
// Removes items from player inventory based on the required advancement rank.
void _ChargeItemCost(Player *player, MpAdvancementRank* advancementRank, uint32 diceCostLevel, uint32 itemEntry1, uint32 itemEntry2, uint32 itemEntry3);
};
#define sAdvancementMgr AdvancementMgr::instance()
#endif