mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-14 15:41:30 +00:00
[Quest API] Simplify bulk Scribe and Train logic. (#1660)
* [Quest API] Simplify bulk Scribe and Train logic. - Add $client->GetFreeDisciplineSlot(starting_slot) to Perl. - Add $client->ScribeSpells(min_level, max_level) to Perl. - Add $client->LearnDisciplines(min_level, max_level) to Perl. - Add client:GetNextAvailableDisciplineSlot(starting_slot) to Lua. - Add client:ScribeSpells(min_level, max_level) to Lua. - Add client:LearnDisciplines(min_level, max_level) to Lua. Convert quest::scribespells() and quest::traindisc() to use new ScribeSpells and LearnDisciplines methods for consistency. * Update command.cpp
This commit is contained in:
parent
17aaab1f9d
commit
9d515b20f2
@ -10719,3 +10719,93 @@ void Client::SaveDisciplines()
|
|||||||
CharacterDisciplinesRepository::InsertMany(database, character_discs);
|
CharacterDisciplinesRepository::InsertMany(database, character_discs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16 Client::ScribeSpells(uint8 min_level, uint8 max_level)
|
||||||
|
{
|
||||||
|
int available_book_slot = GetNextAvailableSpellBookSlot();
|
||||||
|
std::vector<int> 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<int> 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;
|
||||||
|
}
|
||||||
@ -804,6 +804,10 @@ public:
|
|||||||
void SaveSpells();
|
void SaveSpells();
|
||||||
void SaveDisciplines();
|
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
|
// defer save used when bulk saving
|
||||||
void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false);
|
void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false);
|
||||||
void UnscribeSpellAll(bool update_client = true);
|
void UnscribeSpellAll(bool update_client = true);
|
||||||
@ -1026,6 +1030,7 @@ public:
|
|||||||
int FindSpellBookSlotBySpellID(uint16 spellid);
|
int FindSpellBookSlotBySpellID(uint16 spellid);
|
||||||
uint32 GetSpellIDByBookSlot(int book_slot);
|
uint32 GetSpellIDByBookSlot(int book_slot);
|
||||||
int GetNextAvailableSpellBookSlot(int starting_slot = 0);
|
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 uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; }
|
||||||
inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; }
|
inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; }
|
||||||
uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group);
|
uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group);
|
||||||
|
|||||||
256
zone/command.cpp
256
zone/command.cpp
@ -7854,109 +7854,64 @@ void command_beardcolor(Client *c, const Seperator *sep)
|
|||||||
|
|
||||||
void command_scribespells(Client *c, const Seperator *sep)
|
void command_scribespells(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
Client *t = c;
|
Client *target = c;
|
||||||
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) {
|
||||||
t = c->GetTarget()->CastToClient();
|
target = c->GetTarget()->CastToClient();
|
||||||
|
}
|
||||||
|
|
||||||
if(sep->argnum < 1 || !sep->IsNumber(1)) {
|
if(sep->argnum < 1 || !sep->IsNumber(1)) {
|
||||||
c->Message(Chat::White, "FORMAT: #scribespells <max level> <min level>");
|
c->Message(Chat::White, "FORMAT: #scribespells <max level> <min level>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 max_level = (uint8)atol(sep->arg[1]);
|
uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel);
|
||||||
if (!c->GetGM() && max_level > (uint8)RuleI(Character, MaxLevel))
|
uint8 max_level = (uint8) std::stoi(sep->arg[1]);
|
||||||
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
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()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level
|
||||||
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
|
if (max_level > rule_max_level) {
|
||||||
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the 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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_level > max_level) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->Message(Chat::White, "Scribing spells to spellbook.");
|
uint16 scribed_spells = target->ScribeSpells(min_level, max_level);
|
||||||
if(t != c)
|
if (target != c) {
|
||||||
c->Message(Chat::White, "Scribing spells for %s.", t->GetName());
|
std::string spell_message = (
|
||||||
LogInfo("Scribe spells request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level);
|
scribed_spells > 0 ?
|
||||||
|
(
|
||||||
int book_slot = t->GetNextAvailableSpellBookSlot();
|
scribed_spells == 1 ?
|
||||||
int spell_id = 0;
|
"A new spell" :
|
||||||
int count = 0;
|
fmt::format("{} New spells", scribed_spells)
|
||||||
|
) :
|
||||||
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQ::spells::SPELLBOOK_SIZE; ++spell_id) {
|
"No new spells"
|
||||||
if (book_slot == -1) {
|
);
|
||||||
t->Message(
|
c->Message(
|
||||||
13,
|
Chat::White,
|
||||||
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
|
fmt::format(
|
||||||
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
|
"{} scribed for {}.",
|
||||||
spell_id
|
spell_message,
|
||||||
);
|
target->GetCleanName()
|
||||||
if (t != c)
|
).c_str()
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10942,101 +10897,64 @@ void command_reloadtitles(Client *c, const Seperator *sep)
|
|||||||
|
|
||||||
void command_traindisc(Client *c, const Seperator *sep)
|
void command_traindisc(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
Client *t = c;
|
Client *target = c;
|
||||||
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) {
|
||||||
t = c->GetTarget()->CastToClient();
|
target = c->GetTarget()->CastToClient();
|
||||||
|
}
|
||||||
|
|
||||||
if (sep->argnum < 1 || !sep->IsNumber(1)) {
|
if(sep->argnum < 1 || !sep->IsNumber(1)) {
|
||||||
c->Message(Chat::White, "FORMAT: #traindisc <max level> <min level>");
|
c->Message(Chat::White, "FORMAT: #traindisc <max level> <min level>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 max_level = (uint8)atol(sep->arg[1]);
|
uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel);
|
||||||
if (!c->GetGM() && max_level >(uint8)RuleI(Character, MaxLevel))
|
uint8 max_level = (uint8) std::stoi(sep->arg[1]);
|
||||||
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
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()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level
|
||||||
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
|
if (max_level > rule_max_level) {
|
||||||
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the 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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_level > max_level) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->Message(Chat::White, "Training disciplines");
|
uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level);
|
||||||
if(t != c)
|
if (target != c) {
|
||||||
c->Message(Chat::White, "Training disciplines for %s.", t->GetName());
|
std::string discipline_message = (
|
||||||
LogInfo("Train disciplines request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level);
|
learned_disciplines > 0 ?
|
||||||
|
(
|
||||||
int spell_id = 0;
|
learned_disciplines == 1 ?
|
||||||
int count = 0;
|
"A new discipline" :
|
||||||
|
fmt::format("{} New disciplines", learned_disciplines)
|
||||||
bool change = false;
|
) :
|
||||||
|
"No new disciplines"
|
||||||
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
|
);
|
||||||
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
|
c->Message(
|
||||||
c->Message(Chat::Red, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
|
Chat::White,
|
||||||
return;
|
fmt::format(
|
||||||
}
|
"{} learned for {}.",
|
||||||
|
discipline_message,
|
||||||
while (true) {
|
target->GetCleanName()
|
||||||
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
|
).c_str()
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2291,6 +2291,26 @@ void Lua_Client::RemoveLDoNWin(uint32 theme_id) {
|
|||||||
self->UpdateLDoNWinLoss(theme_id, true, true);
|
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() {
|
luabind::scope lua_register_client() {
|
||||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@ -2673,7 +2693,11 @@ luabind::scope lua_register_client() {
|
|||||||
.def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID)
|
.def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID)
|
||||||
.def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems)
|
.def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems)
|
||||||
.def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss)
|
.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() {
|
luabind::scope lua_register_inventory_where() {
|
||||||
|
|||||||
@ -176,11 +176,13 @@ public:
|
|||||||
luabind::object GetScribeableSpells(lua_State* L, uint8 min_level, uint8 max_level);
|
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);
|
||||||
void ScribeSpell(int spell_id, int slot, bool update_client);
|
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);
|
||||||
void UnscribeSpell(int slot, bool update_client);
|
void UnscribeSpell(int slot, bool update_client);
|
||||||
void UnscribeSpellAll();
|
void UnscribeSpellAll();
|
||||||
void UnscribeSpellAll(bool update_client);
|
void UnscribeSpellAll(bool update_client);
|
||||||
void TrainDisc(int itemid);
|
void TrainDisc(int itemid);
|
||||||
|
uint16 LearnDisciplines(uint8 min_level, uint8 max_level);
|
||||||
void TrainDiscBySpellID(int32 spell_id);
|
void TrainDiscBySpellID(int32 spell_id);
|
||||||
int GetDiscSlotBySpellID(int32 spell_id);
|
int GetDiscSlotBySpellID(int32 spell_id);
|
||||||
void UntrainDisc(int slot);
|
void UntrainDisc(int slot);
|
||||||
@ -300,6 +302,8 @@ public:
|
|||||||
void ClearCompassMark();
|
void ClearCompassMark();
|
||||||
int GetNextAvailableSpellBookSlot();
|
int GetNextAvailableSpellBookSlot();
|
||||||
int GetNextAvailableSpellBookSlot(int start);
|
int GetNextAvailableSpellBookSlot(int start);
|
||||||
|
int GetNextAvailableDisciplineSlot();
|
||||||
|
int GetNextAvailableDisciplineSlot(int starting_slot);
|
||||||
uint32 GetSpellIDByBookSlot(int book_slot);
|
uint32 GetSpellIDByBookSlot(int book_slot);
|
||||||
int FindSpellBookSlotBySpellID(int spell_id);
|
int FindSpellBookSlotBySpellID(int spell_id);
|
||||||
void UpdateTaskActivity(int task, int activity, int count);
|
void UpdateTaskActivity(int task, int activity, int count);
|
||||||
|
|||||||
@ -5865,6 +5865,68 @@ XS(XS_Client_RemoveLDoNWin) {
|
|||||||
XSRETURN_EMPTY;
|
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
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
#endif
|
#endif
|
||||||
@ -6186,6 +6248,9 @@ XS(boot_Client) {
|
|||||||
newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$");
|
newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$");
|
||||||
newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$");
|
newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$");
|
||||||
newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, 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;
|
XSRETURN_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1116,69 +1116,12 @@ void QuestManager::permagender(int gender_id) {
|
|||||||
|
|
||||||
uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
|
uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
|
||||||
QuestManagerCurrentQuestVars();
|
QuestManagerCurrentQuestVars();
|
||||||
int book_slot = initiator->GetNextAvailableSpellBookSlot();
|
return initiator->ScribeSpells(min_level, max_level);
|
||||||
std::vector<int> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
|
uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
|
||||||
QuestManagerCurrentQuestVars();
|
QuestManagerCurrentQuestVars();
|
||||||
int character_id = initiator->CharacterID();
|
return initiator->LearnDisciplines(min_level, max_level);
|
||||||
std::vector<int> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuestManager::unscribespells() {
|
void QuestManager::unscribespells() {
|
||||||
|
|||||||
@ -6257,5 +6257,12 @@ bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id)
|
|||||||
return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id, false);
|
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
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user