[Fixes] AA System Fixes (#3572)

* -Always load AAs beyond our current expansion (Will need this for refunding invalid AAs).
-AAs beyond our current expansion will no longer be buyable or sendable to clients.

* #reload aa will now reload character aa data.

* Base Implementation of auto grant AA

* -Add DB manifest entry
-Made has already purchased fn a bit better
-Added auto grant to db entry

* -Added grantaa command.
-Reworked grantaa to not spam the client with packets, it still does spam messages because the feedback is important.

* Port suggested changes for Finish AA purchase.

---------

Co-authored-by: KimLS <KimLS@peqtgc.com>
This commit is contained in:
Alex 2023-09-17 11:12:43 -07:00 committed by GitHub
parent e85a8db8c4
commit d3a414a048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 218 additions and 34 deletions

View File

@ -4930,6 +4930,17 @@ CREATE TABLE `character_stats_record` (
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`character_id`)
);
)"
},
ManifestEntry{
.version = 9236,
.description = "2023_08_24_aa_ability_auto_grant.sql",
.check = "SHOW COLUMNS FROM `aa_ability` LIKE 'auto_grant_enabled';",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `aa_ability` ADD COLUMN `auto_grant_enabled` TINYINT(4) NOT NULL DEFAULT '0' AFTER `reset_on_death`;
UPDATE `aa_ability` SET `auto_grant_enabled` = 1 WHERE `grant_only` = 0 AND `charges` = 0 AND `category` = -1;
)"
},

View File

@ -820,6 +820,7 @@ RULE_CATEGORY_END()
RULE_CATEGORY(Expansion)
RULE_INT(Expansion, CurrentExpansion, -1, "The current expansion enabled for the server [-1 = ALL, 0 = Classic, 1 = Kunark etc.]")
RULE_BOOL(Expansion, UseCurrentExpansionAAOnly, false, "When true will only load AA ranks that match CurrentExpansion rule")
RULE_INT(Expansion, AutoGrantAAExpansion, -1, "Expansion to auto grant AAs up to, [-1 = Disabled, 0 = Classic, 1 = Kunark etc.]")
RULE_CATEGORY_END()
RULE_CATEGORY(Instances)

View File

@ -1111,7 +1111,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) {
return;
}
FinishAlternateAdvancementPurchase(rank, false);
FinishAlternateAdvancementPurchase(rank, false, true);
}
bool Client::GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore_cost) {
@ -1134,13 +1134,13 @@ bool Client::GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore
}
ret = true;
FinishAlternateAdvancementPurchase(rank, ignore_cost);
FinishAlternateAdvancementPurchase(rank, ignore_cost, true);
}
return ret;
}
void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost) {
void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost, bool send_message_and_save) {
auto rank_id = rank->base_ability->first_rank_id;
if (rank->base_ability->charges) {
@ -1156,7 +1156,7 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
SetAA(rank_id, rank->current_value, 0);
//if not max then send next aa
if (rank->next) {
if (rank->next && send_message_and_save) {
SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value);
}
}
@ -1164,10 +1164,12 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
auto cost = !ignore_cost ? rank->cost : 0;
m_pp.aapoints -= static_cast<uint32>(cost);
SaveAA();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
if (send_message_and_save) {
SaveAA();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
}
if (player_event_logs.IsEventEnabled(PlayerEvent::AA_PURCHASE)) {
auto e = PlayerEvent::AAPurchasedEvent{
@ -1181,14 +1183,16 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
}
if (rank->prev) {
MessageString(
Chat::Yellow,
AA_IMPROVE,
std::to_string(rank->title_sid).c_str(),
std::to_string(rank->prev->current_value).c_str(),
std::to_string(cost).c_str(),
cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str()
);
if (send_message_and_save) {
MessageString(
Chat::Yellow,
AA_IMPROVE,
std::to_string(rank->title_sid).c_str(),
std::to_string(rank->prev->current_value).c_str(),
std::to_string(cost).c_str(),
cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str()
);
}
/* QS: Player_Log_AA_Purchases */
if (RuleB(QueryServ, PlayerLogAAPurchases)) {
@ -1203,13 +1207,15 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc);
}
} else {
MessageString(
Chat::Yellow,
AA_GAIN_ABILITY,
std::to_string(rank->title_sid).c_str(),
std::to_string(cost).c_str(),
cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str()
);
if (send_message_and_save) {
MessageString(
Chat::Yellow,
AA_GAIN_ABILITY,
std::to_string(rank->title_sid).c_str(),
std::to_string(cost).c_str(),
cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str()
);
}
/* QS: Player_Log_AA_Purchases */
if (RuleB(QueryServ, PlayerLogAAPurchases)) {
@ -1594,6 +1600,15 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
}
}
int expansion = RuleI(Expansion, CurrentExpansion);
bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly);
if (use_expansion_aa && expansion >= 0) {
if (rank->expansion > expansion) {
return false;
}
}
if (IsClient()) {
if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
return false;
@ -1645,6 +1660,10 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price,
return false;
}
if (IsClient() && CastToClient()->HasAlreadyPurchasedRank(rank)) {
return false;
}
//You can't purchase grant only AAs they can only be assigned
if(check_grant && ability->grant_only) {
return false;
@ -1764,7 +1783,7 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
{
abilities.clear();
std::string query = "SELECT id, name, category, classes, races, deities, drakkin_heritage, status, type, charges, "
"grant_only, reset_on_death, first_rank_id FROM aa_ability WHERE enabled = 1";
"grant_only, reset_on_death, auto_grant_enabled, first_rank_id FROM aa_ability WHERE enabled = 1";
auto results = QueryDatabase(query);
if(results.Success()) {
for(auto row = results.begin(); row != results.end(); ++row) {
@ -1782,7 +1801,8 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
ability->charges = Strings::ToInt(row[9]);
ability->grant_only = Strings::ToBool(row[10]);
ability->reset_on_death = Strings::ToBool(row[11]);
ability->first_rank_id = Strings::ToInt(row[12]);
ability->auto_grant_enabled = Strings::ToBool(row[12]);
ability->first_rank_id = Strings::ToInt(row[13]);
ability->first = nullptr;
abilities[ability->id] = std::unique_ptr<AA::Ability>(ability);
@ -1793,17 +1813,11 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
}
LogInfo("Loaded [{}] Alternate Advancement Abilities", Strings::Commify((int)abilities.size()));
int expansion = RuleI(Expansion, CurrentExpansion);
bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly);
ranks.clear();
if (use_expansion_aa && expansion >= 0) {
query = fmt::format("SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
"next_id, expansion FROM aa_ranks WHERE expansion <= {}", expansion);
} else {
query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
"next_id, expansion FROM aa_ranks";
}
results = QueryDatabase(query);
if(results.Success()) {
for(auto row = results.begin(); row != results.end(); ++row) {
@ -2068,3 +2082,126 @@ void Client::TogglePurchaseAlternativeAdvancementRank(int rank_id){
CalcBonuses();
}
void Client::AutoGrantAAPoints() {
int auto_grant_expansion = RuleI(Expansion, AutoGrantAAExpansion);
if (auto_grant_expansion == -1) {
return;
}
//iterate through every AA
for (auto& iter : zone->aa_abilities) {
auto ability = iter.second.get();
if (ability->grant_only) {
continue;
}
if (ability->charges > 0) {
continue;
}
if (!ability->auto_grant_enabled) {
continue;
}
auto level = GetLevel();
auto p = 1;
auto rank = ability->first;
while (rank != nullptr) {
if (CanUseAlternateAdvancementRank(rank)) {
if (rank->expansion <= auto_grant_expansion && rank->level_req <= level && !HasAlreadyPurchasedRank(rank)) {
FinishAlternateAdvancementPurchase(rank, true, false);
if (rank->prev) {
MessageString(
Chat::Yellow,
AA_IMPROVE,
std::to_string(rank->title_sid).c_str(),
std::to_string(rank->prev->current_value).c_str(),
"0",
std::to_string(AA_POINTS).c_str()
);
}
else {
MessageString(
Chat::Yellow,
AA_GAIN_ABILITY,
std::to_string(rank->title_sid).c_str(),
"0",
std::to_string(AA_POINTS).c_str()
);
}
}
}
else {
break;
}
p++;
rank = rank->next;
}
}
SendClearAA();
SendAlternateAdvancementTable();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
}
void Client::GrantAllAAPoints()
{
//iterate through every AA
for (auto& iter : zone->aa_abilities) {
auto ability = iter.second.get();
if (ability->charges > 0) {
continue;
}
auto level = GetLevel();
auto p = 1;
auto rank = ability->first;
while (rank != nullptr) {
if (CanUseAlternateAdvancementRank(rank)) {
if (rank->level_req <= level && !HasAlreadyPurchasedRank(rank)) {
FinishAlternateAdvancementPurchase(rank, true, false);
}
}
else {
break;
}
p++;
rank = rank->next;
}
}
SaveAA();
SendClearAA();
SendAlternateAdvancementTable();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
}
bool Client::HasAlreadyPurchasedRank(AA::Rank *rank) {
auto iter = aa_ranks.find(rank->base_ability->id);
if (iter == aa_ranks.end()) {
return false;
}
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(iter->first, iter->second.first);
auto ability = ability_rank.first;
auto current = ability_rank.second;
while (current != nullptr) {
if (current == rank) {
return true;
}
current = current->prev;
}
return false;
}

View File

@ -50,6 +50,7 @@ public:
int status;
bool grant_only;
bool reset_on_death;
bool auto_grant_enabled;
int type;
int charges;
int first_rank_id;

View File

@ -905,6 +905,9 @@ public:
int GetAAPoints() { return m_pp.aapoints; }
int GetSpentAA() { return m_pp.aapoints_spent; }
uint32 GetRequiredAAExperience();
void AutoGrantAAPoints();
void GrantAllAAPoints();
bool HasAlreadyPurchasedRank(AA::Rank* rank);
bool SendGMCommand(std::string message, bool ignore_status = false);
@ -1662,7 +1665,7 @@ protected:
bool client_data_loaded;
void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost);
void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost, bool send_message_and_save);
Mob* bind_sight_target;

View File

@ -918,6 +918,8 @@ void Client::CompleteConnect()
RecordStats();
AutoGrantAAPoints();
// enforce some rules..
if (!CanEnterZone()) {
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());

View File

@ -137,6 +137,7 @@ int command_init(void)
command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) ||
command_add("gmzone", "[Zone ID|Zone Short Name] [Version] [Instance Identifier] - Zones to a private GM instance (Version defaults to 0 and Instance Identifier defaults to 'gmzone' if not used)", AccountStatus::GMAdmin, command_gmzone) ||
command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) ||
command_add("grantaa", "Grants a player all available AA points for their level.", AccountStatus::GMMgmt, command_grantaa) ||
command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", AccountStatus::GMAreas, command_grid) ||
command_add("guild", "Guild manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_guild) ||
command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) ||
@ -826,6 +827,7 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/givemoney.cpp"
#include "gm_commands/gmzone.cpp"
#include "gm_commands/goto.cpp"
#include "gm_commands/grantaa.cpp"
#include "gm_commands/grid.cpp"
#include "gm_commands/guild.cpp"
#include "gm_commands/hp.cpp"

View File

@ -87,6 +87,7 @@ void command_giveitem(Client *c, const Seperator *sep);
void command_givemoney(Client *c, const Seperator *sep);
void command_gmzone(Client *c, const Seperator *sep);
void command_goto(Client *c, const Seperator *sep);
void command_grantaa(Client* c, const Seperator* sep);
void command_grid(Client *c, const Seperator *sep);
void command_guild(Client *c, const Seperator *sep);
void command_help(Client *c, const Seperator *sep);

View File

@ -5652,6 +5652,10 @@ void EntityList::SendAlternateAdvancementStats() {
for (auto &c : client_list) {
c.second->Message(Chat::White, "Reloading AA");
c.second->ReloadExpansionProfileSetting();
if (!database.LoadAlternateAdvancement(c.second)) {
c.second->Message(Chat::Red, "Error loading alternate advancement character data");
}
c.second->SendClearPlayerAA();
c.second->SendAlternateAdvancementTable();
c.second->SendAlternateAdvancementStats();

View File

@ -938,6 +938,8 @@ void Client::SetLevel(uint8 set_level, bool command)
m_pp.exp = GetEXPForLevel(set_level);
Message(Chat::Yellow, fmt::format("Welcome to level {}!", set_level).c_str());
lu->exp = 0;
AutoGrantAAPoints();
} else {
const auto temporary_xp = (
static_cast<float>(m_pp.exp - GetEXPForLevel(GetLevel())) /

View File

@ -0,0 +1,20 @@
#include "../client.h"
void command_grantaa(Client *c, const Seperator *sep)
{
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
c->Message(Chat::White, "You must target a player to use this command.");
return;
}
auto t = c->GetTarget()->CastToClient();
t->GrantAllAAPoints();
c->Message(
Chat::White,
fmt::format(
"Successfully granted all Alternate Advancements for {}.",
c->GetTargetDescription(t)
).c_str()
);
}