[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
This commit is contained in:
Alex King 2023-05-25 19:18:14 -04:00 committed by GitHub
parent 67fdc75df3
commit 50db7637aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 161 additions and 28 deletions

View File

@ -1045,4 +1045,11 @@ enum ResurrectionActions
Accept Accept
}; };
enum ScribeSpellActions
{
Scribe,
Memorize,
Unmemorize
};
#endif /*COMMON_EQ_CONSTANTS_H*/ #endif /*COMMON_EQ_CONSTANTS_H*/

View File

@ -2783,18 +2783,64 @@ void Client::GMKill() {
safe_delete(outapp); safe_delete(outapp);
} }
void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 reduction){ void Client::MemorizeSpell(uint32 slot, uint32 spell_id, uint32 scribing, uint32 reduction){
if (slot < 0 || slot >= EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) if (
!EQ::ValueWithin(
slot,
0,
(EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize - 1)
)
) {
return; 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; return;
}
auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct)); auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct));
MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer;
mss->scribing=scribing; auto* mss = (MemorizeSpell_Struct*) outapp->pBuffer;
mss->slot=slot;
mss->spell_id=spellid; mss->scribing = scribing;
mss->slot = slot;
mss->spell_id = spell_id;
mss->reduction = reduction; mss->reduction = reduction;
outapp->priority = 5; 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); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }

View File

@ -932,7 +932,7 @@ public:
inline uint32 GetAAPercent() const { return m_epp.perAA; } inline uint32 GetAAPercent() const { return m_epp.perAA; }
void SetAATitle(std::string title); void SetAATitle(std::string title);
void SetTitleSuffix(std::string suffix); 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 // Item methods
void UseAugmentContainer(int container_slot); void UseAugmentContainer(int container_slot);

View File

@ -179,6 +179,10 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_ITEM_CLICK_CAST_CLIENT", "EVENT_ITEM_CLICK_CAST_CLIENT",
"EVENT_DESTROY_ITEM_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",
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT", "EVENT_SPELL_EFFECT_BOT",
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT" "EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
@ -2160,6 +2164,19 @@ void PerlembParser::ExportEventVariables(
break; 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: { default: {
break; break;
} }

View File

@ -124,6 +124,10 @@ typedef enum {
EVENT_ITEM_CLICK_CAST_CLIENT, EVENT_ITEM_CLICK_CAST_CLIENT,
EVENT_DESTROY_ITEM_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,
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT, EVENT_SPELL_EFFECT_BOT,
EVENT_SPELL_EFFECT_BUFF_TIC_BOT, EVENT_SPELL_EFFECT_BUFF_TIC_BOT,

View File

@ -3,18 +3,18 @@
void command_unscribespell(Client *c, const Seperator *sep) void command_unscribespell(Client *c, const Seperator *sep)
{ {
int arguments = sep->argnum; const auto arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) { 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"); c->Message(Chat::White, "Usage: #unscribespell [Spell ID] - Unscribe a spell from your or your target's spell book by Spell ID");
return; return;
} }
auto target = c; auto t = c;
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { 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)) { if (!IsValidSpell(spell_id)) {
c->Message( c->Message(
@ -29,16 +29,16 @@ void command_unscribespell(Client *c, const Seperator *sep)
auto spell_name = GetSpellName(spell_id); auto spell_name = GetSpellName(spell_id);
if (target->HasSpellScribed(spell_id)) { if (t->HasSpellScribed(spell_id)) {
target->UnscribeSpellBySpellID(spell_id); t->UnscribeSpellBySpellID(spell_id);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Unscribing {} ({}) for {}.", "Unscribing {} ({}) from {}.",
spell_name, spell_name,
spell_id, spell_id,
c->GetTargetDescription(target) c->GetTargetDescription(t, TargetDescriptionType::LCSelf)
).c_str() ).c_str()
); );
} else { } else {
@ -46,8 +46,8 @@ void command_unscribespell(Client *c, const Seperator *sep)
Chat::White, Chat::White,
fmt::format( fmt::format(
"{} {} not have {} ({}) scribed.", "{} {} not have {} ({}) scribed.",
c->GetTargetDescription(target), c->GetTargetDescription(t, TargetDescriptionType::UCYou),
c == target ? "do" : "does", c == t ? "do" : "does",
spell_name, spell_name,
spell_id spell_id
).c_str() ).c_str()

View File

@ -5474,7 +5474,11 @@ luabind::scope lua_register_events() {
luabind::value("item_click_client", static_cast<int>(EVENT_ITEM_CLICK_CLIENT)), luabind::value("item_click_client", static_cast<int>(EVENT_ITEM_CLICK_CLIENT)),
luabind::value("item_click_cast_client", static_cast<int>(EVENT_ITEM_CLICK_CAST_CLIENT)), luabind::value("item_click_cast_client", static_cast<int>(EVENT_ITEM_CLICK_CAST_CLIENT)),
luabind::value("destroy_item_client", static_cast<int>(EVENT_DESTROY_ITEM_CLIENT)), luabind::value("destroy_item_client", static_cast<int>(EVENT_DESTROY_ITEM_CLIENT)),
luabind::value("drop_item_client", static_cast<int>(EVENT_DROP_ITEM_CLIENT)) luabind::value("drop_item_client", static_cast<int>(EVENT_DROP_ITEM_CLIENT)),
luabind::value("memorize_spell", static_cast<int>(EVENT_MEMORIZE_SPELL)),
luabind::value("unmemorize_spell", static_cast<int>(EVENT_UNMEMORIZE_SPELL)),
luabind::value("scribe_spell", static_cast<int>(EVENT_SCRIBE_SPELL)),
luabind::value("unscribe_spell", static_cast<int>(EVENT_UNSCRIBE_SPELL))
)]; )];
} }

View File

@ -164,7 +164,11 @@ const char *LuaEvents[_LargestEventID] = {
"event_item_click_client", "event_item_click_client",
"event_item_click_cast_client", "event_item_click_cast_client",
"event_destroy_item_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; extern Zone *zone;
@ -292,6 +296,10 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_DESTROY_ITEM_CLIENT] = handle_player_destroy_item; PlayerArgumentDispatch[EVENT_DESTROY_ITEM_CLIENT] = handle_player_destroy_item;
PlayerArgumentDispatch[EVENT_TARGET_CHANGE] = handle_player_target_change; PlayerArgumentDispatch[EVENT_TARGET_CHANGE] = handle_player_target_change;
PlayerArgumentDispatch[EVENT_DROP_ITEM_CLIENT] = handle_player_drop_item; 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] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;

View File

@ -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<std::any> *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 // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,

View File

@ -734,6 +734,15 @@ void handle_player_drop_item(
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
void handle_player_memorize_scribe_spell(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
// Item // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,

View File

@ -5521,13 +5521,14 @@ void Client::UnmemSpell(int slot, bool update_client)
} }
LogSpells("Spell [{}] forgotten from slot [{}]", m_pp.mem_spells[slot], slot); 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); database.DeleteCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot);
if(update_client) { if (update_client) {
MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget);
} }
m_pp.mem_spells[slot] = UINT32_MAX;
} }
void Client::UnmemSpellBySpellID(int32 spell_id) 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 (update_client) {
if (m_pp.spell_book[slot] != 0xFFFFFFFF) { if (m_pp.spell_book[slot] != UINT32_MAX) {
UnscribeSpell(slot, update_client, defer_save); 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) 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; return;
} }
LogSpells("Spell [{}] erased from spell book slot [{}]", m_pp.spell_book[slot], slot); LogSpells("Spell [{}] erased from spell book slot [{}]", m_pp.spell_book[slot], slot);
m_pp.spell_book[slot] = 0xFFFFFFFF;
if (!defer_save) { if (!defer_save) {
database.DeleteCharacterSpell(CharacterID(), m_pp.spell_book[slot], slot); database.DeleteCharacterSpell(CharacterID(), m_pp.spell_book[slot], slot);
} }
if (update_client && slot < EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) { if (update_client && slot < EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) {
auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct));
DeleteSpell_Struct *del = (DeleteSpell_Struct *) outapp->pBuffer; auto* del = (DeleteSpell_Struct *) outapp->pBuffer;
del->spell_slot = slot; del->spell_slot = slot;
del->success = 1; del->success = 1;
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(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) void Client::UnscribeSpellAll(bool update_client)