diff --git a/common/repositories/base/base_character_alternate_abilities_repository.h b/common/repositories/base/base_character_alternate_abilities_repository.h index c7009e487..4fcf935b9 100644 --- a/common/repositories/base/base_character_alternate_abilities_repository.h +++ b/common/repositories/base/base_character_alternate_abilities_repository.h @@ -6,7 +6,7 @@ * Any modifications to base repositories are to be made by the generator only * * @generator ./utils/scripts/generators/repository-generator.pl - * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + * @docs https://docs.eqemu.io/developer/repositories */ #ifndef EQEMU_BASE_CHARACTER_ALTERNATE_ABILITIES_REPOSITORY_H @@ -16,6 +16,7 @@ #include "../../strings.h" #include + class BaseCharacterAlternateAbilitiesRepository { public: struct CharacterAlternateAbilities { @@ -116,8 +117,9 @@ public: { auto results = db.QueryDatabase( fmt::format( - "{} WHERE id = {} LIMIT 1", + "{} WHERE {} = {} LIMIT 1", BaseSelect(), + PrimaryKey(), character_alternate_abilities_id ) ); @@ -348,6 +350,68 @@ public: return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); } + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const CharacterAlternateAbilities &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.aa_id)); + v.push_back(std::to_string(e.aa_value)); + v.push_back(std::to_string(e.charges)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.aa_id)); + v.push_back(std::to_string(e.aa_value)); + v.push_back(std::to_string(e.charges)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } }; #endif //EQEMU_BASE_CHARACTER_ALTERNATE_ABILITIES_REPOSITORY_H diff --git a/zone/aa.cpp b/zone/aa.cpp index 20b6a4091..ce00fda09 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -39,6 +39,8 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) #include "bot.h" +#include "../common/repositories/character_alternate_abilities_repository.h" + extern WorldServer worldserver; extern QueryServ* QServ; @@ -510,35 +512,38 @@ void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *tar, uint32 d delete made_npc; } -void Client::ResetAA() { +void Client::ResetAA() +{ SendClearAA(); RefundAA(); memset(&m_pp.aa_array[0], 0, sizeof(AA_Array) * MAX_PP_AA_ARRAY); - int i = 0; - for(auto &rank_value : aa_ranks) { - auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value.first, rank_value.second.first); - auto ability = ability_rank.first; - auto rank = ability_rank.second; + int slot_id = 0; - if(!rank) { + for (auto& rank_value: aa_ranks) { + auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value.first, rank_value.second.first); + auto ability = ability_rank.first; + auto rank = ability_rank.second; + + if (!rank) { continue; } - m_pp.aa_array[i].AA = rank_value.first; - m_pp.aa_array[i].value = rank_value.second.first; - m_pp.aa_array[i].charges = rank_value.second.second; - ++i; + m_pp.aa_array[slot_id].AA = rank_value.first; + m_pp.aa_array[slot_id].value = rank_value.second.first; + m_pp.aa_array[slot_id].charges = rank_value.second.second; + ++slot_id; } - for(int i = 0; i < _maxLeaderAA; ++i) - m_pp.leader_abilities.ranks[i] = 0; + for (int slot_id = 0; slot_id < _maxLeaderAA; ++slot_id) { + m_pp.leader_abilities.ranks[slot_id] = 0; + } m_pp.group_leadership_points = 0; - m_pp.raid_leadership_points = 0; - m_pp.group_leadership_exp = 0; - m_pp.raid_leadership_exp = 0; + m_pp.raid_leadership_points = 0; + m_pp.group_leadership_exp = 0; + m_pp.raid_leadership_exp = 0; database.DeleteCharacterAAs(CharacterID()); database.DeleteCharacterLeadershipAbilities(CharacterID()); @@ -833,31 +838,31 @@ void Client::RefundAA() { int refunded = 0; auto rank_value = aa_ranks.begin(); - while(rank_value != aa_ranks.end()) { + while (rank_value != aa_ranks.end()) { auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value->first, rank_value->second.first); - auto ability = ability_rank.first; - auto rank = ability_rank.second; + auto ability = ability_rank.first; + auto rank = ability_rank.second; - if(!ability) { + if (!ability) { ++rank_value; continue; } - if(ability->charges > 0 && rank_value->second.second < 1) { + if (ability->charges > 0 && rank_value->second.second < 1) { ++rank_value; continue; } - if(ability->grant_only) { + if (ability->grant_only) { ++rank_value; continue; } refunded += rank->total_cost; - rank_value = aa_ranks.erase(rank_value); + rank_value = aa_ranks.erase(rank_value); } - if(refunded > 0) { + if (refunded > 0) { m_pp.aapoints += refunded; SaveAA(); Save(); @@ -1438,40 +1443,42 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { bool ZoneDatabase::LoadAlternateAdvancement(Client *c) { c->ClearAAs(); - std::string query = StringFormat( - "SELECT " - "aa_id, " - "aa_value, " - "charges " - "FROM " - "`character_alternate_abilities` " - "WHERE `id` = %u", c->CharacterID()); - MySQLRequestResult results = database.QueryDatabase(query); - int i = 0; - for(auto row = results.begin(); row != results.end(); ++row) { - uint32 aa = Strings::ToUnsignedInt(row[0]); - uint32 value = Strings::ToUnsignedInt(row[1]); - uint32 charges = Strings::ToUnsignedInt(row[2]); + const auto& l = CharacterAlternateAbilitiesRepository::GetWhere( + database, + fmt::format( + "`id` = {}", + c->CharacterID() + ) + ); - auto rank = zone->GetAlternateAdvancementRank(aa); - if(!rank) { + uint32 slot_id = 0; + + for (const auto& e : l) { + const uint16 aa_id = e.aa_id; + const uint16 aa_value = e.aa_value; + const uint16 charges = e.charges; + + auto rank = zone->GetAlternateAdvancementRank(aa_id); + if (!rank) { continue; } auto ability = rank->base_ability; - if(!ability) { + if (!ability) { continue; } - rank = ability->GetRankByPointsSpent(value); + rank = ability->GetRankByPointsSpent(aa_value); - if(c->CanUseAlternateAdvancementRank(rank)) { - c->GetPP().aa_array[i].AA = aa; - c->GetPP().aa_array[i].value = value; - c->GetPP().aa_array[i].charges = charges; - c->SetAA(aa, value, charges); - i++; + if (c->CanUseAlternateAdvancementRank(rank)) { + c->GetPP().aa_array[slot_id].AA = aa_id; + c->GetPP().aa_array[slot_id].value = aa_value; + c->GetPP().aa_array[slot_id].charges = charges; + + c->SetAA(aa_id, aa_value, charges); + + slot_id++; } } @@ -1556,56 +1563,61 @@ uint32 Mob::GetAAByAAID(uint32 aa_id, uint32 *charges) const { return 0; } -bool Mob::SetAA(uint32 rank_id, uint32 new_value, uint32 charges) { - if(zone) { - AA::Ability *ability = zone->GetAlternateAdvancementAbilityByRank(rank_id); +bool Mob::SetAA(uint32 rank_id, uint32 new_value, uint32 charges) +{ + if (zone) { + auto a = zone->GetAlternateAdvancementAbilityByRank(rank_id); - if(!ability) { + if (!a) { return false; } - if(new_value > ability->GetMaxLevel(this)) { + if(new_value > a->GetMaxLevel(this)) { return false; } - aa_ranks[ability->id] = std::make_pair(new_value, charges); + aa_ranks[a->id] = std::make_pair(new_value, charges); } return true; } -bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) { +bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) +{ if (!rank) { return false; } - AA::Ability *ability = rank->base_ability; + const auto a = rank->base_ability; - if(!ability) - return false; - - if(!(ability->classes & (1 << GetClass()))) { + if (!a) { return false; } - // Passive and Active Shroud AAs - // For now we skip them - if(ability->category == 3 || ability->category == 4) { + if (!(a->classes & (1 << GetClass()))) { + return false; + } + + // Passive and Active Shroud AAs, skip for now + if ( + a->category == AACategory::ShroudPassive || + a->category == AACategory::ShroudActive + ) { return false; } //the one titanium hack i will allow //just to make sure we dont crash the client with newer aas //we'll exclude any expendable ones - if(IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskTitaniumAndEarlier) { - if(ability->charges > 0) { + if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskTitaniumAndEarlier) { + if (a->charges > 0) { return false; } } - int expansion = RuleI(Expansion, CurrentExpansion); - bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly); + const int expansion = RuleI(Expansion, CurrentExpansion); + const bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly); if (use_expansion_aa && expansion >= 0) { if (rank->expansion > expansion) { return false; @@ -1617,36 +1629,35 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) { if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) { return false; } - } - else if (IsBot()) { + } else if (IsBot()) { if (rank->expansion && !(CastToBot()->GetExpansionBitmask() & (1 << (rank->expansion - 1)))) { return false; } - } - else { + } else { if (rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) { return false; } } auto race = GetPlayerRaceValue(GetBaseRace()); - race = race > 16 ? 1 : race; - if(!(ability->races & (1 << (race - 1)))) { + + race = race > PLAYER_RACE_COUNT ? Race::Human : race; + + if (!(a->races & (1 << (race - 1)))) { return false; } - auto deity = GetDeityBit(); - if(!(ability->deities & deity)) { + const auto deity = GetDeityBit(); + if (!(a->deities & deity)) { return false; } - if(IsClient() && CastToClient()->Admin() < ability->status) { + if (IsClient() && CastToClient()->Admin() < a->status) { return false; } - if(GetBaseRace() == 522) { - //drakkin_heritage - if(!(ability->drakkin_heritage & (1 << GetDrakkinHeritage()))) { + if (GetBaseRace() == Race::Drakkin) { + if (!(a->drakkin_heritage & (1 << GetDrakkinHeritage()))) { return false; } } @@ -1654,13 +1665,15 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) { return true; } -bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant) { - AA::Ability *ability = rank->base_ability; +bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant) +{ + auto a = rank->base_ability; - if(!ability) + if (!a) { return false; + } - if(!CanUseAlternateAdvancementRank(rank)) { + if (!CanUseAlternateAdvancementRank(rank)) { return false; } @@ -1668,54 +1681,53 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, return false; } - //You can't purchase grant only AAs they can only be assigned - if(check_grant && ability->grant_only) { + // You cannot purchase grant only AAs they can only be assigned + if (check_grant && a->grant_only) { return false; } - //check level req - if(rank->level_req > GetLevel()) { + if (rank->level_req > GetLevel()) { return false; } - uint32 current_charges = 0; - auto points = GetAA(rank->id, ¤t_charges); + uint32 current_charges = 0; + const uint32 points = GetAA(rank->id, ¤t_charges); //check that we are on previous rank already (if exists) //grant ignores the req to own the previous rank. - if(check_grant && rank->prev) { - if(points != rank->prev->current_value) { + if (check_grant && rank->prev) { + if (points != rank->prev->current_value) { return false; } } //check that we aren't already on this rank or one ahead of us - if(points >= rank->current_value) { + if (points >= rank->current_value) { return false; } //if expendable only let us purchase if we have no charges already //not quite sure on how this functions client side atm //I intend to look into it later to make sure the behavior is right - if(ability->charges > 0 && current_charges > 0) { + if (a->charges > 0 && current_charges > 0) { return false; } //check prereqs - for(auto &prereq : rank->prereqs) { + for (auto &prereq: rank->prereqs) { AA::Ability *prereq_ability = zone->GetAlternateAdvancementAbility(prereq.first); - if(prereq_ability) { + if (prereq_ability) { auto ranks = GetAA(prereq_ability->first_rank_id); - if(ranks < prereq.second) { + if (ranks < prereq.second) { return false; } } } //check price, if client - if(check_price && IsClient()) { - if(rank->cost > CastToClient()->GetAAPoints()) { + if (check_price && IsClient()) { + if (rank->cost > CastToClient()->GetAAPoints()) { return false; } } diff --git a/zone/aa.h b/zone/aa.h index 4d9047e39..720fabfd8 100644 --- a/zone/aa.h +++ b/zone/aa.h @@ -1525,6 +1525,19 @@ enum { //values of AA_Action.action aaActionBuy = 3 }; +namespace AACategory { + constexpr int None = -1; + constexpr int Passive = 1; + constexpr int Progression = 2; + constexpr int ShroudPassive = 3; + constexpr int ShroudActive = 4; + constexpr int VeteranReward = 5; + constexpr int Tradeskill = 6; + constexpr int Expendable = 7; + constexpr int RacialInnate = 8; + constexpr int EverQuest = 9; +} + class Timer; class Mob; class SwarmPet { diff --git a/zone/client.cpp b/zone/client.cpp index be4971738..ea41f637d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -58,6 +58,7 @@ extern volatile bool RunLoops; #include "mob_movement_manager.h" #include "cheat_manager.h" +#include "../common/repositories/character_alternate_abilities_repository.h" #include "../common/repositories/account_flags_repository.h" #include "../common/repositories/bug_reports_repository.h" #include "../common/repositories/char_recipe_list_repository.h" @@ -586,45 +587,52 @@ void Client::ReportConnectingState() { }; } -bool Client::SaveAA() { - std::string iquery; - int spentpoints = 0; - int i = 0; - for(auto &rank : aa_ranks) { - AA::Ability *ability = zone->GetAlternateAdvancementAbility(rank.first); - if(!ability) +bool Client::SaveAA() +{ + std::vector v; + + uint32 aa_points_spent = 0; + + auto e = CharacterAlternateAbilitiesRepository::NewEntity(); + + for (auto &rank : aa_ranks) { + auto a = zone->GetAlternateAdvancementAbility(rank.first); + if (!a) { continue; + } - if(rank.second.first > 0) { - AA::Rank *r = ability->GetRankByPointsSpent(rank.second.first); - - if(!r) + if (rank.second.first > 0) { + auto r = a->GetRankByPointsSpent(rank.second.first); + if (!r) { continue; - - spentpoints += r->total_cost; - - if(i == 0) { - iquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)" - " VALUES (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second); - } else { - iquery += StringFormat(", (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second); } - i++; + + aa_points_spent += r->total_cost; + + e.id = character_id; + e.aa_id = a->first_rank_id; + e.aa_value = rank.second.first; + e.charges = rank.second.second; + + v.emplace_back(e); } } - m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa; - if(iquery.length() > 0) { - database.QueryDatabase(iquery); - } - - return true; + return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v); } void Client::RemoveExpendedAA(int aa_id) { - database.QueryDatabase(StringFormat("DELETE from `character_alternate_abilities` WHERE `id` = %d and `aa_id` = %d", character_id, aa_id)); + CharacterAlternateAbilitiesRepository::DeleteWhere( + database, + fmt::format( + "`id` = {} AND `aa_id` = {}", + CharacterID(), + aa_id + ) + ); } bool Client::Save(uint8 iCommitNow) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 2070803b8..17eb306b4 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -32,6 +32,7 @@ #include "../common/repositories/character_potionbelt_repository.h" #include "../common/repositories/character_bandolier_repository.h" #include "../common/repositories/character_currency_repository.h" +#include "../common/repositories/character_alternate_abilities_repository.h" #include #include @@ -1272,15 +1273,6 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru ); } -bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges){ - std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)" - " VALUES (%u, %u, %u, %u)", - character_id, aa_id, current_level, charges); - auto results = QueryDatabase(rquery); - LogDebug("Saving AA for character ID: [{}], aa_id: [{}] current_level: [{}]", character_id, aa_id, current_level); - return true; -} - bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ if (!IsValidSpell(spell_id)) { return false; @@ -1347,10 +1339,15 @@ bool ZoneDatabase::DeleteCharacterLeadershipAbilities(uint32 character_id) return CharacterLeadershipAbilitiesRepository::DeleteOne(*this, character_id); } -bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `aa_id` NOT IN(SELECT a.first_rank_id FROM aa_ability a WHERE a.grant_only != 0)", character_id); - QueryDatabase(query); - return true; +bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id) +{ + return CharacterAlternateAbilitiesRepository::DeleteWhere( + *this, + fmt::format( + "`id` = {} AND `aa_id` NOT IN (SELECT a.first_rank_id FROM aa_ability a WHERE a.grant_only != 0)", + character_id + ) + ); } bool ZoneDatabase::DeleteCharacterMaterialColor(uint32 character_id) diff --git a/zone/zonedb.h b/zone/zonedb.h index 17b326132..849e703c9 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -444,7 +444,6 @@ public: bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); - bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges); bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(Client* c, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);