diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index b3dcb597a..0a22718ea 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -541,6 +541,8 @@ SET(gm_commands gm_commands/undyeme.cpp gm_commands/unfreeze.cpp gm_commands/unlock.cpp + gm_commands/unmemspell.cpp + gm_commands/unmemspells.cpp gm_commands/unscribespell.cpp gm_commands/unscribespells.cpp gm_commands/untraindisc.cpp diff --git a/zone/client.h b/zone/client.h index 5dea68d2b..649f9d31f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -792,8 +792,9 @@ public: void UnmemSpell(int slot, bool update_client = true); void UnmemSpellBySpellID(int32 spell_id); void UnmemSpellAll(bool update_client = true); + int FindEmptyMemSlot(); uint16 FindMemmedSpellBySlot(int slot); - int FindMemmedSpellByID(uint16 spell_id); + int FindMemmedSpellBySpellID(uint16 spell_id); int MemmedCount(); std::vector GetLearnableDisciplines(uint8 min_level = 1, uint8 max_level = 0); std::vector GetLearnedDisciplines(); diff --git a/zone/command.cpp b/zone/command.cpp index a17361d18..c1e16aa28 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -241,7 +241,7 @@ int command_init(void) command_add("makepet", "[level] [class] [race] [texture] - Make a pet", AccountStatus::Guide, command_makepet) || command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || command_add("maxskills", "Maxes skills for you.", AccountStatus::GMMgmt, command_max_all_skills) || - command_add("memspell", "[Slot] [Spell ID] - Memorize a Spell by ID in the specified Slot", AccountStatus::Guide, command_memspell) || + command_add("memspell", "[Spell ID] [Spell Gem] - Memorize a Spell by ID to the specified Spell Gem for you or your target", AccountStatus::Guide, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || @@ -378,6 +378,8 @@ int command_init(void) command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || + command_add("unmemspell", "[Spell ID] - Unmemorize a Spell by ID for you or your target", AccountStatus::Guide, command_unmemspell) || + command_add("unmemspells", " - Unmemorize all spells for you or your target", AccountStatus::Guide, command_unmemspells) || command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", AccountStatus::GMCoder, command_unscribespell) || command_add("unscribespells", "- Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || command_add("untraindisc", "[spellid] - Untrain specified discipline from your target.", AccountStatus::GMCoder, command_untraindisc) || diff --git a/zone/command.h b/zone/command.h index d9113a5d5..9d64004a5 100644 --- a/zone/command.h +++ b/zone/command.h @@ -301,6 +301,8 @@ void command_undye(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep); void command_unfreeze(Client *c, const Seperator *sep); void command_unlock(Client *c, const Seperator *sep); +void command_unmemspell(Client *c, const Seperator *sep); +void command_unmemspells(Client *c, const Seperator *sep); void command_unscribespell(Client *c, const Seperator *sep); void command_unscribespells(Client *c, const Seperator *sep); void command_untraindisc(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/memspell.cpp b/zone/gm_commands/memspell.cpp index 6fce2900f..673d2efd0 100755 --- a/zone/gm_commands/memspell.cpp +++ b/zone/gm_commands/memspell.cpp @@ -5,43 +5,75 @@ void command_memspell(Client *c, const Seperator *sep) int arguments = sep->argnum; if ( !arguments || - !sep->IsNumber(1) || - !sep->IsNumber(2) + !sep->IsNumber(1) ) { - c->Message(Chat::White, "Usage: #memspell [Slot] [Spell ID]"); + c->Message(Chat::White, "Usage: #memspell [Spell ID] [Spell Gem]"); return; } - Client* target = c; + auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { target = c->GetTarget()->CastToClient(); } - uint32 spell_gem = std::stoul(sep->arg[1]); - uint32 slot = (spell_gem - 1); - uint16 spell_id = static_cast(std::stoul(sep->arg[2])); - if ( - IsValidSpell(spell_id) && - slot < EQ::spells::SPELL_GEM_COUNT - ) { - target->MemSpell(spell_id, slot); + auto spell_id = static_cast(std::stoul(sep->arg[1])); + if (!IsValidSpell(spell_id)) { c->Message( Chat::White, fmt::format( - "{} ({}) has been memorized to Spell Gem {} ({}) for {}.", - GetSpellName(spell_id), - spell_id, - spell_gem, - slot, + "Spell ID {} could not be found.", + spell_id + ).c_str() + ); + return; + } + + auto empty_slot = target->FindEmptyMemSlot(); + if (empty_slot == -1) { + c->Message( + Chat::White, + fmt::format( + "{} not have a place to memorize {} ({}).", ( c == target ? - "yourself" : + "You do" : fmt::format( - "{} ({})", + "{} ({}) does", target->GetCleanName(), target->GetID() ) - ) + ), + GetSpellName(spell_id), + spell_id + ).c_str() + ); + return; + } + + auto spell_gem = sep->IsNumber(2) ? std::stoul(sep->arg[2]) : empty_slot; + if (spell_gem > EQ::spells::SPELL_GEM_COUNT) { + c->Message( + Chat::White, + fmt::format( + "Spell Gems range from 0 to {}.", + EQ::spells::SPELL_GEM_COUNT + ).c_str() + ); + return; + } + + target->MemSpell(spell_id, spell_gem); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) memorized to spell gem {} for {} ({}).", + GetSpellName(spell_id), + spell_id, + spell_gem, + target->GetCleanName(), + target->GetID() ).c_str() ); } diff --git a/zone/gm_commands/unmemspell.cpp b/zone/gm_commands/unmemspell.cpp new file mode 100644 index 000000000..67ffc5664 --- /dev/null +++ b/zone/gm_commands/unmemspell.cpp @@ -0,0 +1,68 @@ +#include "../client.h" + +void command_unmemspell(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) + ) { + c->Message(Chat::White, "Usage: #unmemspell [Spell ID]"); + return; + } + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + auto spell_id = static_cast(std::stoul(sep->arg[1])); + if (!IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} could not be found.", + spell_id + ).c_str() + ); + return; + } + + auto spell_gem = target->FindMemmedSpellBySpellID(spell_id); + if (spell_gem == -1) { + c->Message( + Chat::White, + fmt::format( + "{} not have {} ({}) memorized.", + ( + c == target ? + "You do" : + fmt::format( + "{} ({}) does", + target->GetCleanName(), + target->GetID() + ) + ), + GetSpellName(spell_id), + spell_id + ).c_str() + ); + return; + } + + target->UnmemSpellBySpellID(spell_id); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) unmemorized for {} ({}) from spell gem {}.", + GetSpellName(spell_id), + spell_id, + target->GetCleanName(), + target->GetID(), + spell_gem + ).c_str() + ); + } +} diff --git a/zone/gm_commands/unmemspells.cpp b/zone/gm_commands/unmemspells.cpp new file mode 100644 index 000000000..f5f6f0987 --- /dev/null +++ b/zone/gm_commands/unmemspells.cpp @@ -0,0 +1,43 @@ +#include "../client.h" + +void command_unmemspells(Client *c, const Seperator *sep) +{ + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + auto memmed_count = target->MemmedCount(); + if (!memmed_count) { + c->Message( + Chat::White, + fmt::format( + "{} no spells to unmemorize.", + ( + c == target ? + "You have" : + fmt::format( + "{} ({}) has", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); + return; + } + + target->UnmemSpellAll(); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) has had {} spells unmemorized.", + target->GetCleanName(), + target->GetID(), + memmed_count + ).c_str() + ); + } +} diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a7459cff3..ec4c1bd65 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -650,6 +650,16 @@ uint16 Lua_Client::FindMemmedSpellBySlot(int slot) { return self->FindMemmedSpellBySlot(slot); } +int Lua_Client::FindMemmedSpellBySpellID(uint16 spell_id) { + Lua_Safe_Call_Int(); + return self->FindMemmedSpellBySpellID(spell_id); +} + +int Lua_Client::FindEmptyMemSlot() { + Lua_Safe_Call_Int(); + return self->FindEmptyMemSlot(); +} + int Lua_Client::MemmedCount() { Lua_Safe_Call_Int(); return self->MemmedCount(); @@ -2378,7 +2388,9 @@ luabind::scope lua_register_client() { .def("Escape", (void(Lua_Client::*)(void))&Lua_Client::Escape) .def("FailTask", (void(Lua_Client::*)(int))&Lua_Client::FailTask) .def("FilteredMessage", &Lua_Client::FilteredMessage) + .def("FindEmptyMemSlot", (int(Lua_Client::*)(void))&Lua_Client::FindEmptyMemSlot) .def("FindMemmedSpellBySlot", (uint16(Lua_Client::*)(int))&Lua_Client::FindMemmedSpellBySlot) + .def("FindMemmedSpellBySpellID", (int(Lua_Client::*)(uint16))&Lua_Client::FindMemmedSpellBySpellID) .def("FindSpellBookSlotBySpellID", (int(Lua_Client::*)(int))&Lua_Client::FindSpellBookSlotBySpellID) .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) diff --git a/zone/lua_client.h b/zone/lua_client.h index 5bab5c219..7e91ea9f1 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -163,7 +163,9 @@ public: void UnmemSpellBySpellID(int32 spell_id); void UnmemSpellAll(); void UnmemSpellAll(bool update_client); + int FindEmptyMemSlot(); uint16 FindMemmedSpellBySlot(int slot); + int FindMemmedSpellBySpellID(uint16 spell_id); int MemmedCount(); luabind::object GetLearnableDisciplines(lua_State* L); luabind::object GetLearnableDisciplines(lua_State* L, uint8 min_level); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index fc300e200..bb5ee2310 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1897,6 +1897,23 @@ XS(XS_Client_UnmemSpellAll) { XSRETURN_EMPTY; } +XS(XS_Client_FindEmptyMemSlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_FindEmptyMemSlot) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::FindEmptyMemSlot(THIS)"); // @categories Account and Character, Spells and Disciplines + { + Client *THIS; + int RETVAL; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + RETVAL = THIS->FindEmptyMemSlot(); + XSprePUSH; + PUSHi((IV) RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_FindMemmedSpellBySlot); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_FindMemmedSpellBySlot) { dXSARGS; @@ -1915,6 +1932,24 @@ XS(XS_Client_FindMemmedSpellBySlot) { XSRETURN(1); } +XS(XS_Client_FindMemmedSpellBySpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_FindMemmedSpellBySpellID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::FindMemmedSpellBySpellID(THIS, uint16 spell_id)"); // @categories Account and Character, Spells and Disciplines + { + Client *THIS; + int RETVAL; + dXSTARG; + uint16 spell_id = (uint16) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + RETVAL = THIS->FindMemmedSpellBySpellID(spell_id); + XSprePUSH; + PUSHi((IV) RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_MemmedCount); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_MemmedCount) { dXSARGS; @@ -5960,7 +5995,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$"); + newXSproto(strcpy(buf, "FindEmptyMemSlot"), XS_Client_FindEmptyMemSlot, file, "$"); newXSproto(strcpy(buf, "FindMemmedSpellBySlot"), XS_Client_FindMemmedSpellBySlot, file, "$$"); + newXSproto(strcpy(buf, "FindMemmedSpellBySpellID"), XS_Client_FindMemmedSpellBySpellID, file, "$$"); newXSproto(strcpy(buf, "Fling"), XS_Client_Fling, file, "$$$$$;$$"); newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); diff --git a/zone/spells.cpp b/zone/spells.cpp index 60d7e9516..e14d100ce 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5246,7 +5246,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id) manachange->spell_id = spell_id; manachange->stamina = CastToClient()->GetEndurance(); manachange->keepcasting = 0; - manachange->slot = CastToClient()->FindMemmedSpellByID(spell_id); + manachange->slot = CastToClient()->FindMemmedSpellBySpellID(spell_id); outapp->priority = 6; CastToClient()->QueuePacket(outapp); safe_delete(outapp); @@ -5377,88 +5377,98 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) void Client::MemSpell(uint16 spell_id, int slot, bool update_client) { - if(slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) + if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) { return; + } - if(update_client) - { - if(m_pp.mem_spells[slot] != 0xFFFFFFFF) + if(update_client) { + if (IsValidSpell(m_pp.mem_spells[slot])) { UnmemSpell(slot, update_client); + } } m_pp.mem_spells[slot] = spell_id; LogSpells("Spell [{}] memorized into slot [{}]", spell_id, slot); - database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + database.SaveCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot); - if(update_client) - { + if(update_client) { MemorizeSpell(slot, spell_id, memSpellMemorize); } } void Client::UnmemSpell(int slot, bool update_client) { - if(slot > EQ::spells::SPELL_GEM_COUNT || slot < 0) + if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) { return; + } LogSpells("Spell [{}] forgotten from slot [{}]", m_pp.mem_spells[slot], slot); m_pp.mem_spells[slot] = 0xFFFFFFFF; - database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + database.DeleteCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot); - if(update_client) - { + if(update_client) { MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); } } void Client::UnmemSpellBySpellID(int32 spell_id) { - for(int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { - if(m_pp.mem_spells[i] == spell_id) { - UnmemSpell(i, true); - break; - } + auto spell_gem = FindMemmedSpellBySpellID(spell_id); + if (spell_gem >= EQ::spells::SPELL_GEM_COUNT || spell_gem < 0) { + return; } + + UnmemSpell(spell_gem); } void Client::UnmemSpellAll(bool update_client) { - int i; - - for(i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) - if(m_pp.mem_spells[i] != 0xFFFFFFFF) - UnmemSpell(i, update_client); + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem])) { + UnmemSpell(spell_gem, update_client); + } + } } uint32 Client::GetSpellIDByBookSlot(int book_slot) { if (book_slot <= EQ::spells::SPELLBOOK_SIZE) { return GetSpellByBookSlot(book_slot); } - return -1; //default + return -1; +} + +int Client::FindEmptyMemSlot() { + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (!IsValidSpell(m_pp.mem_spells[spell_gem])) { + return spell_gem; + } + } + return -1; } uint16 Client::FindMemmedSpellBySlot(int slot) { - if (m_pp.mem_spells[slot] != 0xFFFFFFFF) + if (IsValidSpell(m_pp.mem_spells[slot])) { return m_pp.mem_spells[slot]; - + } return 0; } int Client::MemmedCount() { int memmed_count = 0; - for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) - if (m_pp.mem_spells[i] != 0xFFFFFFFF) + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem])) { memmed_count++; - + } + } return memmed_count; } -int Client::FindMemmedSpellByID(uint16 spell_id) { - for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { - if (m_pp.mem_spells[i] == spell_id) { - return i; +int Client::FindMemmedSpellBySpellID(uint16 spell_id) { + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem]) && m_pp.mem_spells[spell_gem] == spell_id) { + return spell_gem; } } return -1;