[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_item.cpp
lua_iteminst.cpp
lua_merc.cpp
lua_mob.cpp
lua_mod.cpp
lua_npc.cpp
@ -115,6 +116,7 @@ SET(zone_sources
perl_groups.cpp
perl_hateentry.cpp
perl_inventory.cpp
perl_merc.cpp
perl_mob.cpp
perl_npc.cpp
perl_object.cpp
@ -224,6 +226,7 @@ SET(zone_headers
lua_inventory.h
lua_item.h
lua_iteminst.h
lua_merc.h
lua_mob.h
lua_mod.h
lua_npc.h

View File

@ -1550,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
hit.damage_done = 0;
}
if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) {
const auto& export_string = fmt::format(
parse->EventBotMerc(
EVENT_USE_SKILL,
this,
nullptr,
[&]() {
return fmt::format(
"{} {}",
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->IsNPC()) {
if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) {
parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0);
}
parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this);
if (killer_mob->IsNPC()) {
killed_by = KilledByTypes::Killed_NPC;
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->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);
}
@ -2458,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
spell_id = SPELL_UNKNOWN;
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if (attacked_timer.Check())
{
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);
}
if (attacked_timer.Check()) {
parse->EventMercNPC(EVENT_ATTACK, this, other);
}
attacked_timer.Start(CombatEventTimer_expire);
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;
if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
auto exports = [&]() {
return fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
};
if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
return false;
}
if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
} 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) {
@ -3077,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
}
if (killer_mob && killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) {
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
if (killer_mob) {
parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this);
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)) {
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 };
std::vector<std::any> args = { corpse };
parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args);
}
parse->EventMercNPC(
EVENT_DEATH_COMPLETE,
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)
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.
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;
int64 damage_override = 0;
if (has_given_event && attacker) {
const auto export_string = fmt::format(
"{} {} {} {} {} {} {} {} {}",
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) {
args = { this };
if (attacker->IsBot() && has_bot_given_event) {
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0);
} else if (attacker->IsClient() && has_player_given_event) {
args.push_back(this);
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args);
} else if (attacker->IsNPC() && has_npc_given_event) {
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0);
}
parse->EventMob(
EVENT_DAMAGE_GIVEN,
attacker,
this,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}",
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) {
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)
);
args = { attacker };
if (IsBot() && has_bot_taken_event) {
damage_override = parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0);
} else if (IsClient() && has_player_taken_event) {
args.push_back(attacker ? attacker : nullptr);
damage_override = parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
} else if (IsNPC() && has_npc_taken_event) {
damage_override = parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
}
}
damage_override = parse->EventMob(
EVENT_DAMAGE_TAKEN,
this,
attacker,
[&]() {
return 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)
);
},
0,
&args
);
if (damage_override > 0) {
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);
}
Mob* t = GetTarget();
if (
GetTarget() &&
GetTarget()->IsNPC() &&
!IsInvisible(GetTarget())
t &&
!IsInvisible(t) &&
DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)
) {
auto* t = GetTarget()->CastToNPC();
if (!t->IsEngaged()) {
CheckLDoNHail(t);
CheckEmoteHail(t, message);
const bool is_engaged = t->IsEngaged();
if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) {
parse->EventNPC(EVENT_SAY, t, this, message, language);
}
if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(t)) {
t->DoQuestPause(this);
}
}
}
if (is_engaged) {
parse->EventBotMercNPC(EVENT_AGGRO_SAY, t, this, [&]() { return message; }, language);
} else {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) {
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
}
}
parse->EventBotMercNPC(EVENT_SAY, t, this, [&]() { return message; }, language);
}
}
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) {
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
if (GetTarget()->IsEngaged()) {
if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) {
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language);
}
} else {
if (parse->BotHasQuestSub(EVENT_SAY)) {
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
if (t->IsNPC() && !is_engaged) {
CheckLDoNHail(t->CastToNPC());
CheckEmoteHail(t->CastToNPC(), message);
if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(t->CastToNPC())) {
t->CastToNPC()->DoQuestPause(this);
}
}
}

View File

@ -11984,15 +11984,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
auto t = GetTarget();
if (t) {
if (t->IsNPC()) {
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);
}
}
parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); });
}
}

View File

@ -57,6 +57,7 @@ void perl_register_expedition();
void perl_register_expedition_lock_messages();
void perl_register_bot();
void perl_register_buff();
void perl_register_merc();
#endif // EMBPERL_XS_CLASSES
#endif // EMBPERL_XS
@ -217,6 +218,8 @@ PerlembParser::PerlembParser() : perl(nullptr)
global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
}
PerlembParser::~PerlembParser()
@ -258,6 +261,8 @@ void PerlembParser::ReloadQuests()
global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
item_quest_status_.clear();
spell_quest_status_.clear();
@ -285,6 +290,8 @@ int PerlembParser::EventCommon(
bool is_global_npc_quest = false;
bool is_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_spell_quest = false;
@ -295,6 +302,8 @@ int PerlembParser::EventCommon(
is_global_player_quest,
is_bot_quest,
is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest,
is_item_quest,
is_spell_quest,
@ -310,6 +319,8 @@ int PerlembParser::EventCommon(
is_global_player_quest,
is_bot_quest,
is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest,
is_item_quest,
is_spell_quest,
@ -339,6 +350,8 @@ int PerlembParser::EventCommon(
is_global_player_quest,
is_bot_quest,
is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest,
is_item_quest,
is_spell_quest,
@ -356,6 +369,8 @@ int PerlembParser::EventCommon(
is_global_player_quest,
is_bot_quest,
is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest,
is_item_quest,
is_spell_quest,
@ -382,7 +397,7 @@ int PerlembParser::EventCommon(
if (is_player_quest || is_global_player_quest) {
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);
} else if (is_item_quest) {
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
dTHX;
{
std::string cl = fmt::format("${}::client", prefix);
std::string np = fmt::format("${}::npc", prefix);
std::string qi = fmt::format("${}::questitem", prefix);
std::string sp = fmt::format("${}::spell", prefix);
std::string enl = fmt::format("${}::entity_list", prefix);
std::string bot = fmt::format("${}::bot", prefix);
const std::vector<std::string>& suffixes = {
"bot",
"client",
"entity_list",
"merc",
"npc",
"questitem",
"spell"
};
if (clear_vars_.find(cl) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", cl);
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());
for (const auto& suffix : suffixes) {
const std::string& key = fmt::format("${}::{}", prefix, suffix);
if (clear_vars_.find(suffix) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", key);
perl->eval(e.c_str());
}
}
}
@ -1060,19 +1056,21 @@ int PerlembParser::SendCommands(
sv_setsv(client, _empty_sv);
}
//only export NPC if it's a npc quest
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()) {
if (other->IsBot()) {
Bot* b = quest_manager.GetBot();
buf = fmt::format("{}::bot", prefix);
SV* bot = get_sv(buf.c_str(), true);
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
@ -1098,23 +1096,25 @@ int PerlembParser::SendCommands(
#endif
//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
{
std::string cl = fmt::format("${}::client", prefix);
std::string np = fmt::format("${}::npc", prefix);
std::string qi = fmt::format("${}::questitem", prefix);
std::string sp = fmt::format("${}::spell", prefix);
std::string enl = fmt::format("${}::entity_list", prefix);
std::string bot = fmt::format("${}::bot", prefix);
const std::vector<std::string>& suffixes = {
"bot",
"client",
"entity_list",
"merc",
"npc",
"questitem",
"spell"
};
clear_vars_[cl] = 1;
clear_vars_[np] = 1;
clear_vars_[qi] = 1;
clear_vars_[sp] = 1;
clear_vars_[enl] = 1;
clear_vars_[bot] = 1;
for (const auto& suffix : suffixes) {
const std::string& key = fmt::format("${}::{}", prefix, suffix);
clear_vars_[key] = 1;
}
}
#endif
@ -1184,6 +1184,7 @@ void PerlembParser::MapFunctions()
perl_register_expedition_lock_messages();
perl_register_bot();
perl_register_buff();
perl_register_merc();
#endif // EMBPERL_XS_CLASSES
}
@ -1192,6 +1193,8 @@ void PerlembParser::GetQuestTypes(
bool& is_global_player_quest,
bool& is_bot_quest,
bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest,
bool& is_item_quest,
bool& is_spell_quest,
@ -1219,10 +1222,14 @@ void PerlembParser::GetQuestTypes(
if (is_global) {
if (npc_mob->IsBot()) {
is_global_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_global_merc_quest = true;
}
} else {
if (npc_mob->IsBot()) {
is_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_merc_quest = true;
}
}
} else {
@ -1251,6 +1258,8 @@ void PerlembParser::GetQuestPackageName(
bool& is_global_player_quest,
bool& is_bot_quest,
bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest,
bool& is_item_quest,
bool& is_spell_quest,
@ -1268,6 +1277,8 @@ void PerlembParser::GetQuestPackageName(
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
@ -1291,6 +1302,10 @@ void PerlembParser::GetQuestPackageName(
package_name = "qst_bot";
} else if (is_global_bot_quest) {
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 {
package_name = fmt::format("qst_spell_{}", object_id);
}
@ -1316,6 +1331,8 @@ void PerlembParser::ExportQGlobals(
bool is_global_player_quest,
bool is_bot_quest,
bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest,
bool is_item_quest,
bool is_spell_quest,
@ -1331,6 +1348,8 @@ void PerlembParser::ExportQGlobals(
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
@ -1466,6 +1485,8 @@ void PerlembParser::ExportMobVariables(
bool is_global_player_quest,
bool is_bot_quest,
bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest,
bool is_item_quest,
bool is_spell_quest,
@ -1491,6 +1512,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest
) {
if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) {
@ -1521,6 +1544,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
@ -2081,7 +2106,8 @@ void PerlembParser::ExportEventVariables(
"killed_bot_id",
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;
@ -2357,6 +2383,7 @@ void PerlembParser::ExportEventVariables(
case EVENT_DESPAWN: {
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_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);
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

View File

@ -118,6 +118,24 @@ public:
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 HasGlobalQuestSub(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 BotHasQuestSub(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 LoadGlobalNPCScript(std::string filename);
@ -135,6 +155,8 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id);
virtual void LoadBotScript(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 std::string GetVar(std::string name);
@ -182,6 +204,8 @@ private:
bool& is_global_player_quest,
bool& is_bot_quest,
bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest,
bool& is_item_quest,
bool& is_spell_quest,
@ -197,6 +221,8 @@ private:
bool& is_global_player_quest,
bool& is_bot_quest,
bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest,
bool& is_item_quest,
bool& is_spell_quest,
@ -216,6 +242,8 @@ private:
bool is_global_player_quest,
bool is_bot_quest,
bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest,
bool is_item_quest,
bool is_spell_quest,
@ -230,6 +258,8 @@ private:
bool is_global_player_quest,
bool is_bot_quest,
bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest,
bool is_item_quest,
bool is_spell_quest,
@ -263,6 +293,8 @@ private:
PerlQuestStatus global_player_quest_status_;
PerlQuestStatus bot_quest_status_;
PerlQuestStatus global_bot_quest_status_;
PerlQuestStatus merc_quest_status_;
PerlQuestStatus global_merc_quest_status_;
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));
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_bot.h"
#include "lua_merc.h"
bool Lua_Entity::IsClient() {
Lua_Safe_Call_Bool();
@ -140,6 +141,12 @@ Lua_Bot Lua_Entity::CastToBot() {
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() {
return luabind::class_<Lua_Entity>("Entity")
.def(luabind::constructor<>())
@ -149,6 +156,7 @@ luabind::scope lua_register_entity() {
.def("CastToClient", &Lua_Entity::CastToClient)
.def("CastToCorpse", &Lua_Entity::CastToCorpse)
.def("CastToDoor", &Lua_Entity::CastToDoor)
.def("CastToMerc", &Lua_Entity::CastToMerc)
.def("CastToMob", &Lua_Entity::CastToMob)
.def("CastToNPC", &Lua_Entity::CastToNPC)
.def("CastToObject", &Lua_Entity::CastToObject)

View File

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

View File

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

View File

@ -8,6 +8,7 @@ class EntityList;
class Lua_Mob;
class Lua_Client;
class Lua_Bot;
class Lua_Merc;
class Lua_NPC;
class Lua_Door;
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_StatBonuses;
class Lua_Bot;
class Lua_Merc;
class Lua_NPC;
class Lua_Client;
struct Lua_Mob_List;

View File

@ -32,6 +32,7 @@
#include "lua_inventory.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_merc.h"
#include "lua_mob.h"
#include "lua_npc.h"
#include "lua_object.h"
@ -1279,6 +1280,7 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_npc(),
lua_register_client(),
lua_register_bot(),
lua_register_merc(),
lua_register_inventory(),
lua_register_inventory_where(),
lua_register_iteminst(),
@ -1835,3 +1837,190 @@ void LuaParser::LoadBotScript(std::string filename) {
void LuaParser::LoadGlobalBotScript(std::string filename) {
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,
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 HasGlobalQuestSub(QuestEventID evt);
@ -120,6 +136,8 @@ public:
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt);
virtual bool BotHasQuestSub(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 LoadGlobalNPCScript(std::string filename);
@ -130,6 +148,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name);
virtual void LoadBotScript(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 std::string GetVar(std::string name);
@ -179,6 +199,14 @@ public:
uint32 extra_data,
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 inst;
@ -269,6 +297,16 @@ private:
std::vector<std::any> *extra_pointers,
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 MapFunctions(lua_State *L);

View File

@ -734,6 +734,18 @@ void handle_player_death(
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4]));
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(

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(*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(*MercArgumentHandler)(QuestInterface*, lua_State*, Merc*, Mob*, std::string, uint32, std::vector<std::any>*);
// NPC
void handle_npc_event_say(

View File

@ -5,6 +5,7 @@
#include "entity.h"
#include "groups.h"
#include "mob.h"
#include "quest_parser_collection.h"
#include "zone.h"
#include "string_ids.h"
@ -4078,12 +4079,6 @@ bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillT
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 (!Suspend()) {
Depop();
@ -4671,7 +4666,6 @@ bool Merc::Spawn(Client *owner) {
//UpdateMercAppearance();
return true;
}
@ -5189,9 +5183,6 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetStance(GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
return;
}
bool Merc::Suspend() {
@ -5914,3 +5905,19 @@ uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type
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 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 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0);

View File

@ -5514,37 +5514,13 @@ void Mob::SetTarget(Mob *mob)
target = mob;
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) {
DisplayInfo(mob);
}
if (has_target_change_event) {
std::vector<std::any> args;
std::vector<std::any> args = { mob };
args.emplace_back(mob);
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);
}
}
}
parse->EventMob(EVENT_TARGET_CHANGE, this, mob, [&]() { return ""; }, 0, &args);
if (IsPet() && GetOwner() && GetOwner()->IsClient()) {
GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob);
@ -5717,22 +5693,10 @@ bool Mob::ClearEntityVariables()
return false;
}
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(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 };
for (const auto& e : m_EntityVariables) {
std::vector<std::any> args = { e.first, e.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);
}
}
parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
}
m_EntityVariables.clear();
@ -5750,24 +5714,11 @@ bool Mob::DeleteEntityVariable(std::string variable_name)
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);
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;
}
@ -5814,32 +5765,16 @@ void Mob::SetEntityVariable(std::string variable_name, std::string variable_valu
return;
}
const QuestEventID event_id = (
!EntityVariableExists(variable_name) ?
EVENT_ENTITY_VARIABLE_SET :
EVENT_ENTITY_VARIABLE_UPDATE
);
std::vector<std::any> args;
if (
(IsBot() && parse->BotHasQuestSub(event_id)) ||
(IsClient() && parse->PlayerHasQuestSub(event_id)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), event_id))
) {
std::vector<std::any> args;
if (!EntityVariableExists(variable_name)) {
args = { variable_name, variable_value };
if (event_id != EVENT_ENTITY_VARIABLE_UPDATE) {
args = { variable_name, variable_value };
} else {
args = { variable_name, GetEntityVariable(variable_name), variable_value };
}
parse->EventMob(EVENT_ENTITY_VARIABLE_SET, this, nullptr, [&]() { return ""; }, 0, &args);
} else {
args = { variable_name, GetEntityVariable(variable_name), variable_value };
if (IsBot()) {
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);
}
parse->EventMob(EVENT_ENTITY_VARIABLE_UPDATE, this, nullptr, [&]() { return ""; }, 0, &args);
}
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);
parse->EventBotMerc(EVENT_COMBAT, this, attacker, [&] { return "1"; });
if (IsNPC()) {
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
@ -1819,27 +1815,19 @@ void Mob::AI_Event_NoLongerEngaged() {
StopNavigation();
ClearRampage();
parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; });
if (IsNPC()) {
SetPrimaryAggro(false);
SetAssistAggro(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(GetID())) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) {
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
}
const uint32 emote_id = CastToNPC()->GetEmoteID();
if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
}
m_combat_record.Stop();
CastToNPC()->SetCombatEvent(false);
const uint32 emote_id = CastToNPC()->GetEmoteID();
if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
}
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0);
m_combat_record.Stop();
CastToNPC()->SetCombatEvent(false);
}
}
}

View File

@ -833,22 +833,10 @@ void NPC::Depop(bool start_spawn_timer) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid);
}
if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DESPAWN)) {
parse->EventNPC(EVENT_DESPAWN, this, nullptr, "", 0);
}
parse->EventBotMercNPC(EVENT_DESPAWN, this, nullptr);
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
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);
}
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr);
}
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);
}
Merc* Perl_Client_GetMerc(Client* self)
{
return self->GetMerc();
}
void perl_register_client()
{
perl::interpreter perl(PERL_GET_THX);
@ -3438,6 +3443,7 @@ void perl_register_client()
package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines);
package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID);
package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance);
package.add("GetMerc", &Perl_Client_GetMerc);
package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells);
package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel);
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;
}
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)
{
return false;
@ -189,6 +213,16 @@ public:
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 LoadGlobalNPCScript(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 LoadBotScript(std::string filename) { }
virtual void LoadGlobalBotScript(std::string filename) { }
virtual void LoadMercScript(std::string filename) { }
virtual void LoadGlobalMercScript(std::string filename) { }
virtual int DispatchEventNPC(
QuestEventID event_id,
@ -260,6 +296,18 @@ public:
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 std::string GetVar(std::string name)
{

View File

@ -47,6 +47,8 @@ QuestParserCollection::QuestParserCollection()
_global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
}
QuestParserCollection::~QuestParserCollection() { }
@ -94,6 +96,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers)
_global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
_spell_quest_status.clear();
_item_quest_status.clear();
@ -379,6 +383,49 @@ bool QuestParserCollection::BotHasQuestSub(QuestEventID 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(
QuestEventID event_id,
NPC* npc,
@ -793,6 +840,86 @@ int QuestParserCollection::EventBotGlobal(
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)
{
if (!zone) {
@ -1188,6 +1315,81 @@ QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filena
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)
{
quest_errors.clear();
@ -1303,6 +1505,27 @@ int QuestParserCollection::DispatchEventBot(
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)
{
for (int i = 0; i < _LargestEventID; i++) {
@ -1325,3 +1548,81 @@ void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings*
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 ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
bool BotHasQuestSub(QuestEventID event_id);
bool MercHasQuestSub(QuestEventID event_id);
int EventNPC(
QuestEventID event_id,
@ -126,6 +127,51 @@ public:
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);
/*
@ -161,6 +207,8 @@ private:
bool HasEncounterSub(QuestEventID event_id, const std::string& package_name);
bool BotHasQuestSubLocal(QuestEventID event_id);
bool BotHasQuestSubGlobal(QuestEventID event_id);
bool MercHasQuestSubLocal(QuestEventID event_id);
bool MercHasQuestSubGlobal(QuestEventID event_id);
int EventNPCLocal(
QuestEventID event_id,
@ -214,6 +262,24 @@ private:
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* GetQIByGlobalNPCQuest(std::string& filename);
QuestInterface* GetQIByPlayerQuest(std::string& filename);
@ -223,6 +289,8 @@ private:
QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename);
QuestInterface* GetQIByBotQuest(std::string& filename);
QuestInterface* GetQIByGlobalBotQuest(std::string& filename);
QuestInterface* GetQIByMercQuest(std::string& filename);
QuestInterface* GetQIByGlobalMercQuest(std::string& filename);
int DispatchEventNPC(
QuestEventID event_id,
@ -270,6 +338,15 @@ private:
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, std::string> _extensions;
std::list<QuestInterface*> _load_precedence;
@ -280,6 +357,8 @@ private:
uint32 _global_player_quest_status;
uint32 _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> _item_quest_status;
std::map<std::string, uint32> _encounter_quest_status;

View File

@ -90,12 +90,9 @@ void QuestManager::Process() {
while (cur != end) {
if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
if (cur->mob) {
if (cur->mob->IsNPC()) {
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
}
}
else if (cur->mob->IsEncounter()) {
parse->EventMob(EVENT_TIMER, cur->mob, nullptr, [&]() { return cur->name; }, 0);
if (cur->mob->IsEncounter()) {
parse->EventEncounter(
EVENT_TIMER,
cur->mob->CastToEncounter()->GetEncounterName(),
@ -104,17 +101,6 @@ void QuestManager::Process() {
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
//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;
}
const bool has_start_event = (
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) ||
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) ||
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_START))
);
std::function<std::string()> f = [&]() {
return fmt::format(
"{} {}",
timer_name,
seconds * 1000
);
};
if (!QTimerList.empty()) {
for (auto& e : QTimerList) {
if (e.mob && e.mob == mob && e.name == timer_name) {
e.Timer_.Start(seconds * 1000, false);
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
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));
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
}
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;
}
const bool has_start_event = (
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) ||
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) ||
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_START))
);
std::function<std::string()> f = [&]() {
return fmt::format(
"{} {}",
timer_name,
milliseconds
);
};
if (questitem) {
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) {
e.Timer_.Start(milliseconds, false);
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
return;
}
@ -648,21 +596,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name));
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
}
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;
}
const bool has_start_event = (
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) ||
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) ||
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_START))
);
std::function<std::string()> f = [&]() {
return fmt::format(
"{} {}",
timer_name,
milliseconds
);
};
if (!QTimerList.empty()) {
for (auto& e : QTimerList) {
if (e.mob && e.mob == m && e.name == timer_name) {
e.Timer_.Start(milliseconds, false);
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
return;
}
@ -712,21 +634,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_start_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
}
void QuestManager::stoptimer(const std::string& timer_name)
@ -751,23 +659,16 @@ void QuestManager::stoptimer(const std::string& timer_name)
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) {
if (e->mob && e->mob == owner && e->name == timer_name) {
if (has_stop_event) {
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, timer_name, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, timer_name, 0);
parse->EventMob(
EVENT_TIMER_STOP,
owner,
nullptr,
[&]() {
return timer_name;
}
}
);
QTimerList.erase(e);
break;
@ -792,23 +693,16 @@ void QuestManager::stoptimer(const std::string& timer_name, Mob* m)
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();) {
if (e->mob && e->mob == m) {
if (has_stop_event) {
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
parse->EventMob(
EVENT_TIMER_STOP,
m,
nullptr,
[&]() {
return timer_name;
}
}
);
QTimerList.erase(e);
break;
@ -847,23 +741,16 @@ void QuestManager::stopalltimers()
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();) {
if (e->mob && e->mob == owner) {
if (has_stop_event) {
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), e->name, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, e->name, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, e->name, 0);
parse->EventMob(
EVENT_TIMER_STOP,
owner,
nullptr,
[&]() {
return e->name;
}
}
);
e = QTimerList.erase(e);
} else {
@ -903,23 +790,16 @@ void QuestManager::stopalltimers(Mob* m)
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();) {
if (e->mob && e->mob == m) {
if (has_stop_event) {
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
parse->EventMob(
EVENT_TIMER_STOP,
m,
nullptr,
[&]() {
return e->name;
}
}
);
e = QTimerList.erase(e);
} else {
@ -955,12 +835,6 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m)
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()) {
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
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) {
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_PAUSE, mob->CastToClient(), export_string, 0);
} 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);
parse->EventMob(
EVENT_TIMER_PAUSE,
mob,
nullptr,
[&]() {
return fmt::format(
"{} {}",
timer_name,
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;
}
const bool has_resume_event = (
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_RESUME)) ||
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_RESUME)) ||
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_RESUME))
);
std::function<std::string()> f = [&]() {
return fmt::format(
"{} {}",
timer_name,
milliseconds
);
};
if (!QTimerList.empty()) {
for (auto e : QTimerList) {
@ -1049,21 +922,8 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
milliseconds
);
if (has_resume_event) {
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
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;
}
}
@ -1071,21 +931,7 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_resume_event) {
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);
}
}
parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
LogQuests(
"Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining",
@ -4213,6 +4059,15 @@ Bot *QuestManager::GetBot() const {
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 {
if(!quests_running_.empty()) {
running_quest e = quests_running_.top();

View File

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

View File

@ -254,53 +254,33 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
}
}
if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
int return_value = parse->EventMob(
EVENT_CAST_BEGIN,
this,
nullptr,
[&]() {
return fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0, &args) != 0) {
if (IsDiscipline(spell_id)) {
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0);
}
else {
CastToClient()->SendSpellBarEnable(spell_id);
}
return false;
}
}
} 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);
},
0,
&args
);
if (IsClient() && return_value != 0) {
if (IsDiscipline(spell_id)) {
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0);
} else {
CastToClient()->SendSpellBarEnable(spell_id);
}
return false;
}
//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
}
}
//
// at this point the spell has successfully been cast
//
std::vector<std::any> args = { spell_target };
if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_CAST)) {
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
parse->EventMob(
EVENT_CAST,
this,
nullptr,
[&]() {
return fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0, &args);
}
} else if (IsNPC()) {
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);
}
}
},
0,
&args
);
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 = (
(caster->IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(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(
std::function<std::string()> f = [&]() {
return fmt::format(
"{} {}",
curbuf.spellid,
spell_id
);
};
if (caster_has_block_event) {
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);
}
}
parse->EventMob(EVENT_SPELL_BLOCKED, caster, this, f);
if (cast_on_has_block_event && caster != this) {
if (IsBot()) {
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);
}
}
if (caster != this) {
parse->EventMob(EVENT_SPELL_BLOCKED, this, caster, f);
}
}
@ -4033,43 +3964,24 @@ bool Mob::SpellOnTarget(
(spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */
);
if (spelltar->IsNPC()) {
if (parse->HasQuestSub(spelltar->GetNPCTypeID(), EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
std::vector<std::any> args = { spelltar };
parse->EventMob(
EVENT_CAST_ON,
spelltar,
this,
[&]() {
return fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
caster_level,
target_id
);
parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0, &args);
}
} else if (spelltar->IsClient()) {
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);
}
}
},
0,
&args
);
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet);