[Quest API] Add Scripting Support to Mercenaries (#4500)

* [Quest API] Add Scripting Support to Mercenaries

* Cleanup

* Cleanup

* Update lua_merc.h

* Update mob.cpp

* XYZH

* Final

* Update attack.cpp

* Update attack.cpp

* Simplify event invocation

* Inline example

* Nullptr init example

* EVENT_TIMER simplify add EventPlayerNpcBotMerc

* EVENT_TIMER_START

* Remove has_start_event

* EVENT_TIMER_START with settimerMS

* EVENT_POPUP_RESPONSE

* Consolidation

* Update attack.cpp

* Push

* Update quest_parser_collection.h

* Comments

* Cleanup per comments

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Alex King 2024-10-10 21:29:29 -04:00 committed by GitHub
parent 8f86cb353e
commit 448a33a60c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1715 additions and 758 deletions

View File

@ -65,6 +65,7 @@ SET(zone_sources
lua_inventory.cpp lua_inventory.cpp
lua_item.cpp lua_item.cpp
lua_iteminst.cpp lua_iteminst.cpp
lua_merc.cpp
lua_mob.cpp lua_mob.cpp
lua_mod.cpp lua_mod.cpp
lua_npc.cpp lua_npc.cpp
@ -115,6 +116,7 @@ SET(zone_sources
perl_groups.cpp perl_groups.cpp
perl_hateentry.cpp perl_hateentry.cpp
perl_inventory.cpp perl_inventory.cpp
perl_merc.cpp
perl_mob.cpp perl_mob.cpp
perl_npc.cpp perl_npc.cpp
perl_object.cpp perl_object.cpp
@ -224,6 +226,7 @@ SET(zone_headers
lua_inventory.h lua_inventory.h
lua_item.h lua_item.h
lua_iteminst.h lua_iteminst.h
lua_merc.h
lua_mob.h lua_mob.h
lua_mod.h lua_mod.h
lua_npc.h lua_npc.h

View File

@ -1550,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
hit.damage_done = 0; hit.damage_done = 0;
} }
if (IsBot()) { parse->EventBotMerc(
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) { EVENT_USE_SKILL,
const auto& export_string = fmt::format( this,
nullptr,
[&]() {
return fmt::format(
"{} {}", "{} {}",
hit.skill, hit.skill,
GetSkill(hit.skill) GetSkill(hit.skill)
); );
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
} }
} );
} }
} }
@ -1922,11 +1923,9 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
} }
if (killer_mob) { if (killer_mob) {
if (killer_mob->IsNPC()) { parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this);
if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) {
parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0);
}
if (killer_mob->IsNPC()) {
killed_by = KilledByTypes::Killed_NPC; killed_by = KilledByTypes::Killed_NPC;
auto emote_id = killer_mob->GetEmoteID(); auto emote_id = killer_mob->GetEmoteID();
@ -1934,12 +1933,6 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
} }
killer_mob->TrySpellOnKill(killed_level, spell);
} else if (killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_SLAY)) {
parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@ -2458,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
spell_id = SPELL_UNKNOWN; spell_id = SPELL_UNKNOWN;
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if (attacked_timer.Check()) if (attacked_timer.Check()) {
{ parse->EventMercNPC(EVENT_ATTACK, this, other);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) {
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
}
} }
attacked_timer.Start(CombatEventTimer_expire); attacked_timer.Start(CombatEventTimer_expire);
if (!IsEngaged()) if (!IsEngaged())
@ -2506,41 +2495,22 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
if (IsNPC()) { auto exports = [&]() {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) { return fmt::format(
const auto& export_string = fmt::format( "{} {} {} {}",
"{} {} {} {}", killer_mob ? killer_mob->GetID() : 0,
killer_mob ? killer_mob->GetID() : 0, damage,
damage, spell,
spell, static_cast<int>(attack_skill)
static_cast<int>(attack_skill) );
); };
if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) { if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
}
return false;
}
} }
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_DEATH)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
return false; return false;
}
}
} }
if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
@ -3077,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (killer_mob && killer_mob->IsBot()) { if (killer_mob) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) { parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this);
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@ -3108,24 +3076,29 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) { std::vector<std::any> args = { corpse };
const auto& export_string = fmt::format(
"{} {} {} {} {} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill),
entity_id,
m_combat_record.GetStartTime(),
m_combat_record.GetEndTime(),
m_combat_record.GetDamageReceived(),
m_combat_record.GetHealingReceived()
);
std::vector<std::any> args = { corpse }; parse->EventMercNPC(
EVENT_DEATH_COMPLETE,
parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args); this,
} owner_or_self,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill),
entity_id,
m_combat_record.GetStartTime(),
m_combat_record.GetEndTime(),
m_combat_record.GetDamageReceived(),
m_combat_record.GetHealingReceived()
);
},
0,
&args
);
// Zone controller process EVENT_DEATH_ZONE (Death events) // Zone controller process EVENT_DEATH_ZONE (Death events)
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
@ -4285,100 +4258,60 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//final damage has been determined. //final damage has been determined.
int old_hp_ratio = (int)GetHPRatio(); int old_hp_ratio = (int)GetHPRatio();
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_npc_given_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
)
);
const auto has_npc_taken_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
)
);
const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_given_event = (
has_bot_given_event ||
has_npc_given_event ||
has_player_given_event
);
const auto has_taken_event = (
has_bot_taken_event ||
has_npc_taken_event ||
has_player_taken_event
);
std::vector<std::any> args; std::vector<std::any> args;
int64 damage_override = 0; int64 damage_override = 0;
if (has_given_event && attacker) { if (attacker) {
const auto export_string = fmt::format( args = { this };
"{} {} {} {} {} {} {} {} {}",
GetID(),
damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
if (attacker->IsBot() && has_bot_given_event) { parse->EventMob(
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0); EVENT_DAMAGE_GIVEN,
} else if (attacker->IsClient() && has_player_given_event) { attacker,
args.push_back(this); this,
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args); [&]() {
} else if (attacker->IsNPC() && has_npc_given_event) { return fmt::format(
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0); "{} {} {} {} {} {} {} {} {}",
} GetID(),
damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
},
0,
&args
);
} }
if (has_taken_event) { args = { attacker };
const auto export_string = fmt::format(
"{} {} {} {} {} {} {} {} {}",
attacker ? attacker->GetID() : 0,
damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
if (IsBot() && has_bot_taken_event) { damage_override = parse->EventMob(
damage_override = parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0); EVENT_DAMAGE_TAKEN,
} else if (IsClient() && has_player_taken_event) { this,
args.push_back(attacker ? attacker : nullptr); attacker,
damage_override = parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args); [&]() {
} else if (IsNPC() && has_npc_taken_event) { return fmt::format(
damage_override = parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0); "{} {} {} {} {} {} {} {} {}",
} attacker ? attacker->GetID() : 0,
} damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
},
0,
&args
);
if (damage_override > 0) { if (damage_override > 0) {
damage = damage_override; damage = damage_override;

View File

@ -1243,45 +1243,28 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
entity_list.ProcessProximitySay(message, this, language); entity_list.ProcessProximitySay(message, this, language);
} }
Mob* t = GetTarget();
if ( if (
GetTarget() && t &&
GetTarget()->IsNPC() && !IsInvisible(t) &&
!IsInvisible(GetTarget()) DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)
) { ) {
auto* t = GetTarget()->CastToNPC(); const bool is_engaged = t->IsEngaged();
if (!t->IsEngaged()) {
CheckLDoNHail(t);
CheckEmoteHail(t, message);
if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { if (is_engaged) {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) { parse->EventBotMercNPC(EVENT_AGGRO_SAY, t, this, [&]() { return message; }, language);
parse->EventNPC(EVENT_SAY, t, this, message, language);
}
if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(t)) {
t->DoQuestPause(this);
}
}
}
} else { } else {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) { parse->EventBotMercNPC(EVENT_SAY, t, this, [&]() { return message; }, language);
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
}
}
} }
} if (t->IsNPC() && !is_engaged) {
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) { CheckLDoNHail(t->CastToNPC());
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { CheckEmoteHail(t->CastToNPC(), message);
if (GetTarget()->IsEngaged()) {
if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language); if (UpdateTasksOnSpeakWith(t->CastToNPC())) {
} t->CastToNPC()->DoQuestPause(this);
} else {
if (parse->BotHasQuestSub(EVENT_SAY)) {
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
} }
} }
} }

View File

@ -11984,15 +11984,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
auto t = GetTarget(); auto t = GetTarget();
if (t) { if (t) {
if (t->IsNPC()) { parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); });
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_POPUP_RESPONSE)) {
parse->EventNPC(EVENT_POPUP_RESPONSE, t->CastToNPC(), this, std::to_string(popup_response->popupid), 0);
}
} else if (t->IsBot()) {
if (parse->BotHasQuestSub(EVENT_POPUP_RESPONSE)) {
parse->EventBot(EVENT_POPUP_RESPONSE, t->CastToBot(), this, std::to_string(popup_response->popupid), 0);
}
}
} }
} }

View File

@ -57,6 +57,7 @@ void perl_register_expedition();
void perl_register_expedition_lock_messages(); void perl_register_expedition_lock_messages();
void perl_register_bot(); void perl_register_bot();
void perl_register_buff(); void perl_register_buff();
void perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
#endif // EMBPERL_XS #endif // EMBPERL_XS
@ -217,6 +218,8 @@ PerlembParser::PerlembParser() : perl(nullptr)
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
} }
PerlembParser::~PerlembParser() PerlembParser::~PerlembParser()
@ -258,6 +261,8 @@ void PerlembParser::ReloadQuests()
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
item_quest_status_.clear(); item_quest_status_.clear();
spell_quest_status_.clear(); spell_quest_status_.clear();
@ -285,6 +290,8 @@ int PerlembParser::EventCommon(
bool is_global_npc_quest = false; bool is_global_npc_quest = false;
bool is_bot_quest = false; bool is_bot_quest = false;
bool is_global_bot_quest = false; bool is_global_bot_quest = false;
bool is_merc_quest = false;
bool is_global_merc_quest = false;
bool is_item_quest = false; bool is_item_quest = false;
bool is_spell_quest = false; bool is_spell_quest = false;
@ -295,6 +302,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@ -310,6 +319,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@ -339,6 +350,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@ -356,6 +369,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@ -382,7 +397,7 @@ int PerlembParser::EventCommon(
if (is_player_quest || is_global_player_quest) { if (is_player_quest || is_global_player_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr);
} else if (is_bot_quest || is_global_bot_quest) { } else if (is_bot_quest || is_global_bot_quest || is_merc_quest || is_global_merc_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr);
} else if (is_item_quest) { } else if (is_item_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr);
@ -1009,41 +1024,22 @@ int PerlembParser::SendCommands(
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
dTHX; dTHX;
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
if (clear_vars_.find(cl) != clear_vars_.end()) { for (const auto& suffix : suffixes) {
auto e = fmt::format("{} = undef;", cl); const std::string& key = fmt::format("${}::{}", prefix, suffix);
perl->eval(e.c_str()); if (clear_vars_.find(suffix) != clear_vars_.end()) {
} auto e = fmt::format("{} = undef;", key);
perl->eval(e.c_str());
if (clear_vars_.find(np) != clear_vars_.end()) { }
auto e = fmt::format("{} = undef;", np);
perl->eval(e.c_str());
}
if (clear_vars_.find(qi) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", qi);
perl->eval(e.c_str());
}
if (clear_vars_.find(sp) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", sp);
perl->eval(e.c_str());
}
if (clear_vars_.find(enl) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", enl);
perl->eval(e.c_str());
}
if (clear_vars_.find(bot) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", bot);
perl->eval(e.c_str());
} }
} }
@ -1060,19 +1056,21 @@ int PerlembParser::SendCommands(
sv_setsv(client, _empty_sv); sv_setsv(client, _empty_sv);
} }
//only export NPC if it's a npc quest if (other->IsBot()) {
if (!other->IsClient() && other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
}
if (!other->IsClient() && other->IsBot()) {
Bot* b = quest_manager.GetBot(); Bot* b = quest_manager.GetBot();
buf = fmt::format("{}::bot", prefix); buf = fmt::format("{}::bot", prefix);
SV* bot = get_sv(buf.c_str(), true); SV* bot = get_sv(buf.c_str(), true);
sv_setref_pv(bot, "Bot", b); sv_setref_pv(bot, "Bot", b);
} else if (other->IsMerc()) {
Merc* m = quest_manager.GetMerc();
buf = fmt::format("{}::merc", prefix);
SV* merc = get_sv(buf.c_str(), true);
sv_setref_pv(merc, "Merc", m);
} else if (other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
} }
//only export QuestItem if it's an inst quest //only export QuestItem if it's an inst quest
@ -1098,23 +1096,25 @@ int PerlembParser::SendCommands(
#endif #endif
//now call the requested sub //now call the requested sub
ret_value = perl->dosub(std::string(prefix).append("::").append(event_id).c_str()); const std::string& sub_key = fmt::format("{}::{}", prefix, event_id);
ret_value = perl->dosub(sub_key.c_str());
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
clear_vars_[cl] = 1; for (const auto& suffix : suffixes) {
clear_vars_[np] = 1; const std::string& key = fmt::format("${}::{}", prefix, suffix);
clear_vars_[qi] = 1; clear_vars_[key] = 1;
clear_vars_[sp] = 1; }
clear_vars_[enl] = 1;
clear_vars_[bot] = 1;
} }
#endif #endif
@ -1184,6 +1184,7 @@ void PerlembParser::MapFunctions()
perl_register_expedition_lock_messages(); perl_register_expedition_lock_messages();
perl_register_bot(); perl_register_bot();
perl_register_buff(); perl_register_buff();
perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
} }
@ -1192,6 +1193,8 @@ void PerlembParser::GetQuestTypes(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@ -1219,10 +1222,14 @@ void PerlembParser::GetQuestTypes(
if (is_global) { if (is_global) {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_global_bot_quest = true; is_global_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_global_merc_quest = true;
} }
} else { } else {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_bot_quest = true; is_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_merc_quest = true;
} }
} }
} else { } else {
@ -1251,6 +1258,8 @@ void PerlembParser::GetQuestPackageName(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@ -1268,6 +1277,8 @@ void PerlembParser::GetQuestPackageName(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@ -1291,6 +1302,10 @@ void PerlembParser::GetQuestPackageName(
package_name = "qst_bot"; package_name = "qst_bot";
} else if (is_global_bot_quest) { } else if (is_global_bot_quest) {
package_name = "qst_global_bot"; package_name = "qst_global_bot";
} else if (is_merc_quest) {
package_name = "qst_merc";
} else if (is_global_merc_quest) {
package_name = "qst_global_merc";
} else { } else {
package_name = fmt::format("qst_spell_{}", object_id); package_name = fmt::format("qst_spell_{}", object_id);
} }
@ -1316,6 +1331,8 @@ void PerlembParser::ExportQGlobals(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@ -1331,6 +1348,8 @@ void PerlembParser::ExportQGlobals(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@ -1466,6 +1485,8 @@ void PerlembParser::ExportMobVariables(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@ -1491,6 +1512,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest !is_item_quest
) { ) {
if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) { if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) {
@ -1521,6 +1544,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@ -2081,7 +2106,8 @@ void PerlembParser::ExportEventVariables(
"killed_bot_id", "killed_bot_id",
killed->IsBot() ? killed->CastToBot()->GetBotID() : 0 killed->IsBot() ? killed->CastToBot()->GetBotID() : 0
); );
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "killed_merc_id", killed->IsMerc() ? killed->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "killed_npc_id", !killed->IsMerc() && killed->IsNPC() ? killed->GetNPCTypeID() : 0);
} }
} }
break; break;
@ -2357,6 +2383,7 @@ void PerlembParser::ExportEventVariables(
case EVENT_DESPAWN: { case EVENT_DESPAWN: {
ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID()); ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID());
ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0); ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0);
ExportVar(package_name.c_str(), "despawned_merc_id", npc_mob->IsMerc() ? npc_mob->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0);
break; break;
} }
@ -2637,4 +2664,124 @@ int PerlembParser::EventGlobalBot(
); );
} }
void PerlembParser::LoadMercScript(std::string filename)
{
if (!perl || merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Merc Quest File [{}] Error [{}]",
filename,
e
)
);
merc_quest_status_ = questFailedToLoad;
return;
}
merc_quest_status_ = questLoaded;
}
void PerlembParser::LoadGlobalMercScript(std::string filename)
{
if (!perl || global_merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_global_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Global Merc Quest File [{}] Error [{}]",
filename,
e
)
);
global_merc_quest_status_ = questFailedToLoad;
return;
}
global_merc_quest_status_ = questLoaded;
}
bool PerlembParser::MercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return perl->SubExists("qst_merc", QuestEventSubroutines[event_id]);
}
bool PerlembParser::GlobalMercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
global_merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return (perl->SubExists("qst_global_merc", QuestEventSubroutines[event_id]));
}
int PerlembParser::EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
false,
extra_pointers
);
}
int PerlembParser::EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
true,
extra_pointers
);
}
#endif #endif

View File

@ -118,6 +118,24 @@ public:
std::vector<std::any>* extra_pointers std::vector<std::any>* extra_pointers
); );
virtual int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id); virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id);
virtual bool HasGlobalQuestSub(QuestEventID event_id); virtual bool HasGlobalQuestSub(QuestEventID event_id);
virtual bool PlayerHasQuestSub(QuestEventID event_id); virtual bool PlayerHasQuestSub(QuestEventID event_id);
@ -126,6 +144,8 @@ public:
virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
virtual bool BotHasQuestSub(QuestEventID event_id); virtual bool BotHasQuestSub(QuestEventID event_id);
virtual bool GlobalBotHasQuestSub(QuestEventID event_id); virtual bool GlobalBotHasQuestSub(QuestEventID event_id);
virtual bool MercHasQuestSub(QuestEventID event_id);
virtual bool GlobalMercHasQuestSub(QuestEventID event_id);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@ -135,6 +155,8 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadSpellScript(std::string filename, uint32 spell_id);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@ -182,6 +204,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@ -197,6 +221,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@ -216,6 +242,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@ -230,6 +258,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@ -263,6 +293,8 @@ private:
PerlQuestStatus global_player_quest_status_; PerlQuestStatus global_player_quest_status_;
PerlQuestStatus bot_quest_status_; PerlQuestStatus bot_quest_status_;
PerlQuestStatus global_bot_quest_status_; PerlQuestStatus global_bot_quest_status_;
PerlQuestStatus merc_quest_status_;
PerlQuestStatus global_merc_quest_status_;
SV* _empty_sv; SV* _empty_sv;

View File

@ -776,6 +776,10 @@ void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc)); merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc));
mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc)); mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc));
if (parse->MercHasQuestSub(EVENT_SPAWN)) {
parse->EventMerc(EVENT_SPAWN, merc, nullptr, "", 0);
}
} }
} }

View File

@ -12,6 +12,7 @@
#include "lua_door.h" #include "lua_door.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
bool Lua_Entity::IsClient() { bool Lua_Entity::IsClient() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
@ -140,6 +141,12 @@ Lua_Bot Lua_Entity::CastToBot() {
return Lua_Bot(b); return Lua_Bot(b);
} }
Lua_Merc Lua_Entity::CastToMerc() {
void *d = GetLuaPtrData();
Merc *m = reinterpret_cast<Merc*>(d);
return Lua_Merc(m);
}
luabind::scope lua_register_entity() { luabind::scope lua_register_entity() {
return luabind::class_<Lua_Entity>("Entity") return luabind::class_<Lua_Entity>("Entity")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@ -149,6 +156,7 @@ luabind::scope lua_register_entity() {
.def("CastToClient", &Lua_Entity::CastToClient) .def("CastToClient", &Lua_Entity::CastToClient)
.def("CastToCorpse", &Lua_Entity::CastToCorpse) .def("CastToCorpse", &Lua_Entity::CastToCorpse)
.def("CastToDoor", &Lua_Entity::CastToDoor) .def("CastToDoor", &Lua_Entity::CastToDoor)
.def("CastToMerc", &Lua_Entity::CastToMerc)
.def("CastToMob", &Lua_Entity::CastToMob) .def("CastToMob", &Lua_Entity::CastToMob)
.def("CastToNPC", &Lua_Entity::CastToNPC) .def("CastToNPC", &Lua_Entity::CastToNPC)
.def("CastToObject", &Lua_Entity::CastToObject) .def("CastToObject", &Lua_Entity::CastToObject)

View File

@ -7,6 +7,7 @@
class Entity; class Entity;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Mob; class Lua_Mob;
struct Lua_HateList; struct Lua_HateList;
@ -59,6 +60,7 @@ public:
Lua_Object CastToObject(); Lua_Object CastToObject();
Lua_Door CastToDoor(); Lua_Door CastToDoor();
Lua_Bot CastToBot(); Lua_Bot CastToBot();
Lua_Merc CastToMerc();
}; };
#endif #endif

View File

@ -16,6 +16,7 @@
#include "lua_spawn.h" #include "lua_spawn.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
struct Lua_Mob_List { struct Lua_Mob_List {
std::vector<Lua_Mob> entries; std::vector<Lua_Mob> entries;

View File

@ -8,6 +8,7 @@ class EntityList;
class Lua_Mob; class Lua_Mob;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Door; class Lua_Door;
class Lua_Corpse; class Lua_Corpse;

229
zone/lua_merc.cpp Normal file
View File

@ -0,0 +1,229 @@
#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include "merc.h"
#include "lua_client.h"
#include "lua_merc.h"
#include "lua_group.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
uint32 Lua_Merc::GetCostFormula()
{
Lua_Safe_Call_Int();
return self->GetCostFormula();
}
Lua_Group Lua_Merc::GetGroup()
{
Lua_Safe_Call_Class(Lua_Group);
return self->GetGroup();
}
int Lua_Merc::GetHatedCount()
{
Lua_Safe_Call_Int();
return self->GetHatedCount();
}
float Lua_Merc::GetMaxMeleeRangeToTarget(Lua_Mob target)
{
Lua_Safe_Call_Real();
return self->GetMaxMeleeRangeToTarget(target);
}
uint32 Lua_Merc::GetMercenaryCharacterID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryCharacterID();
}
uint32 Lua_Merc::GetMercenaryID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryID();
}
uint32 Lua_Merc::GetMercenaryNameType()
{
Lua_Safe_Call_Int();
return self->GetMercNameType();
}
Lua_Client Lua_Merc::GetMercenaryOwner()
{
Lua_Safe_Call_Class(Lua_Client);
return Lua_Client(self->GetMercenaryOwner());
}
uint32 Lua_Merc::GetMercenarySubtype()
{
Lua_Safe_Call_Int();
return self->GetMercenarySubType();
}
uint32 Lua_Merc::GetMercenaryTemplateID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryTemplateID();
}
uint32 Lua_Merc::GetMercenaryType()
{
Lua_Safe_Call_Int();
return self->GetMercenaryType();
}
Lua_Mob Lua_Merc::GetOwner()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwner());
}
Lua_Mob Lua_Merc::GetOwnerOrSelf()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwnerOrSelf());
}
uint8 Lua_Merc::GetProficiencyID()
{
Lua_Safe_Call_Int();
return self->GetProficiencyID();
}
uint8 Lua_Merc::GetStance()
{
Lua_Safe_Call_Int();
return self->GetStance();
}
uint8 Lua_Merc::GetTierID()
{
Lua_Safe_Call_Int();
return self->GetTierID();
}
bool Lua_Merc::HasOrMayGetAggro()
{
Lua_Safe_Call_Bool();
return self->HasOrMayGetAggro();
}
bool Lua_Merc::IsMercenaryCaster()
{
Lua_Safe_Call_Bool();
return self->IsMercCaster();
}
bool Lua_Merc::IsMercenaryCasterCombatRange(Lua_Mob target)
{
Lua_Safe_Call_Bool();
return self->IsMercCasterCombatRange(target);
}
bool Lua_Merc::IsSitting()
{
Lua_Safe_Call_Bool();
return self->IsSitting();
}
bool Lua_Merc::IsStanding()
{
Lua_Safe_Call_Bool();
return self->IsStanding();
}
void Lua_Merc::ScaleStats(int scale_percentage)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage);
}
void Lua_Merc::ScaleStats(int scale_percentage, bool set_to_max)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage, set_to_max);
}
void Lua_Merc::SendPayload(int payload_id, std::string payload_value)
{
Lua_Safe_Call_Void();
self->SendPayload(payload_id, payload_value);
}
void Lua_Merc::SetTarget(Lua_Mob target)
{
Lua_Safe_Call_Void();
self->SetTarget(target);
}
void Lua_Merc::Signal(int signal_id)
{
Lua_Safe_Call_Void();
self->Signal(signal_id);
}
void Lua_Merc::Sit()
{
Lua_Safe_Call_Void();
self->Sit();
}
void Lua_Merc::Stand()
{
Lua_Safe_Call_Void();
self->Stand();
}
bool Lua_Merc::Suspend()
{
Lua_Safe_Call_Bool();
return self->Suspend();
}
bool Lua_Merc::UseDiscipline(uint16 spell_id, uint16 target_id)
{
Lua_Safe_Call_Bool();
return self->UseDiscipline(spell_id, target_id);
}
luabind::scope lua_register_merc() {
return luabind::class_<Lua_Merc, Lua_Mob>("Merc")
.def(luabind::constructor<>())
.def("GetCostFormula", &Lua_Merc::GetCostFormula)
.def("GetGroup", &Lua_Merc::GetGroup)
.def("GetHatedCount", &Lua_Merc::GetHatedCount)
.def("GetMaxMeleeRangeToTarget", &Lua_Merc::GetMaxMeleeRangeToTarget)
.def("GetMercenaryCharacterID", &Lua_Merc::GetMercenaryCharacterID)
.def("GetMercenaryID", &Lua_Merc::GetMercenaryID)
.def("GetMercenaryNameType", &Lua_Merc::GetMercenaryNameType)
.def("GetMercenaryOwner", &Lua_Merc::GetMercenaryOwner)
.def("GetMercenarySubtype", &Lua_Merc::GetMercenarySubtype)
.def("GetMercenaryTemplateID", &Lua_Merc::GetMercenaryTemplateID)
.def("GetMercenaryType", &Lua_Merc::GetMercenaryType)
.def("GetOwner", &Lua_Merc::GetOwner)
.def("GetOwnerOrSelf", &Lua_Merc::GetOwnerOrSelf)
.def("GetProficiencyID", &Lua_Merc::GetProficiencyID)
.def("GetStance", &Lua_Merc::GetStance)
.def("GetTierID", &Lua_Merc::GetTierID)
.def("HasOrMayGetAggro", &Lua_Merc::HasOrMayGetAggro)
.def("IsMercenaryCaster", &Lua_Merc::IsMercenaryCaster)
.def("IsMercenaryCasterCombatRange", &Lua_Merc::IsMercenaryCasterCombatRange)
.def("IsSitting", &Lua_Merc::IsSitting)
.def("IsStanding", &Lua_Merc::IsStanding)
.def("ScaleStats", (void(Lua_Merc::*)(int))&Lua_Merc::ScaleStats)
.def("ScaleStats", (void(Lua_Merc::*)(int,bool))&Lua_Merc::ScaleStats)
.def("SendPayload", &Lua_Merc::SendPayload)
.def("SetTarget", &Lua_Merc::SetTarget)
.def("Signal", &Lua_Merc::Signal)
.def("Sit", &Lua_Merc::Sit)
.def("Stand", &Lua_Merc::Stand)
.def("Suspend", &Lua_Merc::Suspend)
.def("UseDiscipline", &Lua_Merc::UseDiscipline);
}
#endif

63
zone/lua_merc.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef EQEMU_LUA_MERC_H
#define EQEMU_LUA_MERC_H
#ifdef LUA_EQEMU
#include "lua_mob.h"
class Merc;
class Lua_Group;
class Lua_Merc;
class Lua_Mob;
namespace luabind {
struct scope;
}
luabind::scope lua_register_merc();
class Lua_Merc : public Lua_Mob
{
typedef Merc NativeType;
public:
Lua_Merc() { SetLuaPtrData(nullptr); }
Lua_Merc(Merc *d) { SetLuaPtrData(reinterpret_cast<Entity*>(d)); }
virtual ~Lua_Merc() { }
operator Merc*() {
return reinterpret_cast<Merc*>(GetLuaPtrData());
}
uint32 GetCostFormula();
Lua_Group GetGroup();
int GetHatedCount();
float GetMaxMeleeRangeToTarget(Lua_Mob target);
uint32 GetMercenaryCharacterID();
uint32 GetMercenaryID();
uint32 GetMercenaryNameType();
Lua_Client GetMercenaryOwner();
uint32 GetMercenarySubtype();
uint32 GetMercenaryTemplateID();
uint32 GetMercenaryType();
Lua_Mob GetOwner();
Lua_Mob GetOwnerOrSelf();
uint8 GetProficiencyID();
uint8 GetStance();
uint8 GetTierID();
bool HasOrMayGetAggro();
bool IsMercenaryCaster();
bool IsMercenaryCasterCombatRange(Lua_Mob target);
bool IsSitting();
bool IsStanding();
void ScaleStats(int scale_percentage);
void ScaleStats(int scale_percentage, bool set_to_max);
void SendPayload(int payload_id, std::string payload_value);
void SetTarget(Lua_Mob target);
void Signal(int signal_id);
void Sit();
void Stand();
bool Suspend();
bool UseDiscipline(uint16 spell_id, uint16 target_id);
};
#endif // LUA_EQEMU
#endif // EQEMU_LUA_MERC_H

View File

@ -10,6 +10,7 @@ class Lua_Item;
class Lua_ItemInst; class Lua_ItemInst;
class Lua_StatBonuses; class Lua_StatBonuses;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Client; class Lua_Client;
struct Lua_Mob_List; struct Lua_Mob_List;

View File

@ -32,6 +32,7 @@
#include "lua_inventory.h" #include "lua_inventory.h"
#include "lua_item.h" #include "lua_item.h"
#include "lua_iteminst.h" #include "lua_iteminst.h"
#include "lua_merc.h"
#include "lua_mob.h" #include "lua_mob.h"
#include "lua_npc.h" #include "lua_npc.h"
#include "lua_object.h" #include "lua_object.h"
@ -1279,6 +1280,7 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_npc(), lua_register_npc(),
lua_register_client(), lua_register_client(),
lua_register_bot(), lua_register_bot(),
lua_register_merc(),
lua_register_inventory(), lua_register_inventory(),
lua_register_inventory_where(), lua_register_inventory_where(),
lua_register_iteminst(), lua_register_iteminst(),
@ -1835,3 +1837,190 @@ void LuaParser::LoadBotScript(std::string filename) {
void LuaParser::LoadGlobalBotScript(std::string filename) { void LuaParser::LoadGlobalBotScript(std::string filename) {
LoadScript(filename, "global_bot"); LoadScript(filename, "global_bot");
} }
int LuaParser::EventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!MercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!GlobalMercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("global_merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventMerc(
std::string package_name,
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func
) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 2;
PushErrorHandler(L);
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 3;
}
lua_createtable(L, 0, 0);
//push self
Lua_Merc l_merc(merc);
luabind::adl::object l_merc_o = luabind::adl::object(L, l_merc);
l_merc_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = NPCArgumentDispatch[evt];
arg_function(this, L, merc, init, data, extra_data, extra_pointers);
auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(merc, c);
if(lua_pcall(L, 1, 1, start + 1)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
AddError(
fmt::format(
"Lua Exception | [{}] for Merc [{}] in [{}]: {}",
sub_name,
merc->GetID(),
package_name,
ex.what()
)
);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::DispatchEventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
std::string package_name = "merc";
auto iter = lua_encounter_events_registered.find(package_name);
if (iter == lua_encounter_events_registered.end()) {
return 0;
}
int ret = 0;
auto riter = iter->second.begin();
while (riter != iter->second.end()) {
if (riter->event_id == evt) {
package_name = fmt::format("encounter_{}", riter->encounter_name);
int i = _EventMerc(package_name, evt, merc, init, data, extra_data, extra_pointers, &riter->lua_reference);
if (i != 0) {
ret = i;
}
}
++riter;
}
return ret;
}
bool LuaParser::MercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "merc");
}
bool LuaParser::GlobalMercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_merc");
}
void LuaParser::LoadMercScript(std::string filename) {
LoadScript(filename, "merc");
}
void LuaParser::LoadGlobalMercScript(std::string filename) {
LoadScript(filename, "global_merc");
}

View File

@ -109,6 +109,22 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int EventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt);
virtual bool HasGlobalQuestSub(QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt);
@ -120,6 +136,8 @@ public:
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt); virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt);
virtual bool BotHasQuestSub(QuestEventID evt); virtual bool BotHasQuestSub(QuestEventID evt);
virtual bool GlobalBotHasQuestSub(QuestEventID evt); virtual bool GlobalBotHasQuestSub(QuestEventID evt);
virtual bool MercHasQuestSub(QuestEventID evt);
virtual bool GlobalMercHasQuestSub(QuestEventID evt);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@ -130,6 +148,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name); virtual void LoadEncounterScript(std::string filename, std::string encounter_name);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@ -179,6 +199,14 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int DispatchEventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
static LuaParser* Instance() { static LuaParser* Instance() {
static LuaParser inst; static LuaParser inst;
@ -269,6 +297,16 @@ private:
std::vector<std::any> *extra_pointers, std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr luabind::adl::object *l_func = nullptr
); );
int _EventMerc(
std::string package_name,
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers,
luabind::adl::object* l_func = nullptr
);
void LoadScript(std::string filename, std::string package_name); void LoadScript(std::string filename, std::string package_name);
void MapFunctions(lua_State *L); void MapFunctions(lua_State *L);

View File

@ -734,6 +734,18 @@ void handle_player_death(
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4])); lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4]));
lua_setfield(L, -2, "killed_entity_id"); lua_setfield(L, -2, "killed_entity_id");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[5]));
lua_setfield(L, -2, "combat_start_time");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[6]));
lua_setfield(L, -2, "combat_end_time");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[7]));
lua_setfield(L, -2, "damage_received");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[8]));
lua_setfield(L, -2, "healing_received");
} }
void handle_player_timer( void handle_player_timer(

View File

@ -8,6 +8,7 @@ typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::Ite
typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*);
typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*);
typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*); typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*);
typedef void(*MercArgumentHandler)(QuestInterface*, lua_State*, Merc*, Mob*, std::string, uint32, std::vector<std::any>*);
// NPC // NPC
void handle_npc_event_say( void handle_npc_event_say(

View File

@ -5,6 +5,7 @@
#include "entity.h" #include "entity.h"
#include "groups.h" #include "groups.h"
#include "mob.h" #include "mob.h"
#include "quest_parser_collection.h"
#include "zone.h" #include "zone.h"
#include "string_ids.h" #include "string_ids.h"
@ -4078,12 +4079,6 @@ bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillT
Save(); Save();
//no corpse, no exp if we're a merc.
//We'll suspend instead, since that's what live does.
//Not actually sure live supports 'depopping' merc corpses.
//if(entity_list.GetCorpseByID(GetID()))
// entity_list.GetCorpseByID(GetID())->Depop();
// If client is in zone, suspend merc, else depop it. // If client is in zone, suspend merc, else depop it.
if (!Suspend()) { if (!Suspend()) {
Depop(); Depop();
@ -4671,7 +4666,6 @@ bool Merc::Spawn(Client *owner) {
//UpdateMercAppearance(); //UpdateMercAppearance();
return true; return true;
} }
@ -5189,9 +5183,6 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetStance(GetMercInfo().Stance); merc->SetStance(GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName()); Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
return;
} }
bool Merc::Suspend() { bool Merc::Suspend() {
@ -5914,3 +5905,19 @@ uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type
return cost; return cost;
} }
void Merc::Signal(int signal_id)
{
if (parse->MercHasQuestSub(EVENT_SIGNAL)) {
parse->EventMerc(EVENT_SIGNAL, this, nullptr, std::to_string(signal_id), 0);
}
}
void Merc::SendPayload(int payload_id, std::string payload_value)
{
if (parse->MercHasQuestSub(EVENT_PAYLOAD)) {
const auto& export_string = fmt::format("{} {}", payload_id, payload_value);
parse->EventMerc(EVENT_PAYLOAD, this, nullptr, export_string, 0);
}
}

View File

@ -146,6 +146,9 @@ public:
bool IsMedding() { return _medding; }; bool IsMedding() { return _medding; };
bool IsSuspended() { return _suspended; }; bool IsSuspended() { return _suspended; };
void Signal(int signal_id);
void SendPayload(int payload_id, std::string payload_value);
static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0);
static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0);

View File

@ -5514,37 +5514,13 @@ void Mob::SetTarget(Mob *mob)
target = mob; target = mob;
entity_list.UpdateHoTT(this); entity_list.UpdateHoTT(this);
const auto has_target_change_event = (
parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE) ||
parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE) ||
parse->BotHasQuestSub(EVENT_TARGET_CHANGE)
);
if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) { if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) {
DisplayInfo(mob); DisplayInfo(mob);
} }
if (has_target_change_event) { std::vector<std::any> args = { mob };
std::vector<std::any> args;
args.emplace_back(mob); parse->EventMob(EVENT_TARGET_CHANGE, this, mob, [&]() { return ""; }, 0, &args);
if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE)) {
parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0, &args);
}
} else if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0, &args);
}
CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above)
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventBot(EVENT_TARGET_CHANGE, CastToBot(), mob, "", 0, &args);
}
}
}
if (IsPet() && GetOwner() && GetOwner()->IsClient()) { if (IsPet() && GetOwner() && GetOwner()->IsClient()) {
GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob);
@ -5717,22 +5693,10 @@ bool Mob::ClearEntityVariables()
return false; return false;
} }
if ( for (const auto& e : m_EntityVariables) {
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) || std::vector<std::any> args = { e.first, e.second };
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
for (const auto& e : m_EntityVariables) {
std::vector<std::any> args = { e.first, e.second };
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
} }
m_EntityVariables.clear(); m_EntityVariables.clear();
@ -5750,24 +5714,11 @@ bool Mob::DeleteEntityVariable(std::string variable_name)
return false; return false;
} }
std::vector<std::any> args = { v->first, v->second };
parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
m_EntityVariables.erase(v); m_EntityVariables.erase(v);
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
std::vector<std::any> args = { v->first, v->second };
if (IsBot()) {
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
return true; return true;
} }
@ -5814,32 +5765,16 @@ void Mob::SetEntityVariable(std::string variable_name, std::string variable_valu
return; return;
} }
const QuestEventID event_id = ( std::vector<std::any> args;
!EntityVariableExists(variable_name) ?
EVENT_ENTITY_VARIABLE_SET :
EVENT_ENTITY_VARIABLE_UPDATE
);
if ( if (!EntityVariableExists(variable_name)) {
(IsBot() && parse->BotHasQuestSub(event_id)) || args = { variable_name, variable_value };
(IsClient() && parse->PlayerHasQuestSub(event_id)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), event_id))
) {
std::vector<std::any> args;
if (event_id != EVENT_ENTITY_VARIABLE_UPDATE) { parse->EventMob(EVENT_ENTITY_VARIABLE_SET, this, nullptr, [&]() { return ""; }, 0, &args);
args = { variable_name, variable_value }; } else {
} else { args = { variable_name, GetEntityVariable(variable_name), variable_value };
args = { variable_name, GetEntityVariable(variable_name), variable_value };
}
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_UPDATE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(event_id, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(event_id, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(event_id, CastToNPC(), nullptr, "", 0, &args);
}
} }
m_EntityVariables[variable_name] = variable_value; m_EntityVariables[variable_name] = variable_value;

View File

@ -1755,6 +1755,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
SetAppearance(eaStanding); SetAppearance(eaStanding);
parse->EventBotMerc(EVENT_COMBAT, this, attacker, [&] { return "1"; });
if (IsNPC()) { if (IsNPC()) {
CastToNPC()->AIautocastspell_timer->Start(300, false); CastToNPC()->AIautocastspell_timer->Start(300, false);
@ -1793,12 +1795,6 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
} }
} }
} }
if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), attacker, "1", 0);
}
}
} }
// Note: Hate list may not be actually clear until after this function call completes // Note: Hate list may not be actually clear until after this function call completes
@ -1819,27 +1815,19 @@ void Mob::AI_Event_NoLongerEngaged() {
StopNavigation(); StopNavigation();
ClearRampage(); ClearRampage();
parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; });
if (IsNPC()) { if (IsNPC()) {
SetPrimaryAggro(false); SetPrimaryAggro(false);
SetAssistAggro(false); SetAssistAggro(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(GetID())) { const uint32 emote_id = CastToNPC()->GetEmoteID();
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) { if (emote_id) {
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
}
const uint32 emote_id = CastToNPC()->GetEmoteID();
if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
}
m_combat_record.Stop();
CastToNPC()->SetCombatEvent(false);
} }
}
} else if (IsBot()) { m_combat_record.Stop();
if (parse->BotHasQuestSub(EVENT_COMBAT)) { CastToNPC()->SetCombatEvent(false);
parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0);
} }
} }
} }

View File

@ -833,22 +833,10 @@ void NPC::Depop(bool start_spawn_timer) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid); DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid);
} }
if (IsNPC()) { parse->EventBotMercNPC(EVENT_DESPAWN, this, nullptr);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DESPAWN)) {
parse->EventNPC(EVENT_DESPAWN, this, nullptr, "", 0);
}
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr);
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_DESPAWN)) {
parse->EventBot(EVENT_DESPAWN, CastToBot(), nullptr, "", 0);
}
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr);
}
} }
p_depop = true; p_depop = true;

View File

@ -3207,6 +3207,11 @@ void Perl_Client_AreaTaunt(Client* self, float range, int bonus_hate)
entity_list.AETaunt(self, range, bonus_hate); entity_list.AETaunt(self, range, bonus_hate);
} }
Merc* Perl_Client_GetMerc(Client* self)
{
return self->GetMerc();
}
void perl_register_client() void perl_register_client()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@ -3438,6 +3443,7 @@ void perl_register_client()
package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines); package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines);
package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID); package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID);
package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance); package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance);
package.add("GetMerc", &Perl_Client_GetMerc);
package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells); package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells);
package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel); package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel);
package.add("GetMoney", &Perl_Client_GetMoney); package.add("GetMoney", &Perl_Client_GetMoney);

195
zone/perl_merc.cpp Normal file
View File

@ -0,0 +1,195 @@
#include "../common/features.h"
#ifdef EMBPERL_XS_CLASSES
#include "../common/global_define.h"
#include "embperl.h"
#include "merc.h"
uint32 Perl_Merc_GetCostFormula(Merc* self)
{
return self->GetCostFormula();
}
Group* Perl_Merc_GetGroup(Merc* self)
{
return self->GetGroup();
}
int Perl_Merc_GetHatedCount(Merc* self)
{
return self->GetHatedCount();
}
float Perl_Merc_GetMaxMeleeRangeToTarget(Merc* self, Mob* target)
{
return self->GetMaxMeleeRangeToTarget(target);
}
uint32 Perl_Merc_GetMercenaryCharacterID(Merc* self)
{
return self->GetMercenaryCharacterID();
}
uint32 Perl_Merc_GetMercenaryID(Merc* self)
{
return self->GetMercenaryID();
}
uint32 Perl_Merc_GetMercenaryNameType(Merc* self)
{
return self->GetMercNameType();
}
Client* Perl_Merc_GetMercenaryOwner(Merc* self)
{
return self->GetMercenaryOwner();
}
uint32 Perl_Merc_GetMercenarySubtype(Merc* self)
{
return self->GetMercenarySubType();
}
uint32 Perl_Merc_GetMercenaryTemplateID(Merc* self)
{
return self->GetMercenaryTemplateID();
}
uint32 Perl_Merc_GetMercenaryType(Merc* self)
{
return self->GetMercenaryType();
}
Mob* Perl_Merc_GetOwner(Merc* self)
{
return self->GetOwner();
}
Mob* Perl_Merc_GetOwnerOrSelf(Merc* self)
{
return self->GetOwnerOrSelf();
}
uint8 Perl_Merc_GetProficiencyID(Merc* self)
{
return self->GetProficiencyID();
}
uint8 Perl_Merc_GetStance(Merc* self)
{
return self->GetStance();
}
uint8 Perl_Merc_GetTierID(Merc* self)
{
return self->GetTierID();
}
bool Perl_Merc_HasOrMayGetAggro(Merc* self)
{
return self->HasOrMayGetAggro();
}
bool Perl_Merc_IsMercenaryCaster(Merc* self)
{
return self->IsMercCaster();
}
bool Perl_Merc_IsMercenaryCasterCombatRange(Merc* self, Mob* target)
{
return self->IsMercCasterCombatRange(target);
}
bool Perl_Merc_IsSitting(Merc* self)
{
return self->IsSitting();
}
bool Perl_Merc_IsStanding(Merc* self)
{
return self->IsStanding();
}
void Perl_Merc_ScaleStats(Merc* self, int scale_percentage)
{
self->ScaleStats(scale_percentage);
}
void Perl_Merc_ScaleStats(Merc* self, int scale_percentage, bool set_to_max)
{
self->ScaleStats(scale_percentage, set_to_max);
}
void Perl_Merc_SendPayload(Merc* self, int payload_id, std::string payload_value)
{
self->SendPayload(payload_id, payload_value);
}
void Perl_Merc_SetTarget(Merc* self, Mob* target)
{
self->SetTarget(target);
}
void Perl_Merc_Signal(Merc* self, int signal_id)
{
self->Signal(signal_id);
}
void Perl_Merc_Sit(Merc* self)
{
self->Sit();
}
void Perl_Merc_Stand(Merc* self)
{
self->Stand();
}
bool Perl_Merc_Suspend(Merc* self)
{
return self->Suspend();
}
bool Perl_Merc_UseDiscipline(Merc* self, uint16 spell_id, uint16 target_id)
{
return self->UseDiscipline(spell_id, target_id);
}
void perl_register_merc()
{
perl::interpreter state(PERL_GET_THX);
auto package = state.new_class<Merc>("Merc");
package.add_base_class("NPC");
package.add("GetCostFormula", &Perl_Merc_GetCostFormula);
package.add("GetGroup", &Perl_Merc_GetGroup);
package.add("GetHatedCount", &Perl_Merc_GetHatedCount);
package.add("GetMaxMeleeRangeToTarget", &Perl_Merc_GetMaxMeleeRangeToTarget);
package.add("GetMercenaryCharacterID", &Perl_Merc_GetMercenaryCharacterID);
package.add("GetMercenaryID", &Perl_Merc_GetMercenaryID);
package.add("GetMercenaryNameType", &Perl_Merc_GetMercenaryNameType);
package.add("GetMercenaryOwner", &Perl_Merc_GetMercenaryOwner);
package.add("GetMercenarySubtype", &Perl_Merc_GetMercenarySubtype);
package.add("GetMercenaryTemplateID", &Perl_Merc_GetMercenaryTemplateID);
package.add("GetMercenaryType", &Perl_Merc_GetMercenaryType);
package.add("GetOwner", &Perl_Merc_GetOwner);
package.add("GetOwnerOrSelf", &Perl_Merc_GetOwnerOrSelf);
package.add("GetProficiencyID", &Perl_Merc_GetProficiencyID);
package.add("GetStance", &Perl_Merc_GetStance);
package.add("GetTierID", &Perl_Merc_GetTierID);
package.add("HasOrMayGetAggro", &Perl_Merc_HasOrMayGetAggro);
package.add("IsMercenaryCaster", &Perl_Merc_IsMercenaryCaster);
package.add("IsMercenaryCasterCombatRange", &Perl_Merc_IsMercenaryCasterCombatRange);
package.add("IsSitting", &Perl_Merc_IsSitting);
package.add("IsStanding", &Perl_Merc_IsStanding);
package.add("ScaleStats", (void(*)(Merc*, int))&Perl_Merc_ScaleStats);
package.add("ScaleStats", (void(*)(Merc*, int, bool))&Perl_Merc_ScaleStats);
package.add("SendPayload", &Perl_Merc_SendPayload);
package.add("SetTarget", &Perl_Merc_SetTarget);
package.add("Signal", &Perl_Merc_Signal);
package.add("Sit", &Perl_Merc_Sit);
package.add("Stand", &Perl_Merc_Stand);
package.add("Suspend", &Perl_Merc_Suspend);
package.add("UseDiscipline", &Perl_Merc_UseDiscipline);
}
#endif //EMBPERL_XS_CLASSES

View File

@ -139,6 +139,30 @@ public:
return 0; return 0;
} }
virtual int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual int EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id) virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id)
{ {
return false; return false;
@ -189,6 +213,16 @@ public:
return false; return false;
} }
virtual bool MercHasQuestSub(QuestEventID event_id)
{
return false;
}
virtual bool GlobalMercHasQuestSub(QuestEventID event_id)
{
return false;
}
virtual void LoadNPCScript(std::string filename, int npc_id) { } virtual void LoadNPCScript(std::string filename, int npc_id) { }
virtual void LoadGlobalNPCScript(std::string filename) { } virtual void LoadGlobalNPCScript(std::string filename) { }
virtual void LoadPlayerScript(std::string filename) { } virtual void LoadPlayerScript(std::string filename) { }
@ -198,6 +232,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { } virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { }
virtual void LoadBotScript(std::string filename) { } virtual void LoadBotScript(std::string filename) { }
virtual void LoadGlobalBotScript(std::string filename) { } virtual void LoadGlobalBotScript(std::string filename) { }
virtual void LoadMercScript(std::string filename) { }
virtual void LoadGlobalMercScript(std::string filename) { }
virtual int DispatchEventNPC( virtual int DispatchEventNPC(
QuestEventID event_id, QuestEventID event_id,
@ -260,6 +296,18 @@ public:
return 0; return 0;
} }
virtual int DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual void AddVar(std::string name, std::string val) { } virtual void AddVar(std::string name, std::string val) { }
virtual std::string GetVar(std::string name) virtual std::string GetVar(std::string name)
{ {

View File

@ -47,6 +47,8 @@ QuestParserCollection::QuestParserCollection()
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
} }
QuestParserCollection::~QuestParserCollection() { } QuestParserCollection::~QuestParserCollection() { }
@ -94,6 +96,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers)
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
_spell_quest_status.clear(); _spell_quest_status.clear();
_item_quest_status.clear(); _item_quest_status.clear();
@ -379,6 +383,49 @@ bool QuestParserCollection::BotHasQuestSub(QuestEventID event_id)
return BotHasQuestSubLocal(event_id) || BotHasQuestSubGlobal(event_id); return BotHasQuestSubLocal(event_id) || BotHasQuestSubGlobal(event_id);
} }
bool QuestParserCollection::MercHasQuestSubLocal(QuestEventID event_id)
{
if (_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByMercQuest(filename);
if (qi) {
_merc_quest_status = qi->GetIdentifier();
qi->LoadMercScript(filename);
return qi->MercHasQuestSub(event_id);
}
} else if (_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_merc_quest_status);
return iter->second->MercHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::MercHasQuestSubGlobal(QuestEventID event_id)
{
if (_global_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalMercQuest(filename);
if (qi) {
_global_merc_quest_status = qi->GetIdentifier();
qi->LoadGlobalMercScript(filename);
return qi->GlobalMercHasQuestSub(event_id);
}
} else if (_global_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_merc_quest_status);
return iter->second->GlobalMercHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::MercHasQuestSub(QuestEventID event_id)
{
return MercHasQuestSubLocal(event_id) || MercHasQuestSubGlobal(event_id);
}
int QuestParserCollection::EventNPC( int QuestParserCollection::EventNPC(
QuestEventID event_id, QuestEventID event_id,
NPC* npc, NPC* npc,
@ -793,6 +840,86 @@ int QuestParserCollection::EventBotGlobal(
return 0; return 0;
} }
int QuestParserCollection::EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
const int local_return = EventMercLocal(event_id, merc, init, data, extra_data, extra_pointers);
const int global_return = EventMercGlobal(event_id, merc, init, data, extra_data, extra_pointers);
const int default_return = DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers);
if (local_return != 0) {
return local_return;
} else if (global_return != 0) {
return global_return;
} else if (default_return != 0) {
return default_return;
}
return 0;
}
int QuestParserCollection::EventMercLocal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByMercQuest(filename);
if (qi) {
_merc_quest_status = qi->GetIdentifier();
qi->LoadMercScript(filename);
return qi->EventMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
} else {
if (_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_merc_quest_status);
return iter->second->EventMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
}
return 0;
}
int QuestParserCollection::EventMercGlobal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_global_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalMercQuest(filename);
if (qi) {
_global_merc_quest_status = qi->GetIdentifier();
qi->LoadGlobalMercScript(filename);
return qi->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
} else {
if (_global_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_merc_quest_status);
return iter->second->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
}
return 0;
}
QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename) QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename)
{ {
if (!zone) { if (!zone) {
@ -1188,6 +1315,81 @@ QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filena
return nullptr; return nullptr;
} }
QuestInterface* QuestParserCollection::GetQIByMercQuest(std::string& filename)
{
if (!zone || !zone->IsLoaded()) {
return nullptr;
}
const std::string& global_path = fmt::format(
"{}/{}",
path.GetQuestsPath(),
QUEST_GLOBAL_DIRECTORY
);
const std::string& zone_path = fmt::format(
"{}/{}",
path.GetQuestsPath(),
zone->GetShortName()
);
const std::string& zone_versioned_path = fmt::format(
"{}/{}/v{}",
path.GetQuestsPath(),
zone->GetShortName(),
zone->GetInstanceVersion()
);
std::vector<std::string> file_names = {
fmt::format("{}/merc", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/merc.ext
fmt::format("{}/merc_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version
fmt::format("{}/merc", zone_path), // Local
fmt::format("{}/merc", global_path) // Global
};
std::string file_name;
for (auto & file : file_names) {
for (auto* e: _load_precedence) {
file_name = fmt::format(
"{}.{}",
file,
_extensions.find(e->GetIdentifier())->second
);
if (File::Exists(file_name)) {
filename = file_name;
return e;
}
}
}
return nullptr;
}
QuestInterface* QuestParserCollection::GetQIByGlobalMercQuest(std::string& filename)
{
if (!zone) {
return nullptr;
}
std::string file_name;
for (auto* e: _load_precedence) {
file_name = fmt::format(
"{}/{}/global_merc.{}",
path.GetQuestsPath(),
QUEST_GLOBAL_DIRECTORY,
_extensions.find(e->GetIdentifier())->second
);
if (File::Exists(file_name)) {
filename = file_name;
return e;
}
}
return nullptr;
}
void QuestParserCollection::GetErrors(std::list<std::string>& quest_errors) void QuestParserCollection::GetErrors(std::list<std::string>& quest_errors)
{ {
quest_errors.clear(); quest_errors.clear();
@ -1303,6 +1505,27 @@ int QuestParserCollection::DispatchEventBot(
return ret; return ret;
} }
int QuestParserCollection::DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
int ret = 0;
for (const auto& e: _load_precedence) {
int i = e->DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers);
if (i != 0) {
ret = i;
}
}
return ret;
}
void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s) void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s)
{ {
for (int i = 0; i < _LargestEventID; i++) { for (int i = 0; i < _LargestEventID; i++) {
@ -1325,3 +1548,81 @@ void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings*
LogInfo("Loaded [{}] Perl Event Export Settings", l.size()); LogInfo("Loaded [{}] Perl Event Export Settings", l.size());
} }
int QuestParserCollection::EventBotMerc(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventBotMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventMob(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsClient() && PlayerHasQuestSub(event_id)) {
return EventPlayer(event_id, e->CastToClient(), lazy_data(), extra_data, extra_pointers);
} else if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}

View File

@ -71,6 +71,7 @@ public:
bool SpellHasQuestSub(uint32 spell_id, QuestEventID event_id); bool SpellHasQuestSub(uint32 spell_id, QuestEventID event_id);
bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
bool BotHasQuestSub(QuestEventID event_id); bool BotHasQuestSub(QuestEventID event_id);
bool MercHasQuestSub(QuestEventID event_id);
int EventNPC( int EventNPC(
QuestEventID event_id, QuestEventID event_id,
@ -126,6 +127,51 @@ public:
std::vector<std::any> *extra_pointers = nullptr std::vector<std::any> *extra_pointers = nullptr
); );
int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr
);
int EventBotMerc(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventBotMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventMob(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
void GetErrors(std::list<std::string> &quest_errors); void GetErrors(std::list<std::string> &quest_errors);
/* /*
@ -161,6 +207,8 @@ private:
bool HasEncounterSub(QuestEventID event_id, const std::string& package_name); bool HasEncounterSub(QuestEventID event_id, const std::string& package_name);
bool BotHasQuestSubLocal(QuestEventID event_id); bool BotHasQuestSubLocal(QuestEventID event_id);
bool BotHasQuestSubGlobal(QuestEventID event_id); bool BotHasQuestSubGlobal(QuestEventID event_id);
bool MercHasQuestSubLocal(QuestEventID event_id);
bool MercHasQuestSubGlobal(QuestEventID event_id);
int EventNPCLocal( int EventNPCLocal(
QuestEventID event_id, QuestEventID event_id,
@ -214,6 +262,24 @@ private:
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
int EventMercLocal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
int EventMercGlobal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
QuestInterface* GetQIByNPCQuest(uint32 npc_id, std::string& filename); QuestInterface* GetQIByNPCQuest(uint32 npc_id, std::string& filename);
QuestInterface* GetQIByGlobalNPCQuest(std::string& filename); QuestInterface* GetQIByGlobalNPCQuest(std::string& filename);
QuestInterface* GetQIByPlayerQuest(std::string& filename); QuestInterface* GetQIByPlayerQuest(std::string& filename);
@ -223,6 +289,8 @@ private:
QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename); QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename);
QuestInterface* GetQIByBotQuest(std::string& filename); QuestInterface* GetQIByBotQuest(std::string& filename);
QuestInterface* GetQIByGlobalBotQuest(std::string& filename); QuestInterface* GetQIByGlobalBotQuest(std::string& filename);
QuestInterface* GetQIByMercQuest(std::string& filename);
QuestInterface* GetQIByGlobalMercQuest(std::string& filename);
int DispatchEventNPC( int DispatchEventNPC(
QuestEventID event_id, QuestEventID event_id,
@ -270,6 +338,15 @@ private:
std::vector<std::any>* extra_pointers std::vector<std::any>* extra_pointers
); );
int DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
std::map<uint32, QuestInterface*> _interfaces; std::map<uint32, QuestInterface*> _interfaces;
std::map<uint32, std::string> _extensions; std::map<uint32, std::string> _extensions;
std::list<QuestInterface*> _load_precedence; std::list<QuestInterface*> _load_precedence;
@ -280,6 +357,8 @@ private:
uint32 _global_player_quest_status; uint32 _global_player_quest_status;
uint32 _bot_quest_status; uint32 _bot_quest_status;
uint32 _global_bot_quest_status; uint32 _global_bot_quest_status;
uint32 _merc_quest_status;
uint32 _global_merc_quest_status;
std::map<uint32, uint32> _spell_quest_status; std::map<uint32, uint32> _spell_quest_status;
std::map<uint32, uint32> _item_quest_status; std::map<uint32, uint32> _item_quest_status;
std::map<std::string, uint32> _encounter_quest_status; std::map<std::string, uint32> _encounter_quest_status;

View File

@ -90,12 +90,9 @@ void QuestManager::Process() {
while (cur != end) { while (cur != end) {
if (cur->Timer_.Enabled() && cur->Timer_.Check()) { if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
if (cur->mob) { if (cur->mob) {
if (cur->mob->IsNPC()) { parse->EventMob(EVENT_TIMER, cur->mob, nullptr, [&]() { return cur->name; }, 0);
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); if (cur->mob->IsEncounter()) {
}
}
else if (cur->mob->IsEncounter()) {
parse->EventEncounter( parse->EventEncounter(
EVENT_TIMER, EVENT_TIMER,
cur->mob->CastToEncounter()->GetEncounterName(), cur->mob->CastToEncounter()->GetEncounterName(),
@ -104,17 +101,6 @@ void QuestManager::Process() {
nullptr nullptr
); );
} }
else if (cur->mob->IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_TIMER)) {
//this is inheriently unsafe if we ever make it so more than npc/client start timers
parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
}
}
else if (cur->mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_TIMER)) {
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
}
}
//we MUST reset our iterator since the quest could have removed/added any //we MUST reset our iterator since the quest could have removed/added any
//number of timers... worst case we have to check a bunch of timers twice //number of timers... worst case we have to check a bunch of timers twice
@ -539,32 +525,20 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob*
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
); seconds * 1000
);
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto& e : QTimerList) { for (auto& e : QTimerList) {
if (e.mob && e.mob == mob && e.name == timer_name) { if (e.mob && e.mob == mob && e.name == timer_name) {
e.Timer_.Start(seconds * 1000, false); e.Timer_.Start(seconds * 1000, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
seconds * 1000
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@ -573,21 +547,7 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob*
QTimerList.emplace_back(QuestTimer(seconds * 1000, mob, timer_name)); QTimerList.emplace_back(QuestTimer(seconds * 1000, mob, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
seconds * 1000
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds) void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds)
@ -598,11 +558,13 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
); milliseconds
);
};
if (questitem) { if (questitem) {
questitem->SetTimer(timer_name, milliseconds); questitem->SetTimer(timer_name, milliseconds);
@ -625,21 +587,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
if (e.mob && e.mob == owner && e.name == timer_name) { if (e.mob && e.mob == owner && e.name == timer_name) {
e.Timer_.Start(milliseconds, false); e.Timer_.Start(milliseconds, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
e.name,
milliseconds
);
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@ -648,21 +596,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds, EQ::ItemInstance* inst) void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds, EQ::ItemInstance* inst)
@ -678,32 +612,20 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
); milliseconds
);
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto& e : QTimerList) { for (auto& e : QTimerList) {
if (e.mob && e.mob == m && e.name == timer_name) { if (e.mob && e.mob == m && e.name == timer_name) {
e.Timer_.Start(milliseconds, false); e.Timer_.Start(milliseconds, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@ -712,21 +634,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::stoptimer(const std::string& timer_name) void QuestManager::stoptimer(const std::string& timer_name)
@ -751,23 +659,16 @@ void QuestManager::stoptimer(const std::string& timer_name)
return; return;
} }
const bool has_stop_event = (
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
if (e->mob && e->mob == owner && e->name == timer_name) { if (e->mob && e->mob == owner && e->name == timer_name) {
if (has_stop_event) { parse->EventMob(
if (owner->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0); owner,
} else if (owner->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, timer_name, 0); [&]() {
} else if (owner->IsNPC()) { return timer_name;
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, timer_name, 0);
} }
} );
QTimerList.erase(e); QTimerList.erase(e);
break; break;
@ -792,23 +693,16 @@ void QuestManager::stoptimer(const std::string& timer_name, Mob* m)
return; return;
} }
const bool has_stop_event = (
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == m) { if (e->mob && e->mob == m) {
if (has_stop_event) { parse->EventMob(
if (m->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); m,
} else if (m->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (m->IsNPC()) { return timer_name;
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
} }
} );
QTimerList.erase(e); QTimerList.erase(e);
break; break;
@ -847,23 +741,16 @@ void QuestManager::stopalltimers()
return; return;
} }
const bool has_stop_event = (
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == owner) { if (e->mob && e->mob == owner) {
if (has_stop_event) { parse->EventMob(
if (owner->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), e->name, 0); owner,
} else if (owner->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (owner->IsNPC()) { return e->name;
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, e->name, 0);
} }
} );
e = QTimerList.erase(e); e = QTimerList.erase(e);
} else { } else {
@ -903,23 +790,16 @@ void QuestManager::stopalltimers(Mob* m)
return; return;
} }
const bool has_stop_event = (
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == m) { if (e->mob && e->mob == m) {
if (has_stop_event) { parse->EventMob(
if (m->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); m,
} else if (m->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (m->IsNPC()) { return e->name;
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
} }
} );
e = QTimerList.erase(e); e = QTimerList.erase(e);
} else { } else {
@ -955,12 +835,6 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m)
uint32 milliseconds = 0; uint32 milliseconds = 0;
const bool has_pause_event = (
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_PAUSE)) ||
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_PAUSE)) ||
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_PAUSE))
);
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
if (e->mob && e->mob == mob && e->name == timer_name) { if (e->mob && e->mob == mob && e->name == timer_name) {
@ -979,21 +853,18 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m)
} }
); );
if (has_pause_event) { parse->EventMob(
const std::string& export_string = fmt::format( EVENT_TIMER_PAUSE,
"{} {}", mob,
timer_name, nullptr,
milliseconds [&]() {
); return fmt::format(
"{} {}",
if (mob->IsClient()) { timer_name,
parse->EventPlayer(EVENT_TIMER_PAUSE, mob->CastToClient(), export_string, 0); milliseconds
} else if (mob->IsBot()) { );
parse->EventBot(EVENT_TIMER_PAUSE, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_PAUSE, mob->CastToNPC(), nullptr, export_string, 0);
} }
} );
LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds);
} }
@ -1031,11 +902,13 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
return; return;
} }
const bool has_resume_event = ( std::function<std::string()> f = [&]() {
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_RESUME)) || return fmt::format(
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_RESUME)) || "{} {}",
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_RESUME)) timer_name,
); milliseconds
);
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto e : QTimerList) { for (auto e : QTimerList) {
@ -1049,21 +922,8 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
milliseconds milliseconds
); );
if (has_resume_event) { parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
} }
@ -1071,21 +931,7 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_resume_event) { parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0);
}
}
LogQuests( LogQuests(
"Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining", "Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining",
@ -4213,6 +4059,15 @@ Bot *QuestManager::GetBot() const {
return nullptr; return nullptr;
} }
Merc *QuestManager::GetMerc() const {
if (!quests_running_.empty()) {
running_quest e = quests_running_.top();
return (e.owner && e.owner->IsMerc()) ? e.owner->CastToMerc() : nullptr;
}
return nullptr;
}
Mob *QuestManager::GetOwner() const { Mob *QuestManager::GetOwner() const {
if(!quests_running_.empty()) { if(!quests_running_.empty()) {
running_quest e = quests_running_.top(); running_quest e = quests_running_.top();

View File

@ -360,6 +360,7 @@ public:
Bot *GetBot() const; Bot *GetBot() const;
Client *GetInitiator() const; Client *GetInitiator() const;
Merc* GetMerc() const;
NPC *GetNPC() const; NPC *GetNPC() const;
Mob *GetOwner() const; Mob *GetOwner() const;
EQ::InventoryProfile* GetInventory() const; EQ::InventoryProfile* GetInventory() const;

View File

@ -254,53 +254,33 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
} }
} }
if (IsClient()) { Mob* spell_target = entity_list.GetMobID(target_id);
if (parse->PlayerHasQuestSub(EVENT_CAST_BEGIN)) { std::vector<std::any> args = { spell_target };
Mob* spell_target = entity_list.GetMobID(target_id); int return_value = parse->EventMob(
std::vector<std::any> args = { spell_target }; EVENT_CAST_BEGIN,
const auto& export_string = fmt::format( this,
nullptr,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
GetCasterLevel(spell_id), GetCasterLevel(spell_id),
target_id target_id
); );
if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0, &args) != 0) { },
if (IsDiscipline(spell_id)) { 0,
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); &args
} );
else {
CastToClient()->SendSpellBarEnable(spell_id); if (IsClient() && return_value != 0) {
} if (IsDiscipline(spell_id)) {
return false; CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0);
} } else {
} CastToClient()->SendSpellBarEnable(spell_id);
} else if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0, &args);
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventBot(EVENT_CAST_BEGIN, CastToBot(), nullptr, export_string, 0, &args);
} }
return false;
} }
//To prevent NPC ghosting when spells are cast from scripts //To prevent NPC ghosting when spells are cast from scripts
@ -1822,47 +1802,24 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
} }
} }
// std::vector<std::any> args = { spell_target };
// at this point the spell has successfully been cast
//
if (IsClient()) { parse->EventMob(
if (parse->PlayerHasQuestSub(EVENT_CAST)) { EVENT_CAST,
std::vector<std::any> args = { spell_target }; this,
const auto& export_string = fmt::format( nullptr,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
GetCasterLevel(spell_id), GetCasterLevel(spell_id),
target_id target_id
); );
parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0, &args); },
} 0,
} else if (IsNPC()) { &args
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST)) { );
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0, &args);
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST)) {
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventBot(EVENT_CAST, CastToBot(), nullptr, export_string, 0, &args);
}
}
if(bard_song_mode) if(bard_song_mode)
{ {
@ -3622,44 +3579,18 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
); );
} }
const bool caster_has_block_event = ( std::function<std::string()> f = [&]() {
(caster->IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) || return fmt::format(
(caster->IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(caster->IsNPC() && parse->HasQuestSub(caster->GetNPCTypeID(), EVENT_SPELL_BLOCKED))
);
const bool cast_on_has_block_event = (
(IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_SPELL_BLOCKED))
);
if (caster_has_block_event || cast_on_has_block_event) {
const std::string& export_string = fmt::format(
"{} {}", "{} {}",
curbuf.spellid, curbuf.spellid,
spell_id spell_id
); );
};
if (caster_has_block_event) { parse->EventMob(EVENT_SPELL_BLOCKED, caster, this, f);
if (caster->IsBot()) {
parse->EventBot(EVENT_SPELL_BLOCKED, caster->CastToBot(), this, export_string, 0);
} else if (caster->IsClient()) {
parse->EventPlayer(EVENT_SPELL_BLOCKED, caster->CastToClient(), export_string, 0);
} else if (caster->IsNPC()) {
parse->EventNPC(EVENT_SPELL_BLOCKED, caster->CastToNPC(), this, export_string, 0);
}
}
if (cast_on_has_block_event && caster != this) { if (caster != this) {
if (IsBot()) { parse->EventMob(EVENT_SPELL_BLOCKED, this, caster, f);
parse->EventBot(EVENT_SPELL_BLOCKED, CastToBot(), caster, export_string, 0);
} else if (IsClient()) {
parse->EventPlayer(EVENT_SPELL_BLOCKED, CastToClient(), export_string, 0);
} else if (IsNPC()) {
parse->EventNPC(EVENT_SPELL_BLOCKED, CastToNPC(), caster, export_string, 0);
}
}
} }
} }
@ -4033,43 +3964,24 @@ bool Mob::SpellOnTarget(
(spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */ (spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */
); );
if (spelltar->IsNPC()) { std::vector<std::any> args = { spelltar };
if (parse->HasQuestSub(spelltar->GetNPCTypeID(), EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar }; parse->EventMob(
const auto& export_string = fmt::format( EVENT_CAST_ON,
spelltar,
this,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
caster_level, caster_level,
target_id target_id
); );
parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0, &args); },
} 0,
} else if (spelltar->IsClient()) { &args
if (parse->PlayerHasQuestSub(EVENT_CAST_ON)) { );
std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
caster_level,
target_id
);
parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0, &args);
}
} else if (spelltar->IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
caster_level,
target_id
);
parse->EventBot(EVENT_CAST_ON, spelltar->CastToBot(), this, export_string, 0, &args);
}
}
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet); safe_delete(action_packet);