diff --git a/zone/client.cpp b/zone/client.cpp index a1afb485f..dc6b04307 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10719,3 +10719,93 @@ void Client::SaveDisciplines() CharacterDisciplinesRepository::InsertMany(database, character_discs); } } + +uint16 Client::ScribeSpells(uint8 min_level, uint8 max_level) +{ + int available_book_slot = GetNextAvailableSpellBookSlot(); + std::vector spell_ids = GetScribeableSpells(min_level, max_level); + uint16 spell_count = spell_ids.size(); + uint16 scribed_spells = 0; + if (spell_count > 0) { + for (auto spell_id : spell_ids) { + if (available_book_slot == -1) { + Message( + Chat::Red, + fmt::format( + "Unable to scribe {} ({}) to Spell Book because your Spell Book is full.", + spells[spell_id].name, + spell_id + ).c_str() + ); + break; + } + + if (HasSpellScribed(spell_id)) { + continue; + } + + // defer saving per spell and bulk save at the end + ScribeSpell(spell_id, available_book_slot, true, true); + available_book_slot = GetNextAvailableSpellBookSlot(available_book_slot); + scribed_spells++; + } + } + + if (scribed_spells > 0) { + std::string spell_message = ( + scribed_spells == 1 ? + "a new spell" : + fmt::format("{} new spells", scribed_spells) + ); + Message(Chat::White, fmt::format("You have learned {}!", spell_message).c_str()); + + // bulk insert spells + SaveSpells(); + } + return scribed_spells; +} + +uint16 Client::LearnDisciplines(uint8 min_level, uint8 max_level) +{ + int available_discipline_slot = GetNextAvailableDisciplineSlot(); + int character_id = CharacterID(); + std::vector spell_ids = GetLearnableDisciplines(min_level, max_level); + uint16 discipline_count = spell_ids.size(); + uint16 learned_disciplines = 0; + if (discipline_count > 0) { + for (auto spell_id : spell_ids) { + if (available_discipline_slot == -1) { + Message( + Chat::Red, + fmt::format( + "Unable to learn {} ({}) because your Discipline slots are full.", + spells[spell_id].name, + spell_id + ).c_str() + ); + break; + } + + if (HasDisciplineLearned(spell_id)) { + continue; + } + + GetPP().disciplines.values[available_discipline_slot] = spell_id; + available_discipline_slot = GetNextAvailableDisciplineSlot(available_discipline_slot); + learned_disciplines++; + } + } + + if (learned_disciplines > 0) { + std::string discipline_message = ( + learned_disciplines == 1 ? + "a new discipline" : + fmt::format("{} new disciplines", learned_disciplines) + ); + Message(Chat::White, fmt::format("You have learned {}!", discipline_message).c_str()); + SendDisciplineUpdate(); + SaveDisciplines(); + } + + return learned_disciplines; +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 41fc994ee..7df7bb143 100644 --- a/zone/client.h +++ b/zone/client.h @@ -804,6 +804,10 @@ public: void SaveSpells(); void SaveDisciplines(); + // Bulk Scribe/Learn + uint16 ScribeSpells(uint8 min_level, uint8 max_level); + uint16 LearnDisciplines(uint8 min_level, uint8 max_level); + // defer save used when bulk saving void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false); void UnscribeSpellAll(bool update_client = true); @@ -1026,6 +1030,7 @@ public: int FindSpellBookSlotBySpellID(uint16 spellid); uint32 GetSpellIDByBookSlot(int book_slot); int GetNextAvailableSpellBookSlot(int starting_slot = 0); + int GetNextAvailableDisciplineSlot(int starting_slot = 0); inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; } inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; } uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group); diff --git a/zone/command.cpp b/zone/command.cpp index 45024464b..aea1e96fc 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7854,109 +7854,64 @@ void command_beardcolor(Client *c, const Seperator *sep) void command_scribespells(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } if(sep->argnum < 1 || !sep->IsNumber(1)) { c->Message(Chat::White, "FORMAT: #scribespells "); return; } - uint8 max_level = (uint8)atol(sep->arg[1]); - if (!c->GetGM() && max_level > (uint8)RuleI(Character, MaxLevel)) - max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument - uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument - if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel)) - min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than 1."); + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); return; } + if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Min Level must be less than or equal to Max Level."); + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); return; } - t->Message(Chat::White, "Scribing spells to spellbook."); - if(t != c) - c->Message(Chat::White, "Scribing spells for %s.", t->GetName()); - LogInfo("Scribe spells request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); - - int book_slot = t->GetNextAvailableSpellBookSlot(); - int spell_id = 0; - int count = 0; - - for ( ; spell_id < SPDAT_RECORDS && book_slot < EQ::spells::SPELLBOOK_SIZE; ++spell_id) { - if (book_slot == -1) { - t->Message( - 13, - "Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.", - ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), - spell_id - ); - if (t != c) - c->Message( - 13, - "Error scribing spells: %s ran out of spell book slots on spell %s (%i)", - t->GetName(), - ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), - spell_id - ); - - break; - } - if (spell_id < 0 || spell_id >= SPDAT_RECORDS) { - c->Message(Chat::Red, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS); - return; - } - if (book_slot < 0 || book_slot >= EQ::spells::SPELLBOOK_SIZE) { - c->Message(Chat::Red, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQ::spells::SPELLBOOK_SIZE); - return; - } - - while (true) { - if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level - break; - if (spells[spell_id].skill == 52) - break; - - uint16 spell_id_ = (uint16)spell_id; - if ((spell_id_ != spell_id) || (spell_id != spell_id_)) { - c->Message(Chat::Red, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_); - return; - } - - if (!IsDiscipline(spell_id_) && !t->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed - t->ScribeSpell(spell_id_, book_slot, true, true); - ++count; - } - - break; - } - - book_slot = t->GetNextAvailableSpellBookSlot(book_slot); - } - - if (count > 0) { - t->Message(Chat::White, "Successfully scribed %i spells.", count); - if (t != c) { - c->Message(Chat::White, "Successfully scribed %i spells for %s.", count, t->GetName()); - } - - t->SaveSpells(); - } - else { - t->Message(Chat::White, "No spells scribed."); - if (t != c) { - c->Message(Chat::White, "No spells scribed for %s.", t->GetName()); - } + uint16 scribed_spells = target->ScribeSpells(min_level, max_level); + if (target != c) { + std::string spell_message = ( + scribed_spells > 0 ? + ( + scribed_spells == 1 ? + "A new spell" : + fmt::format("{} New spells", scribed_spells) + ) : + "No new spells" + ); + c->Message( + Chat::White, + fmt::format( + "{} scribed for {}.", + spell_message, + target->GetCleanName() + ).c_str() + ); } } @@ -10942,101 +10897,64 @@ void command_reloadtitles(Client *c, const Seperator *sep) void command_traindisc(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } - if (sep->argnum < 1 || !sep->IsNumber(1)) { + if(sep->argnum < 1 || !sep->IsNumber(1)) { c->Message(Chat::White, "FORMAT: #traindisc "); return; } - uint8 max_level = (uint8)atol(sep->arg[1]); - if (!c->GetGM() && max_level >(uint8)RuleI(Character, MaxLevel)) - max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument - uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument - if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel)) - min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than 1."); + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); return; } + if (min_level > max_level) { - c->Message(Chat::White, "Error: Min Level must be less than or equal to Max Level."); + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); return; } - t->Message(Chat::White, "Training disciplines"); - if(t != c) - c->Message(Chat::White, "Training disciplines for %s.", t->GetName()); - LogInfo("Train disciplines request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); - - int spell_id = 0; - int count = 0; - - bool change = false; - - for( ; spell_id < SPDAT_RECORDS; ++spell_id) { - if (spell_id < 0 || spell_id >= SPDAT_RECORDS) { - c->Message(Chat::Red, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS); - return; - } - - while (true) { - if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level - break; - if (spells[spell_id].skill == 52) - break; - - uint16 spell_id_ = (uint16)spell_id; - if ((spell_id_ != spell_id) || (spell_id != spell_id_)) { - c->Message(Chat::Red, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_); - return; - } - - if (!IsDiscipline(spell_id_)) - break; - - for (uint32 r = 0; r < MAX_PP_DISCIPLINES; ++r) { - if (t->GetPP().disciplines.values[r] == spell_id_) { - t->Message(Chat::Red, "You already know this discipline."); - break; // continue the 1st loop - } - else if (t->GetPP().disciplines.values[r] == 0) { - t->GetPP().disciplines.values[r] = spell_id_; - change = true; - t->Message(Chat::White, "You have learned a new discipline!"); - ++count; // success counter - break; // continue the 1st loop - } // if we get to this point, there's already a discipline in this slot, so we continue onto the next slot - } - - break; - } - } - - if (change) { - t->SendDisciplineUpdate(); - t->SaveDisciplines(); - } - - if (count > 0) { - t->Message(Chat::White, "Successfully trained %u disciplines.", count); - if (t != c) { - c->Message(Chat::White, "Successfully trained %u disciplines for %s.", count, t->GetName()); - } - } - else { - t->Message(Chat::White, "No disciplines trained."); - if (t != c) { - c->Message(Chat::White, "No disciplines trained for %s.", t->GetName()); - } + uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); + if (target != c) { + std::string discipline_message = ( + learned_disciplines > 0 ? + ( + learned_disciplines == 1 ? + "A new discipline" : + fmt::format("{} New disciplines", learned_disciplines) + ) : + "No new disciplines" + ); + c->Message( + Chat::White, + fmt::format( + "{} learned for {}.", + discipline_message, + target->GetCleanName() + ).c_str() + ); } } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 441b490a1..002004274 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2291,6 +2291,26 @@ void Lua_Client::RemoveLDoNWin(uint32 theme_id) { self->UpdateLDoNWinLoss(theme_id, true, true); } +uint16 Lua_Client::ScribeSpells(uint8 min_level, uint8 max_level) { + Lua_Safe_Call_Int(); + return self->ScribeSpells(min_level, max_level); +} + +uint16 Lua_Client::LearnDisciplines(uint8 min_level, uint8 max_level) { + Lua_Safe_Call_Int(); + return self->LearnDisciplines(min_level, max_level); +} + +int Lua_Client::GetNextAvailableDisciplineSlot() { + Lua_Safe_Call_Int(); + return self->GetNextAvailableDisciplineSlot(); +} + +int Lua_Client::GetNextAvailableDisciplineSlot(int starting_slot) { + Lua_Safe_Call_Int(); + return self->GetNextAvailableDisciplineSlot(starting_slot); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2673,7 +2693,11 @@ luabind::scope lua_register_client() { .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) .def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss) - .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin); + .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin) + .def("ScribeSpells", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::ScribeSpells) + .def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableDisciplineSlot) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableDisciplineSlot); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index a5fbd0834..5bab5c219 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -176,11 +176,13 @@ public: luabind::object GetScribeableSpells(lua_State* L, uint8 min_level, uint8 max_level); void ScribeSpell(int spell_id, int slot); void ScribeSpell(int spell_id, int slot, bool update_client); + uint16 ScribeSpells(uint8 min_level, uint8 max_level); void UnscribeSpell(int slot); void UnscribeSpell(int slot, bool update_client); void UnscribeSpellAll(); void UnscribeSpellAll(bool update_client); void TrainDisc(int itemid); + uint16 LearnDisciplines(uint8 min_level, uint8 max_level); void TrainDiscBySpellID(int32 spell_id); int GetDiscSlotBySpellID(int32 spell_id); void UntrainDisc(int slot); @@ -300,6 +302,8 @@ public: void ClearCompassMark(); int GetNextAvailableSpellBookSlot(); int GetNextAvailableSpellBookSlot(int start); + int GetNextAvailableDisciplineSlot(); + int GetNextAvailableDisciplineSlot(int starting_slot); uint32 GetSpellIDByBookSlot(int book_slot); int FindSpellBookSlotBySpellID(int spell_id); void UpdateTaskActivity(int task, int activity, int count); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 264b3b09b..3d3aa96b3 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5865,6 +5865,68 @@ XS(XS_Client_RemoveLDoNWin) { XSRETURN_EMPTY; } +XS(XS_Client_GetFreeDisciplineSlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFreeDisciplineSlot) { + dXSARGS; + if (items != 1 || items != 2) + Perl_croak(aTHX_ "Usage: Client::GetFreeDisciplineSlot(THIS, [int starting_slot = 0])"); // @categories Spells and Disciplines + { + Client *THIS; + int free_discipline_slot; + int starting_slot = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + if (items == 2) { + starting_slot = SvIV(ST(1)); + } + + free_discipline_slot = THIS->GetNextAvailableDisciplineSlot(starting_slot); + XSprePUSH; + PUSHi((IV) free_discipline_slot); + } + XSRETURN(1); +} + +XS(XS_Client_ScribeSpells); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ScribeSpells) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::ScribeSpells(THIS, uint8 min_level, uint8 max_level)"); // @categories Spells and Disciplines + { + Client *THIS; + uint8 min_level = (uint8) SvUV(ST(1)); + uint8 max_level = (uint8) SvUV(ST(2)); + uint16 scribed_spells = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + scribed_spells = THIS->ScribeSpells(min_level, max_level); + XSprePUSH; + PUSHu((UV) scribed_spells); + } + XSRETURN(1); +} + +XS(XS_Client_LearnDisciplines); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LearnDisciplines) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::LearnDisciplines(THIS, uint8 min_level, uint8 max_level)"); // @categories Spells and Disciplines + { + Client *THIS; + uint8 min_level = (uint8) SvUV(ST(1)); + uint8 max_level = (uint8) SvUV(ST(2)); + uint16 learned_disciplines = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + learned_disciplines = THIS->LearnDisciplines(min_level, max_level); + XSprePUSH; + PUSHu((UV) learned_disciplines); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -6186,6 +6248,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$"); newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$"); newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, file, "$$$"); + newXSproto(strcpy(buf, "GetFreeDisciplineSlot"), XS_Client_GetFreeDisciplineSlot, file, "$;$"); + newXSproto(strcpy(buf, "ScribeSpells"), XS_Client_ScribeSpells, file, "$$$"); + newXSproto(strcpy(buf, "LearnDisciplines"), XS_Client_LearnDisciplines, file, "$$$"); XSRETURN_YES; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 4da5c2b1d..956b1882b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1116,69 +1116,12 @@ void QuestManager::permagender(int gender_id) { uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { QuestManagerCurrentQuestVars(); - int book_slot = initiator->GetNextAvailableSpellBookSlot(); - std::vector spell_ids = initiator->GetScribeableSpells(min_level, max_level); - int spell_count = spell_ids.size(); - int spells_learned = 0; - if (spell_count > 0) { - for (auto spell_id : spell_ids) { - if (book_slot == -1) { - initiator->Message( - Chat::Red, - "Unable to scribe spell %s (%i) to Spell Book: Spell Book is Full.", spells[spell_id].name, spell_id - ); - break; - } - - if (initiator->HasSpellScribed(spell_id)) - continue; - - // defer saving per spell and bulk save at the end - initiator->ScribeSpell(spell_id, book_slot, true, true); - book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot); - spells_learned++; - } - } - - if (spells_learned > 0) { - std::string spell_message = (spells_learned == 1 ? "a new spell" : fmt::format("{} new spells", spells_learned)); - initiator->Message(Chat::White, fmt::format("You have learned {}!", spell_message).c_str()); - - // bulk insert spells - initiator->SaveSpells(); - } - return spells_learned; + return initiator->ScribeSpells(min_level, max_level); } uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { QuestManagerCurrentQuestVars(); - int character_id = initiator->CharacterID(); - std::vector spell_ids = initiator->GetLearnableDisciplines(min_level, max_level); - int discipline_count = spell_ids.size(); - int disciplines_learned = 0; - if (discipline_count > 0) { - for (auto spell_id : spell_ids) { - if (initiator->HasDisciplineLearned(spell_id)) - continue; - - for (uint32 index = 0; index < MAX_PP_DISCIPLINES; index++) { - if (initiator->GetPP().disciplines.values[index] == 0) { - initiator->GetPP().disciplines.values[index] = spell_id; - database.SaveCharacterDisc(character_id, index, spell_id); - disciplines_learned++; - break; - } - } - } - } - - if (disciplines_learned > 0) { - std::string discipline_message = (disciplines_learned == 1 ? "a new discipline" : fmt::format("{} new disciplines", disciplines_learned)); - initiator->SendDisciplineUpdate(); - initiator->Message(Chat::White, fmt::format("You have learned {}!", discipline_message).c_str()); - } - - return disciplines_learned; + return initiator->LearnDisciplines(min_level, max_level); } void QuestManager::unscribespells() { diff --git a/zone/spells.cpp b/zone/spells.cpp index 9a7ca6647..5def206bc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6257,5 +6257,12 @@ bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id) return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id, false); } +int Client::GetNextAvailableDisciplineSlot(int starting_slot) { + for (uint32 index = starting_slot; index < MAX_PP_DISCIPLINES; index++) { + if (!IsValidSpell(GetPP().disciplines.values[index])) { + return index; + } + } - + return -1; // Return -1 if No Slots open +} \ No newline at end of file