diff --git a/zone/aa.cpp b/zone/aa.cpp index 5b4a8a6ee..a9b573612 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -29,6 +29,7 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) #include "groups.h" #include "mob.h" #include "queryserv.h" +#include "quest_parser_collection.h" #include "raids.h" #include "string_ids.h" #include "titles.h" @@ -1147,65 +1148,95 @@ bool Client::GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore } void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost) { - int rank_id = rank->base_ability->first_rank_id; + auto rank_id = rank->base_ability->first_rank_id; - if(rank->base_ability->charges > 0) { + if (rank->base_ability->charges) { uint32 charges = 0; GetAA(rank_id, &charges); - if(charges > 0) { + if (charges) { return; } SetAA(rank_id, rank->current_value, rank->base_ability->charges); - } - else { + } else { SetAA(rank_id, rank->current_value, 0); //if not max then send next aa - if(rank->next) { + if (rank->next) { SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value); } } - int cost = !ignore_cost ? rank->cost : 0; + auto cost = !ignore_cost ? rank->cost : 0; - m_pp.aapoints -= cost ; + m_pp.aapoints -= static_cast(cost); SaveAA(); SendAlternateAdvancementPoints(); SendAlternateAdvancementStats(); - 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 (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() + ); /* QS: Player_Log_AA_Purchases */ - if(RuleB(QueryServ, PlayerLogAAPurchases)) { - std::string event_desc = StringFormat("Ranked AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, cost, GetZoneID(), GetInstanceID()); + if (RuleB(QueryServ, PlayerLogAAPurchases)) { + const auto event_desc = fmt::format( + "Ranked AA Purchase :: aa_id:{} at cost:{} in zoneid:{} instid:{}", + rank->id, + cost, + GetZoneID(), + GetInstanceID() + ); + 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()); + } 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() + ); + /* QS: Player_Log_AA_Purchases */ - if(RuleB(QueryServ, PlayerLogAAPurchases)) { - std::string event_desc = StringFormat("Initial AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, cost, GetZoneID(), GetInstanceID()); + if (RuleB(QueryServ, PlayerLogAAPurchases)) { + const auto event_desc = fmt::format( + "Initial AA Purchase :: aa_id:{} at cost:{} in zoneid:{} instid:{}", + rank->id, + cost, + GetZoneID(), + GetInstanceID() + ); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); } } + const auto export_string = fmt::format( + "{} {} {} {}", + cost, + rank->id, + rank->prev_id, + rank->next_id + ); + + parse->EventPlayer(EVENT_AA_BUY, this, export_string, 0); + CalcBonuses(); - if(cost > 0) { - if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass())) + if (cost) { + if (title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass())) { NotifyNewTitlesAvailable(); + } } } diff --git a/zone/command.cpp b/zone/command.cpp index 7401d9ec2..d6cbc41cc 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -142,6 +142,7 @@ int command_init(void) command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || command_add("factionassociation", "[factionid] [amount] - triggers a faction hits via association", AccountStatus::GMLeadAdmin, command_faction_association) || command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || + command_add("findaa", "[Search Criteria] - Search for an AA", AccountStatus::Guide, command_findaa) || command_add("findaliases", "[Search Criteria]- Searches for available command aliases, by alias or command", AccountStatus::Player, command_findaliases) || command_add("findclass", "[Search Criteria] - Search for a class", AccountStatus::Guide, command_findclass) || command_add("findfaction", "[Search Criteria] - Search for a faction", AccountStatus::Guide, command_findfaction) || @@ -980,6 +981,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/equipitem.cpp" #include "gm_commands/faction.cpp" #include "gm_commands/feature.cpp" +#include "gm_commands/findaa.cpp" #include "gm_commands/findclass.cpp" #include "gm_commands/findfaction.cpp" #include "gm_commands/findnpctype.cpp" diff --git a/zone/command.h b/zone/command.h index bcc8ab75f..b3ec16fce 100644 --- a/zone/command.h +++ b/zone/command.h @@ -82,6 +82,7 @@ void command_equipitem(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); void command_faction_association(Client *c, const Seperator *sep); void command_feature(Client *c, const Seperator *sep); +void command_findaa(Client *c, const Seperator *sep); void command_findaliases(Client *c, const Seperator *sep); void command_findclass(Client *c, const Seperator *sep); void command_findfaction(Client *c, const Seperator *sep); diff --git a/zone/embparser.cpp b/zone/embparser.cpp index c866e47ca..58e31e57d 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -161,6 +161,8 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_MERCHANT_SELL", "EVENT_INSPECT", "EVENT_TASK_BEFORE_UPDATE", + "EVENT_AA_BUY", + "EVENT_AA_GAIN" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1737,6 +1739,20 @@ void PerlembParser::ExportEventVariables( break; } + case EVENT_AA_BUY: { + Seperator sep(data); + ExportVar(package_name.c_str(), "aa_cost", sep.arg[0]); + ExportVar(package_name.c_str(), "aa_id", sep.arg[1]); + ExportVar(package_name.c_str(), "aa_previous_id", sep.arg[2]); + ExportVar(package_name.c_str(), "aa_next_id", sep.arg[3]); + break; + } + + case EVENT_AA_GAIN: { + ExportVar(package_name.c_str(), "aa_gained", data); + break; + } + case EVENT_INSPECT: { ExportVar(package_name.c_str(), "target_id", extradata); break; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 2e80e0c97..ffb244759 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3724,6 +3724,15 @@ bool Perl__IsSnowing() return zone->IsSnowing(); } +std::string Perl__getaaname(int aa_id) +{ + if (!zone) { + return std::string(); + } + + return zone->GetAAName(aa_id); +} + void perl_register_quest() { perl::interpreter perl(PERL_GET_THX); @@ -4083,6 +4092,7 @@ void perl_register_quest() package.add("forcedooropen", (void(*)(uint32, bool))&Perl__forcedooropen); package.add("getaaexpmodifierbycharid", (double(*)(uint32, uint32))&Perl__getaaexpmodifierbycharid); package.add("getaaexpmodifierbycharid", (double(*)(uint32, uint32, int16))&Perl__getaaexpmodifierbycharid); + package.add("getaaname", (std::string(*)(int))&Perl__getaaname); package.add("getbodytypename", &Perl__getbodytypename); package.add("getcharidbyname", &Perl__getcharidbyname); package.add("getclassname", (std::string(*)(uint8))&Perl__getclassname); diff --git a/zone/event_codes.h b/zone/event_codes.h index 7c469527e..312fd7d72 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -104,6 +104,8 @@ typedef enum { EVENT_MERCHANT_SELL, EVENT_INSPECT, EVENT_TASK_BEFORE_UPDATE, + EVENT_AA_BUY, + EVENT_AA_GAIN, _LargestEventID } QuestEventID; diff --git a/zone/exp.cpp b/zone/exp.cpp index 9372cef9c..e191e412c 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -715,6 +715,13 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { SendSound(); } + const auto export_string = fmt::format( + "{}", + gained + ); + + parse->EventPlayer(EVENT_AA_GAIN, this, export_string, 0); + /* QS: PlayerLogAARate */ if (RuleB(QueryServ, PlayerLogAARate)){ int add_points = (m_pp.aapoints - last_unspentAA); diff --git a/zone/gm_commands/findaa.cpp b/zone/gm_commands/findaa.cpp new file mode 100755 index 000000000..c5d15437c --- /dev/null +++ b/zone/gm_commands/findaa.cpp @@ -0,0 +1,100 @@ +#include "../client.h" + +void command_findaa(Client *c, const Seperator *sep) +{ + auto arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #findaa [Search Criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int aa_id = std::stoi(sep->arg[1]); + auto aa_name = zone->GetAAName(aa_id); + if (!aa_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "AA {}: {}", + aa_id, + aa_name + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "AA ID {} was not found.", + aa_id + ).c_str() + ); + } + } else { + const auto search_criteria = Strings::ToLower(sep->argplus[1]); + if (!search_criteria.empty()) { + std::map ordered_aas; + + for (const auto& a : zone->aa_abilities) { + ordered_aas[a.second.get()->id] = a.second.get()->name; + } + + int found_count = 0; + for (const auto& a : ordered_aas) { + auto aa_name = zone->GetAAName(a.first); + if (!aa_name.empty()) { + auto aa_name_lower = Strings::ToLower(aa_name); + if (aa_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "AA {}: {}", + a.first, + aa_name + ).c_str() + ); + found_count++; + + if (found_count == 50) { + break; + } + } + } + + if (!found_count) { + c->Message( + Chat::White, + fmt::format( + "No AAs were found matching '{}'.", + search_criteria + ).c_str() + ); + return; + } + + if (found_count == 50) { + c->Message( + Chat::White, + fmt::format( + "50 AAs were found matching '{}', max reached.", + search_criteria + ).c_str() + ); + } else { + auto skill_message = found_count == 1 ? "An AA was" : fmt::format("{} AAs were", found_count); + + c->Message( + Chat::White, + fmt::format( + "{} found matching '{}'.", + skill_message, + search_criteria + ).c_str() + ); + } + } + } +} + diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 65601145f..7fb6e793c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3444,6 +3444,14 @@ bool lua_is_snowing() { return zone->IsSnowing(); } +std::string lua_get_aa_name(int aa_id) { + if (!zone) { + return std::string(); + } + + return zone->GetAAName(aa_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3910,6 +3918,7 @@ luabind::scope lua_register_general() { luabind::def("has_recipe_learned", &lua_has_recipe_learned), luabind::def("is_raining", &lua_is_raining), luabind::def("is_snowing", &lua_is_snowing), + luabind::def("get_aa_name", &lua_get_aa_name), /* Cross Zone @@ -4308,7 +4317,9 @@ luabind::scope lua_register_events() { luabind::value("merchant_buy", static_cast(EVENT_MERCHANT_BUY)), luabind::value("merchant_sell", static_cast(EVENT_MERCHANT_SELL)), luabind::value("inspect", static_cast(EVENT_INSPECT)), - luabind::value("task_before_update", static_cast(EVENT_TASK_BEFORE_UPDATE)) + luabind::value("task_before_update", static_cast(EVENT_TASK_BEFORE_UPDATE)), + luabind::value("aa_buy", static_cast(EVENT_AA_BUY)), + luabind::value("aa_gain", static_cast(EVENT_AA_GAIN)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 42f62c967..32fb760a9 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -148,6 +148,8 @@ const char *LuaEvents[_LargestEventID] = { "event_merchant_sell", "event_inspect", "event_task_before_update", + "event_aa_buy", + "event_aa_gain" }; extern Zone *zone; @@ -249,6 +251,8 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_MERCHANT_BUY] = handle_player_merchant; PlayerArgumentDispatch[EVENT_MERCHANT_SELL] = handle_player_merchant; PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect; + PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy; + PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 99083fdfe..e4fa69b7f 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -898,4 +898,24 @@ void handle_player_merchant(QuestInterface* parse, lua_State* L, Client* client, lua_setfield(L, -2, "item_cost"); } +void handle_player_aa_buy(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + Seperator sep(data.c_str()); + lua_pushinteger(L, std::stoi(sep.arg[0])); + lua_setfield(L, -2, "aa_cost"); + + lua_pushinteger(L, std::stoi(sep.arg[1])); + lua_setfield(L, -2, "aa_id"); + + lua_pushinteger(L, std::stoi(sep.arg[2])); + lua_setfield(L, -2, "aa_previous_id"); + + lua_pushinteger(L, std::stoi(sep.arg[3])); + lua_setfield(L, -2, "aa_next_id"); +} + +void handle_player_aa_gain(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + lua_pushinteger(L, std::stoi(data)); + lua_setfield(L, -2, "aa_gained"); +} + #endif diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 9814b92b2..124e97490 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -125,6 +125,10 @@ void handle_player_merchant(QuestInterface* parse, lua_State* L, Client* client, std::vector* extra_pointers); void handle_player_inspect(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_player_aa_buy(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); +void handle_player_aa_gain(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, diff --git a/zone/zone.cpp b/zone/zone.cpp index 51025d707..8a85bd61c 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2950,3 +2950,29 @@ void Zone::LoadDynamicZoneTemplates() dz_template_cache[dz_template.id] = dz_template; } } + +std::string Zone::GetAAName(int aa_id) +{ + if (!aa_id) { + return std::string(); + } + + int current_aa_id = 0; + + const auto& r = aa_ranks.find(aa_id); + if ( + r != aa_ranks.end() && + r->second.get()->base_ability + ) { + current_aa_id = r->second.get()->base_ability->id; + } + + if (current_aa_id) { + const auto& a = aa_abilities.find(current_aa_id); + if (a != aa_abilities.end()) { + return a->second.get()->name; + } + } + + return std::string(); +} diff --git a/zone/zone.h b/zone/zone.h index 1ceba6b37..3ffd83439 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -249,6 +249,8 @@ public: uint32 GetCurrencyID(uint32 item_id); uint32 GetCurrencyItemID(uint32 currency_id); + std::string GetAAName(int aa_id); + inline bool IsRaining() { return zone_weather == EQ::constants::WeatherTypes::Raining; } inline bool IsSnowing() { return zone_weather == EQ::constants::WeatherTypes::Snowing; }