From 50db7637aa59b7d459bb47f86874628b02364fff Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 25 May 2023 19:18:14 -0400 Subject: [PATCH] [Quest API] Add Memorize and Scribe Spell Events to Perl/Lua (#3363) * [Quest API] Add Memorize and Scribe Spell Events to Perl/Lua # Perl - Add `EVENT_MEMORIZE_SPELL`. - Add `EVENT_UNMEMORIZE_SPELL`. - Add `EVENT_SCRIBE_SPELL`. - Add `EVENT_UNSCRIBE_SPELL`. # Lua - Add `event_memorize_spell`. - Add `event_unmemorize_spell`. - Add `event_scribe_spell`. - Add `event_unscribe_spell`. # Notes - Allows operators to perform events on memorization, unmemorization, scribe, or unscribe. - Cleaned up target description messages for `#unscribespell`. * Update client.cpp --- common/eq_constants.h | 7 ++++ zone/client.cpp | 60 ++++++++++++++++++++++++++---- zone/client.h | 2 +- zone/embparser.cpp | 17 +++++++++ zone/event_codes.h | 4 ++ zone/gm_commands/unscribespell.cpp | 20 +++++----- zone/lua_general.cpp | 6 ++- zone/lua_parser.cpp | 10 ++++- zone/lua_parser_events.cpp | 29 +++++++++++++++ zone/lua_parser_events.h | 9 +++++ zone/spells.cpp | 25 +++++++++---- 11 files changed, 161 insertions(+), 28 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index ae2cce84b..b266b3905 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -1045,4 +1045,11 @@ enum ResurrectionActions Accept }; +enum ScribeSpellActions +{ + Scribe, + Memorize, + Unmemorize +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/zone/client.cpp b/zone/client.cpp index 04e2a0b8f..cc28bbce1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2783,18 +2783,64 @@ void Client::GMKill() { safe_delete(outapp); } -void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 reduction){ - if (slot < 0 || slot >= EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) +void Client::MemorizeSpell(uint32 slot, uint32 spell_id, uint32 scribing, uint32 reduction){ + if ( + !EQ::ValueWithin( + slot, + 0, + (EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize - 1) + ) + ) { return; - if ((spellid < 3 || spellid > EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax) && spellid != 0xFFFFFFFF) + } + + if ( + !EQ::ValueWithin( + spell_id, + 3, + EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax + ) && + spell_id != UINT32_MAX + ) { return; + } + auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct)); - MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer; - mss->scribing=scribing; - mss->slot=slot; - mss->spell_id=spellid; + + auto* mss = (MemorizeSpell_Struct*) outapp->pBuffer; + + mss->scribing = scribing; + mss->slot = slot; + mss->spell_id = spell_id; mss->reduction = reduction; + outapp->priority = 5; + + if ( + parse->PlayerHasQuestSub(EVENT_SCRIBE_SPELL) || + parse->PlayerHasQuestSub(EVENT_MEMORIZE_SPELL) || + parse->PlayerHasQuestSub(EVENT_UNMEMORIZE_SPELL) + ) { + const auto export_string = fmt::format("{} {}", slot, spell_id); + + if ( + scribing == ScribeSpellActions::Memorize && + parse->PlayerHasQuestSub(EVENT_MEMORIZE_SPELL) + ) { + parse->EventPlayer(EVENT_MEMORIZE_SPELL, this, export_string, 0); + } else if ( + scribing == ScribeSpellActions::Unmemorize && + parse->PlayerHasQuestSub(EVENT_UNMEMORIZE_SPELL) + ) { + parse->EventPlayer(EVENT_UNMEMORIZE_SPELL, this, export_string, 0); + } else if ( + scribing == ScribeSpellActions::Scribe && + parse->PlayerHasQuestSub(EVENT_SCRIBE_SPELL) + ) { + parse->EventPlayer(EVENT_SCRIBE_SPELL, this, export_string, 0); + } + } + QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/client.h b/zone/client.h index bc047a076..68337a9c2 100644 --- a/zone/client.h +++ b/zone/client.h @@ -932,7 +932,7 @@ public: inline uint32 GetAAPercent() const { return m_epp.perAA; } void SetAATitle(std::string title); void SetTitleSuffix(std::string suffix); - void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0); + void MemorizeSpell(uint32 slot, uint32 spell_id, uint32 scribing, uint32 reduction = 0); // Item methods void UseAugmentContainer(int container_slot); diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 88d2305ce..6763b5fa7 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -179,6 +179,10 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_ITEM_CLICK_CAST_CLIENT", "EVENT_DESTROY_ITEM_CLIENT", "EVENT_DROP_ITEM_CLIENT", + "EVENT_MEMORIZE_SPELL", + "EVENT_UNMEMORIZE_SPELL", + "EVENT_SCRIBE_SPELL", + "EVENT_UNSCRIBE_SPELL", // Add new events before these or Lua crashes "EVENT_SPELL_EFFECT_BOT", "EVENT_SPELL_EFFECT_BUFF_TIC_BOT" @@ -2160,6 +2164,19 @@ void PerlembParser::ExportEventVariables( break; } + case EVENT_MEMORIZE_SPELL: + case EVENT_UNMEMORIZE_SPELL: + case EVENT_SCRIBE_SPELL: + case EVENT_UNSCRIBE_SPELL: { + Seperator sep(data); + ExportVar(package_name.c_str(), "slot_id", sep.arg[0]); + ExportVar(package_name.c_str(), "spell_id", sep.arg[1]); + if (IsValidSpell(Strings::ToUnsignedInt(sep.arg[1]))) { + ExportVar(package_name.c_str(), "spell", "Spell", (void*)&spells[Strings::ToUnsignedInt(sep.arg[1])]); + } + break; + } + default: { break; } diff --git a/zone/event_codes.h b/zone/event_codes.h index df54ba231..4de42a46d 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -124,6 +124,10 @@ typedef enum { EVENT_ITEM_CLICK_CAST_CLIENT, EVENT_DESTROY_ITEM_CLIENT, EVENT_DROP_ITEM_CLIENT, + EVENT_MEMORIZE_SPELL, + EVENT_UNMEMORIZE_SPELL, + EVENT_SCRIBE_SPELL, + EVENT_UNSCRIBE_SPELL, // Add new events before these or Lua crashes EVENT_SPELL_EFFECT_BOT, EVENT_SPELL_EFFECT_BUFF_TIC_BOT, diff --git a/zone/gm_commands/unscribespell.cpp b/zone/gm_commands/unscribespell.cpp index 423288c3b..6965538c4 100755 --- a/zone/gm_commands/unscribespell.cpp +++ b/zone/gm_commands/unscribespell.cpp @@ -3,18 +3,18 @@ void command_unscribespell(Client *c, const Seperator *sep) { - int arguments = sep->argnum; + const auto arguments = sep->argnum; if (!arguments || !sep->IsNumber(1)) { c->Message(Chat::White, "Usage: #unscribespell [Spell ID] - Unscribe a spell from your or your target's spell book by Spell ID"); return; } - auto target = c; + auto t = c; if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - target = c->GetTarget()->CastToClient(); + t = c->GetTarget()->CastToClient(); } - uint16 spell_id = EQ::Clamp(Strings::ToInt(sep->arg[1]), 0, 65535); + const uint16 spell_id = EQ::Clamp(Strings::ToInt(sep->arg[1]), 0, 65535); if (!IsValidSpell(spell_id)) { c->Message( @@ -29,16 +29,16 @@ void command_unscribespell(Client *c, const Seperator *sep) auto spell_name = GetSpellName(spell_id); - if (target->HasSpellScribed(spell_id)) { - target->UnscribeSpellBySpellID(spell_id); + if (t->HasSpellScribed(spell_id)) { + t->UnscribeSpellBySpellID(spell_id); c->Message( Chat::White, fmt::format( - "Unscribing {} ({}) for {}.", + "Unscribing {} ({}) from {}.", spell_name, spell_id, - c->GetTargetDescription(target) + c->GetTargetDescription(t, TargetDescriptionType::LCSelf) ).c_str() ); } else { @@ -46,8 +46,8 @@ void command_unscribespell(Client *c, const Seperator *sep) Chat::White, fmt::format( "{} {} not have {} ({}) scribed.", - c->GetTargetDescription(target), - c == target ? "do" : "does", + c->GetTargetDescription(t, TargetDescriptionType::UCYou), + c == t ? "do" : "does", spell_name, spell_id ).c_str() diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 608409843..7f1f5fb18 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -5474,7 +5474,11 @@ luabind::scope lua_register_events() { luabind::value("item_click_client", static_cast(EVENT_ITEM_CLICK_CLIENT)), luabind::value("item_click_cast_client", static_cast(EVENT_ITEM_CLICK_CAST_CLIENT)), luabind::value("destroy_item_client", static_cast(EVENT_DESTROY_ITEM_CLIENT)), - luabind::value("drop_item_client", static_cast(EVENT_DROP_ITEM_CLIENT)) + luabind::value("drop_item_client", static_cast(EVENT_DROP_ITEM_CLIENT)), + luabind::value("memorize_spell", static_cast(EVENT_MEMORIZE_SPELL)), + luabind::value("unmemorize_spell", static_cast(EVENT_UNMEMORIZE_SPELL)), + luabind::value("scribe_spell", static_cast(EVENT_SCRIBE_SPELL)), + luabind::value("unscribe_spell", static_cast(EVENT_UNSCRIBE_SPELL)) )]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 01ccd1035..bcb8cf0e7 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -164,7 +164,11 @@ const char *LuaEvents[_LargestEventID] = { "event_item_click_client", "event_item_click_cast_client", "event_destroy_item_client", - "event_drop_item_client" + "event_drop_item_client", + "event_memorize_spell", + "event_unmemorize_spell", + "event_scribe_spell", + "event_unscribe_spell" }; extern Zone *zone; @@ -292,6 +296,10 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_DESTROY_ITEM_CLIENT] = handle_player_destroy_item; PlayerArgumentDispatch[EVENT_TARGET_CHANGE] = handle_player_target_change; PlayerArgumentDispatch[EVENT_DROP_ITEM_CLIENT] = handle_player_drop_item; + PlayerArgumentDispatch[EVENT_MEMORIZE_SPELL] = handle_player_memorize_scribe_spell; + PlayerArgumentDispatch[EVENT_UNMEMORIZE_SPELL] = handle_player_memorize_scribe_spell; + PlayerArgumentDispatch[EVENT_SCRIBE_SPELL] = handle_player_memorize_scribe_spell; + PlayerArgumentDispatch[EVENT_UNSCRIBE_SPELL] = handle_player_memorize_scribe_spell; 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 7a81f1f94..9954f4aff 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -1411,6 +1411,35 @@ void handle_player_target_change( } } +void handle_player_memorize_scribe_spell( + QuestInterface *parse, + lua_State* L, + Client* client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Seperator sep(data.c_str()); + + lua_pushnumber(L, Strings::ToUnsignedInt(sep.arg[0])); + lua_setfield(L, -2, "slot_id"); + + lua_pushnumber(L, Strings::ToUnsignedInt(sep.arg[1])); + lua_setfield(L, -2, "spell_id"); + + if (IsValidSpell(Strings::ToUnsignedInt(sep.arg[1]))) { + Lua_Spell l_spell(&spells[Strings::ToUnsignedInt(sep.arg[1])]); + luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell); + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); + } else { + Lua_Spell l_spell(nullptr); + luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell); + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); + } +} + // Item void handle_item_click( QuestInterface *parse, diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index a85dbffe5..b48fa2f03 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -734,6 +734,15 @@ void handle_player_drop_item( std::vector *extra_pointers ); +void handle_player_memorize_scribe_spell( + QuestInterface *parse, + lua_State* L, + Client* client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + // Item void handle_item_click( QuestInterface *parse, diff --git a/zone/spells.cpp b/zone/spells.cpp index 408a2c6a8..209ae2133 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5521,13 +5521,14 @@ void Client::UnmemSpell(int slot, bool update_client) } LogSpells("Spell [{}] forgotten from slot [{}]", m_pp.mem_spells[slot], slot); - m_pp.mem_spells[slot] = 0xFFFFFFFF; database.DeleteCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot); - if(update_client) { + if (update_client) { MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); } + + m_pp.mem_spells[slot] = UINT32_MAX; } void Client::UnmemSpellBySpellID(int32 spell_id) @@ -5599,7 +5600,7 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client, bool def } if (update_client) { - if (m_pp.spell_book[slot] != 0xFFFFFFFF) { + if (m_pp.spell_book[slot] != UINT32_MAX) { UnscribeSpell(slot, update_client, defer_save); } } @@ -5619,25 +5620,33 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client, bool def void Client::UnscribeSpell(int slot, bool update_client, bool defer_save) { - if (slot >= EQ::spells::SPELLBOOK_SIZE || slot < 0) { + if (!EQ::ValueWithin(slot, 0, (EQ::spells::SPELLBOOK_SIZE - 1))) { return; } LogSpells("Spell [{}] erased from spell book slot [{}]", m_pp.spell_book[slot], slot); - m_pp.spell_book[slot] = 0xFFFFFFFF; if (!defer_save) { database.DeleteCharacterSpell(CharacterID(), m_pp.spell_book[slot], slot); } if (update_client && slot < EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) { - auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); - DeleteSpell_Struct *del = (DeleteSpell_Struct *) outapp->pBuffer; + auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); + auto* del = (DeleteSpell_Struct *) outapp->pBuffer; + del->spell_slot = slot; - del->success = 1; + del->success = 1; + QueuePacket(outapp); safe_delete(outapp); } + + if (parse->PlayerHasQuestSub(EVENT_UNSCRIBE_SPELL)) { + const auto export_string = fmt::format("{} {}", slot, m_pp.spell_book[slot]); + parse->EventPlayer(EVENT_UNSCRIBE_SPELL, this, export_string, 0); + } + + m_pp.spell_book[slot] = UINT32_MAX; } void Client::UnscribeSpellAll(bool update_client)