[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
};
enum ScribeSpellActions
{
Scribe,
Memorize,
Unmemorize
};
#endif /*COMMON_EQ_CONSTANTS_H*/

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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()

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_cast_client", static_cast<int>(EVENT_ITEM_CLICK_CAST_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_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;

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
void handle_item_click(
QuestInterface *parse,

View File

@ -734,6 +734,15 @@ void handle_player_drop_item(
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
void handle_item_click(
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);
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)