[Quest API] Export corpse in EVENT_DEATH_COMPLETE (#2519)

It wasn't possible to easily obtain the corpse from post-death events
because the killed entity id is assigned to the corpse and reset to 0
on the entity before the events are dispatched.

This exposes the killed entity's corpse to EVENT_DEATH_COMPLETE and
EVENT_DEATH_ZONE. Lua exports a Corpse object and perl exports a corpse
entity id.

The purpose of this is to make it easier to add items dynamically on
death. Ideally this would be done in EVENT_DEATH before the corpse is
made, but there's currently some combat system bugs that make that event
unusable since it can be dispatched multiple times.

A follow up will provide an api to reset corpse decay times since adding
items after corpse creation will require quests to manually reset the
decay timer in case the corpse had zero items.
This commit is contained in:
hg 2022-11-06 10:36:57 -05:00 committed by GitHub
parent 13b2af1a91
commit 69e90c1739
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 9 deletions

View File

@ -1903,6 +1903,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
}
bool LeftCorpse = false;
Corpse* new_corpse = nullptr;
// now we apply the exp loss, unmem their spells, and make a corpse
// unless they're a GM (or less than lvl 10
@ -1938,7 +1939,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
if ((RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) || RuleB(Character, LeaveNakedCorpses))
{
// creating the corpse takes the cash/items off the player too
auto new_corpse = new Corpse(this, exploss);
new_corpse = new Corpse(this, exploss);
std::string tmp;
database.GetVariable("ServerType", tmp);
@ -2038,7 +2039,8 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
QServ->PlayerLogEvent(Player_Log_Deaths, CharacterID(), event_desc);
}
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0);
std::vector<std::any> args = { new_corpse };
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0, &args);
return true;
}
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
@ -2604,6 +2606,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
bool allow_merchant_corpse = RuleB(Merchant, AllowCorpse);
bool is_merchant = (class_ == MERCHANT || class_ == ADVENTURE_MERCHANT || MerchantType != 0);
Corpse* corpse = nullptr;
if (!HasOwner() && !IsMerc() && !GetSwarmInfo() && (!is_merchant || allow_merchant_corpse) &&
((killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) ||
(killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient())))
@ -2620,7 +2624,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.RemoveFromAutoXTargets(this);
uint32 emoteid = GetEmoteID();
auto corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata,
corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata,
level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS)
: RuleI(NPC, MinorNPCCorpseDecayTimeMS));
entity_list.LimitRemoveNPC(this);
@ -2737,11 +2741,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.UpdateFindableNPCState(this, true);
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0);
std::vector<std::any> args = { corpse };
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
combat_record.Stop();
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
std::vector<std::any> args = { this };
args.push_back(this);
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args);
return true;

View File

@ -1611,9 +1611,17 @@ void PerlembParser::ExportEventVariables(
ExportVar(package_name.c_str(), "killer_damage", sep.arg[1]);
ExportVar(package_name.c_str(), "killer_spell", sep.arg[2]);
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
if (extra_pointers && !extra_pointers->empty())
if (extra_pointers && extra_pointers->size() >= 1)
{
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(0));
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
if (corpse)
{
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
}
}
if (extra_pointers && extra_pointers->size() >= 2)
{
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
if (killed)
{
ExportVar(package_name.c_str(), "killed_npc_id", killed->GetNPCTypeID());

View File

@ -210,9 +210,17 @@ void handle_npc_death(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init,
lua_pushinteger(L, std::stoi(sep.arg[3]));
lua_setfield(L, -2, "skill_id");
if (extra_pointers && !extra_pointers->empty())
if (extra_pointers && extra_pointers->size() >= 1)
{
Lua_NPC l_npc(std::any_cast<NPC*>(extra_pointers->at(0)));
Lua_Corpse l_corpse(std::any_cast<Corpse*>(extra_pointers->at(0)));
luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse);
l_corpse_o.push(L);
lua_setfield(L, -2, "corpse");
}
if (extra_pointers && extra_pointers->size() >= 2)
{
Lua_NPC l_npc(std::any_cast<NPC*>(extra_pointers->at(1)));
luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc);
l_npc_o.push(L);
lua_setfield(L, -2, "killed");