[Feature] Add Zone Scripting Capabilities

This commit is contained in:
Kinglykrab 2025-05-22 23:41:47 -04:00
parent f7775c7a75
commit 54c53ce389
8 changed files with 823 additions and 225 deletions

View File

@ -278,6 +278,7 @@ int PerlembParser::EventCommon(
EQ::ItemInstance* inst,
const SPDat_Spell_Struct* spell,
Mob* mob,
Zone* zone,
uint32 extra_data,
bool is_global,
std::vector<std::any>* extra_pointers
@ -287,52 +288,22 @@ int PerlembParser::EventCommon(
return 0;
}
bool is_player_quest = false;
bool is_global_player_quest = false;
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;
std::string package_name;
GetQuestTypes(
is_player_quest,
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,
QuestType quest_type = GetQuestTypes(
event_id,
npc_mob,
inst,
mob,
zone,
is_global
);
GetQuestPackageName(
is_player_quest,
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,
package_name,
std::string package_name = GetQuestPackageName(
quest_type,
event_id,
object_id,
data,
npc_mob,
inst,
is_global
inst
);
const std::string& sub_name = QuestEventSubroutines[event_id];
@ -348,15 +319,7 @@ int PerlembParser::EventCommon(
/* Check for QGlobal export event enable */
if (parse->perl_event_export_settings[event_id].qglobals) {
ExportQGlobals(
is_player_quest,
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,
quest_type,
package_name,
npc_mob,
mob,
@ -367,15 +330,7 @@ int PerlembParser::EventCommon(
/* Check for Mob export event enable */
if (parse->perl_event_export_settings[event_id].mob) {
ExportMobVariables(
is_player_quest,
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,
quest_type,
package_name,
mob,
npc_mob
@ -397,19 +352,24 @@ int PerlembParser::EventCommon(
ExportEventVariables(package_name, event_id, object_id, data, npc_mob, inst, mob, extra_data, extra_pointers);
}
if (is_player_quest || is_global_player_quest) {
if (quest_type == QuestType::Player || quest_type == QuestType::PlayerGlobal) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr);
} else if (is_bot_quest || is_global_bot_quest || is_merc_quest || is_global_merc_quest) {
} else if (
quest_type == QuestType::Bot ||
quest_type == QuestType::BotGlobal ||
quest_type == QuestType::Merc ||
quest_type == QuestType::MercGlobal
) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr);
} else if (is_item_quest) {
} else if (quest_type == QuestType::Item || quest_type == QuestType::ItemGlobal) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr);
} else if (is_spell_quest) {
} else if (quest_type == QuestType::Spell || quest_type == QuestType::SpellGlobal) {
if (mob) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, spell);
} else {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, spell);
}
} else {
} else if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) {
return SendCommands(
package_name.c_str(),
QuestEventSubroutines[event_id],
@ -439,6 +399,7 @@ int PerlembParser::EventNPC(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
false,
extra_pointers
@ -462,6 +423,7 @@ int PerlembParser::EventGlobalNPC(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
true,
extra_pointers
@ -484,6 +446,7 @@ int PerlembParser::EventPlayer(
nullptr,
nullptr,
client,
nullptr,
extra_data,
false,
extra_pointers
@ -506,6 +469,7 @@ int PerlembParser::EventGlobalPlayer(
nullptr,
nullptr,
client,
nullptr,
extra_data,
true,
extra_pointers
@ -534,6 +498,7 @@ int PerlembParser::EventItem(
inst,
nullptr,
client,
nullptr,
extra_data,
false,
extra_pointers
@ -558,6 +523,7 @@ int PerlembParser::EventSpell(
nullptr,
&spells[spell_id],
client,
nullptr,
extra_data,
false,
extra_pointers
@ -1192,20 +1158,12 @@ void PerlembParser::MapFunctions()
#endif // EMBPERL_XS_CLASSES
}
void PerlembParser::GetQuestTypes(
bool& is_player_quest,
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,
QuestType PerlembParser::GetQuestTypes(
QuestEventID event_id,
Mob* npc_mob,
EQ::ItemInstance* inst,
Mob* mob,
Zone* zone,
bool is_global
)
{
@ -1219,100 +1177,72 @@ void PerlembParser::GetQuestTypes(
event_id == EVENT_SPELL_FADE ||
event_id == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE
) {
is_spell_quest = true;
return is_global ? QuestType::SpellGlobal : QuestType::Spell;
} else {
if (npc_mob) {
if (!inst) {
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;
}
if (npc_mob->IsBot()) {
return is_global ? QuestType::BotGlobal : QuestType::Bot;
} else if (npc_mob->IsMerc()) {
return is_global ? QuestType::MercGlobal : QuestType::Merc;
}
} else {
is_item_quest = true;
return is_global ? QuestType::ItemGlobal : QuestType::Item;
}
} else if (!npc_mob && mob) {
if (!inst) {
if (is_global) {
if (mob->IsClient()) {
is_global_player_quest = true;
}
} else {
if (mob->IsClient()) {
is_player_quest = true;
}
if (mob->IsClient()) {
return is_global ? QuestType::PlayerGlobal : QuestType::Player;
}
} else {
is_item_quest = true;
return is_global ? QuestType::ItemGlobal : QuestType::Item;
}
} else if (zone) {
return is_global ? QuestType::ZoneGlobal : QuestType::Zone;
}
}
}
void PerlembParser::GetQuestPackageName(
bool& is_player_quest,
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,
std::string& package_name,
std::string PerlembParser::GetQuestPackageName(
QuestType quest_type,
QuestEventID event_id,
uint32 object_id,
const char* data,
Mob* npc_mob,
EQ::ItemInstance* inst,
bool is_global
EQ::ItemInstance* inst
)
{
if (
!is_player_quest &&
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
if (is_global) {
is_global_npc_quest = true;
package_name = "qst_global_npc";
} else {
package_name = fmt::format("qst_npc_{}", npc_mob->GetNPCTypeID());
}
} else if (is_item_quest) {
if (quest_type == QuestType::NPC) {
return fmt::format("qst_npc_{}", npc_mob->GetNPCTypeID());
} else if (quest_type == QuestType::NPCGlobal) {
return "qst_global_npc";
} else if (quest_type == QuestType::Item || quest_type == QuestType::ItemGlobal) {
if (!inst) {
return;
return "";
}
package_name = fmt::format("qst_item_{}", inst->GetID());
} else if (is_player_quest) {
package_name = "qst_player";
} else if (is_global_player_quest) {
package_name = "qst_global_player";
} else if (is_bot_quest) {
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);
return fmt::format("qst_item_{}", inst->GetID());
} else if (quest_type == QuestType::Player) {
return "qst_player";
} else if (quest_type == QuestType::PlayerGlobal) {
return "qst_global_player";
} else if (quest_type == QuestType::Bot) {
return "qst_bot";
} else if (quest_type == QuestType::BotGlobal) {
return "qst_global_bot";
} else if (quest_type == QuestType::Merc) {
return "qst_merc";
} else if (quest_type == QuestType::MercGlobal) {
return "qst_global_merc";
} else if (quest_type == QuestType::Spell || quest_type == QuestType::SpellGlobal) {
return fmt::format("qst_spell_{}", object_id);
} else if (quest_type == QuestType::Zone) {
return "qst_zone";
} else if (quest_type == QuestType::ZoneGlobal) {
return "qst_global_zone";
}
return "";
}
void PerlembParser::ExportCharID(const std::string& package_name, int& char_id, Mob* npc_mob, Mob* mob)
@ -1331,15 +1261,7 @@ void PerlembParser::ExportCharID(const std::string& package_name, int& char_id,
}
void PerlembParser::ExportQGlobals(
bool is_player_quest,
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,
QuestType quest_type,
std::string& package_name,
Mob* npc_mob,
Mob* mob,
@ -1347,16 +1269,7 @@ void PerlembParser::ExportQGlobals(
)
{
//NPC quest
if (
!is_player_quest &&
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) {
//only export for npcs that are global enabled.
if (npc_mob && npc_mob->GetQglobal()) {
std::map<std::string, std::string> globhash;
@ -1485,15 +1398,7 @@ void PerlembParser::ExportQGlobals(
}
void PerlembParser::ExportMobVariables(
bool is_player_quest,
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,
QuestType quest_type,
std::string& package_name,
Mob* mob,
Mob* npc_mob
@ -1511,15 +1416,7 @@ void PerlembParser::ExportMobVariables(
ExportVar(package_name.c_str(), "bot_owner_char_id", mob->CastToBot()->GetBotOwnerCharacterID());
}
if (
!is_player_quest &&
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest
) {
if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) {
if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) {
Client* c = mob->CastToClient();
@ -1543,16 +1440,7 @@ void PerlembParser::ExportMobVariables(
ExportVar(package_name.c_str(), "userid", mob->GetID());
}
if (
!is_player_quest &&
!is_global_player_quest &&
!is_bot_quest &&
!is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest &&
!is_spell_quest
) {
if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) {
if (npc_mob->IsNPC()) {
ExportVar(package_name.c_str(), "mname", npc_mob->GetName());
ExportVar(package_name.c_str(), "mobid", npc_mob->GetID());
@ -2648,6 +2536,7 @@ int PerlembParser::EventBot(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
false,
extra_pointers
@ -2671,6 +2560,7 @@ int PerlembParser::EventGlobalBot(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
true,
extra_pointers
@ -2768,6 +2658,7 @@ int PerlembParser::EventMerc(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
false,
extra_pointers
@ -2791,6 +2682,127 @@ int PerlembParser::EventGlobalMerc(
nullptr,
nullptr,
mob,
nullptr,
extra_data,
true,
extra_pointers
);
}
void PerlembParser::LoadZoneScript(std::string filename)
{
if (!perl || zone_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_zone", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Zone Quest File [{}] Error [{}]",
filename,
e
)
);
zone_quest_status_ = questFailedToLoad;
return;
}
zone_quest_status_ = questLoaded;
}
void PerlembParser::LoadGlobalZoneScript(std::string filename)
{
if (!perl || global_zone_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_global_zone", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Global Zone uest File [{}] Error [{}]",
filename,
e
)
);
global_zone_quest_status_ = questFailedToLoad;
return;
}
global_zone_quest_status_ = questLoaded;
}
bool PerlembParser::ZoneHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
zone_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return perl->SubExists("qst_zone", QuestEventSubroutines[event_id]);
}
bool PerlembParser::GlobalZoneHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
global_zone_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return perl->SubExists("qst_global_zone", QuestEventSubroutines[event_id]);
}
int PerlembParser::EventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
nullptr,
nullptr,
nullptr,
nullptr,
zone,
extra_data,
false,
extra_pointers
);
}
int PerlembParser::EventGlobalZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
nullptr,
nullptr,
nullptr,
nullptr,
zone,
extra_data,
true,
extra_pointers

View File

@ -41,6 +41,23 @@ typedef enum {
questFailedToLoad
} PerlQuestStatus;
enum class QuestType {
Bot,
BotGlobal,
Item,
ItemGlobal,
Merc,
MercGlobal,
NPC,
NPCGlobal,
Player,
PlayerGlobal,
Spell,
SpellGlobal,
Zone,
ZoneGlobal
};
class PerlembParser : public QuestInterface {
public:
PerlembParser();
@ -136,6 +153,22 @@ public:
std::vector<std::any>* extra_pointers
);
virtual int EventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalZone(
QuestEventID event_id,
Zone* zone,
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);
@ -146,6 +179,8 @@ public:
virtual bool GlobalBotHasQuestSub(QuestEventID event_id);
virtual bool MercHasQuestSub(QuestEventID event_id);
virtual bool GlobalMercHasQuestSub(QuestEventID event_id);
virtual bool ZoneHasQuestSub(QuestEventID event_id);
virtual bool GlobalZoneHasQuestSub(QuestEventID event_id);
virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename);
@ -157,6 +192,8 @@ public:
virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void LoadZoneScript(std::string filename);
virtual void LoadGlobalZoneScript(std::string filename);
virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name);
@ -182,6 +219,7 @@ private:
EQ::ItemInstance* inst,
const SPDat_Spell_Struct* spell,
Mob* mob,
Zone* zone,
uint32 extra_data,
bool is_global,
std::vector<std::any>* extra_pointers
@ -199,54 +237,28 @@ private:
void MapFunctions();
void GetQuestTypes(
bool& is_player_quest,
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,
QuestType GetQuestTypes(
QuestEventID event,
Mob* npc_mob,
EQ::ItemInstance* inst,
Mob* mob,
Zone* zone,
bool is_global
);
void GetQuestPackageName(
bool& is_player_quest,
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,
std::string& package_name,
std::string GetQuestPackageName(
QuestType quest_type,
QuestEventID event,
uint32 object_id,
const char* data,
Mob* npc_mob,
EQ::ItemInstance* inst,
bool is_global
EQ::ItemInstance* inst
);
void ExportCharID(const std::string& package_name, int& char_id, Mob* npc_mob, Mob* mob);
void ExportQGlobals(
bool is_player_quest,
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,
QuestType quest_type,
std::string& package_name,
Mob* npc_mob,
Mob* mob,
@ -254,15 +266,7 @@ private:
);
void ExportMobVariables(
bool is_player_quest,
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,
QuestType quest_type,
std::string& package_name,
Mob* mob,
Mob* npc_mob
@ -295,6 +299,8 @@ private:
PerlQuestStatus global_bot_quest_status_;
PerlQuestStatus merc_quest_status_;
PerlQuestStatus global_merc_quest_status_;
PerlQuestStatus zone_quest_status_;
PerlQuestStatus global_zone_quest_status_;
SV* _empty_sv;

View File

@ -598,6 +598,51 @@ bool Perl_Zone_VariableExists(Zone* self, const std::string variable_name)
return self->VariableExists(variable_name);
}
uint32 Perl_Zone_GetTimerDuration(Zone* self, std::string name)
{
return self->GetTimerDuration(name);
}
uint32 Perl_Zone_GetTimerRemainingTime(Zone* self, std::string name)
{
return self->GetTimerRemainingTime(name);
}
bool Perl_Zone_HasTimer(Zone* self, std::string name)
{
return self->HasTimer(name);
}
bool Perl_Zone_IsPausedTimer(Zone* self, std::string name)
{
return self->IsPausedTimer(name);
}
void Perl_Zone_PauseTimer(Zone* self, std::string name)
{
self->PauseTimer(name);
}
void Perl_Zone_ResumeTimer(Zone* self, std::string name)
{
self->ResumeTimer(name);
}
void Perl_Zone_SetTimer(Zone* self, std::string name, uint32 duration)
{
self->SetTimer(name, duration);
}
void Perl_Zone_StopTimer(Zone* self, std::string name)
{
self->StopTimer(name);
}
void Perl_Zone_StopAllTimers(Zone* self)
{
self->StopAllTimers();
}
void perl_register_zone()
{
perl::interpreter perl(PERL_GET_THX);
@ -689,6 +734,8 @@ void perl_register_zone()
package.add("GetSnowDuration", (int(*)(Zone*, uint8))&Perl_Zone_GetSnowDuration);
package.add("GetTimeType", &Perl_Zone_GetTimeType);
package.add("GetTimeZone", &Perl_Zone_GetTimeZone);
package.add("GetTimerDuration", &Perl_Zone_GetTimerDuration);
package.add("GetTimerRemainingTime", &Perl_Zone_GetTimerRemainingTime);
package.add("GetZoneDescription", &Perl_Zone_GetZoneDescription);
package.add("GetZoneID", &Perl_Zone_GetZoneID);
package.add("GetZoneType", &Perl_Zone_GetZoneType);
@ -701,12 +748,14 @@ void perl_register_zone()
package.add("GetZoneTotalBlockedSpells", &Perl_Zone_GetZoneTotalBlockedSpells);
package.add("HasGraveyard", &Perl_Zone_HasGraveyard);
package.add("HasMap", &Perl_Zone_HasMap);
package.add("HasTimer", &Perl_Zone_HasTimer);
package.add("HasWaterMap", &Perl_Zone_HasWaterMap);
package.add("HasWeather", &Perl_Zone_HasWeather);
package.add("IsCity", &Perl_Zone_IsCity);
package.add("IsHotzone", &Perl_Zone_IsHotzone);
package.add("IsInstancePersistent", &Perl_Zone_IsInstancePersistent);
package.add("IsIdleWhenEmpty", &Perl_Zone_IsIdleWhenEmpty);
package.add("IsPausedTimer", &Perl_Zone_IsPausedTimer);
package.add("IsPVPZone", &Perl_Zone_IsPVPZone);
package.add("IsRaining", &Perl_Zone_IsRaining);
package.add("IsSnowing", &Perl_Zone_IsSnowing);
@ -715,8 +764,10 @@ void perl_register_zone()
package.add("IsStaticZone", &Perl_Zone_IsStaticZone);
package.add("IsUCSServerAvailable", &Perl_Zone_IsUCSServerAvailable);
package.add("IsWaterZone", &Perl_Zone_IsWaterZone);
package.add("PauseTimer", &Perl_Zone_PauseTimer);
package.add("Repop", (void(*)(Zone*))&Perl_Zone_Repop);
package.add("Repop", (void(*)(Zone*, bool))&Perl_Zone_Repop);
package.add("ResumeTimer", &Perl_Zone_ResumeTimer);
package.add("SetAAEXPModifier", &Perl_Zone_SetAAEXPModifier);
package.add("SetAAEXPModifierByCharacterID", &Perl_Zone_SetAAEXPModifierByCharacterID);
package.add("SetBucket", (void(*)(Zone*, const std::string, const std::string))&Perl_Zone_SetBucket);
@ -726,7 +777,10 @@ void perl_register_zone()
package.add("SetInstanceTimer", &Perl_Zone_SetInstanceTimer);
package.add("SetInstanceTimeRemaining", &Perl_Zone_SetInstanceTimeRemaining);
package.add("SetIsHotzone", &Perl_Zone_SetIsHotzone);
package.add("SetTimer", &Perl_Zone_SetTimer);
package.add("SetVariable", &Perl_Zone_SetVariable);
package.add("StopTimer", &Perl_Zone_StopTimer);
package.add("StopAllTimers", &Perl_Zone_StopAllTimers);
package.add("ShowZoneGlobalLoot", &Perl_Zone_ShowZoneGlobalLoot);
package.add("VariableExists", &Perl_Zone_VariableExists);
}

View File

@ -163,6 +163,28 @@ public:
return 0;
}
virtual int EventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual int EventGlobalZone(
QuestEventID event_id,
Zone* zone,
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;
@ -223,6 +245,16 @@ public:
return false;
}
virtual bool ZoneHasQuestSub(QuestEventID event_id)
{
return false;
}
virtual bool GlobalZoneHasQuestSub(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) { }
@ -234,6 +266,8 @@ public:
virtual void LoadGlobalBotScript(std::string filename) { }
virtual void LoadMercScript(std::string filename) { }
virtual void LoadGlobalMercScript(std::string filename) { }
virtual void LoadZoneScript(std::string filename) { }
virtual void LoadGlobalZoneScript(std::string filename) { }
virtual int DispatchEventNPC(
QuestEventID event_id,
@ -308,6 +342,17 @@ public:
return 0;
}
virtual int DispatchEventZone(
QuestEventID event_id,
Zone* zone,
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

@ -49,6 +49,8 @@ QuestParserCollection::QuestParserCollection()
_global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
_zone_quest_status = QuestUnloaded;
_global_zone_quest_status = QuestUnloaded;
}
QuestParserCollection::~QuestParserCollection() { }
@ -98,6 +100,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers)
_global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
_zone_quest_status = QuestUnloaded;
_global_zone_quest_status = QuestUnloaded;
_spell_quest_status.clear();
_item_quest_status.clear();
@ -426,6 +430,49 @@ bool QuestParserCollection::MercHasQuestSub(QuestEventID event_id)
return MercHasQuestSubLocal(event_id) || MercHasQuestSubGlobal(event_id);
}
bool QuestParserCollection::ZoneHasQuestSubLocal(QuestEventID event_id)
{
if (_zone_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByZoneQuest(filename);
if (qi) {
_zone_quest_status = qi->GetIdentifier();
qi->LoadZoneScript(filename);
return qi->ZoneHasQuestSub(event_id);
}
} else if (_zone_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_zone_quest_status);
return iter->second->ZoneHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::ZoneHasQuestSubGlobal(QuestEventID event_id)
{
if (_global_zone_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalZoneQuest(filename);
if (qi) {
_global_zone_quest_status = qi->GetIdentifier();
qi->LoadGlobalZoneScript(filename);
return qi->GlobalZoneHasQuestSub(event_id);
}
} else if (_global_zone_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_zone_quest_status);
return iter->second->GlobalZoneHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::ZoneHasQuestSub(QuestEventID event_id)
{
return ZoneHasQuestSubLocal(event_id) || ZoneHasQuestSubGlobal(event_id);
}
int QuestParserCollection::EventNPC(
QuestEventID event_id,
NPC* npc,
@ -924,6 +971,83 @@ int QuestParserCollection::EventMercGlobal(
return 0;
}
int QuestParserCollection::EventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
const int local_return = EventZoneLocal(event_id, zone, data, extra_data, extra_pointers);
const int global_return = EventZoneGlobal(event_id, zone, data, extra_data, extra_pointers);
const int default_return = DispatchEventZone(event_id, zone, 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::EventZoneLocal(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_zone_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByZoneQuest(filename);
if (qi) {
_zone_quest_status = qi->GetIdentifier();
qi->LoadZoneScript(filename);
return qi->EventZone(event_id, zone, data, extra_data, extra_pointers);
}
} else {
if (_zone_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_zone_quest_status);
return iter->second->EventZone(event_id, zone, data, extra_data, extra_pointers);
}
}
return 0;
}
int QuestParserCollection::EventZoneGlobal(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_global_zone_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalZoneQuest(filename);
if (qi) {
_global_zone_quest_status = qi->GetIdentifier();
qi->LoadGlobalZoneScript(filename);
return qi->EventGlobalZone(event_id, zone, data, extra_data, extra_pointers);
}
} else {
if (_global_zone_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_zone_quest_status);
return iter->second->EventGlobalZone(event_id, zone, data, extra_data, extra_pointers);
}
}
return 0;
}
QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename)
{
if (!zone) {
@ -1425,6 +1549,81 @@ QuestInterface* QuestParserCollection::GetQIByGlobalMercQuest(std::string& filen
return nullptr;
}
QuestInterface* QuestParserCollection::GetQIByZoneQuest(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("{}/zone", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/zone.ext
fmt::format("{}/zone_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version
fmt::format("{}/zone", zone_path), // Local
fmt::format("{}/zone", 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::GetQIByGlobalZoneQuest(std::string& filename)
{
if (!zone) {
return nullptr;
}
std::string file_name;
for (auto* e: _load_precedence) {
file_name = fmt::format(
"{}/{}/global_zone.{}",
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();
@ -1561,6 +1760,26 @@ int QuestParserCollection::DispatchEventMerc(
return ret;
}
int QuestParserCollection::DispatchEventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
int ret = 0;
for (const auto& e: _load_precedence) {
int i = e->DispatchEventZone(event_id, zone, data, extra_data, extra_pointers);
if (i != 0) {
ret = i;
}
}
return ret;
}
void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s)
{
for (int i = 0; i < _LargestEventID; i++) {

View File

@ -72,6 +72,7 @@ public:
bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
bool BotHasQuestSub(QuestEventID event_id);
bool MercHasQuestSub(QuestEventID event_id);
bool ZoneHasQuestSub(QuestEventID event_id);
int EventNPC(
QuestEventID event_id,
@ -172,6 +173,14 @@ public:
std::vector<std::any>* extra_pointers = nullptr
);
int EventZone(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
void GetErrors(std::list<std::string> &quest_errors);
/*
@ -209,6 +218,8 @@ private:
bool BotHasQuestSubGlobal(QuestEventID event_id);
bool MercHasQuestSubLocal(QuestEventID event_id);
bool MercHasQuestSubGlobal(QuestEventID event_id);
bool ZoneHasQuestSubLocal(QuestEventID event_id);
bool ZoneHasQuestSubGlobal(QuestEventID event_id);
int EventNPCLocal(
QuestEventID event_id,
@ -280,6 +291,22 @@ private:
std::vector<std::any>* extra_pointers
);
int EventZoneLocal(
QuestEventID event_id,
Zone* zone,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
int EventZoneGlobal(
QuestEventID event_id,
Zone* zone,
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);
@ -291,6 +318,8 @@ private:
QuestInterface* GetQIByGlobalBotQuest(std::string& filename);
QuestInterface* GetQIByMercQuest(std::string& filename);
QuestInterface* GetQIByGlobalMercQuest(std::string& filename);
QuestInterface* GetQIByZoneQuest(std::string& filename);
QuestInterface* GetQIByGlobalZoneQuest(std::string& filename);
int DispatchEventNPC(
QuestEventID event_id,
@ -347,6 +376,14 @@ private:
std::vector<std::any>* extra_pointers
);
int DispatchEventZone(
QuestEventID event_id,
Zone* zone,
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;
@ -359,6 +396,8 @@ private:
uint32 _global_bot_quest_status;
uint32 _merc_quest_status;
uint32 _global_merc_quest_status;
uint32 _zone_quest_status;
uint32 _global_zone_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

@ -1693,6 +1693,17 @@ bool Zone::Process() {
}
}
const bool has_timer_event = parse->ZoneHasQuestSub(EVENT_TIMER);
for (auto e : zone_timers) {
LogError("has_timer_event [{}]", has_timer_event ? "y" : "n");
if (e.timer_.Enabled() && e.timer_.Check()) {
if (has_timer_event) {
parse->EventZone(EVENT_TIMER, this, e.name);
}
}
}
mMovementManager->Process();
return true;
@ -3301,4 +3312,190 @@ void Zone::ReloadMaps()
pathing = IPathfinder::Load(map_name);
}
uint32 Zone::GetTimerDuration(std::string name)
{
const auto& e = std::find_if(
zone_timers.begin(),
zone_timers.end(),
[&name](ZoneTimer e) {
return e.name == name;
}
);
return e != zone_timers.end() ? e->timer_.GetDuration() : 0;
}
uint32 Zone::GetTimerRemainingTime(std::string name)
{
const auto& e = std::find_if(
zone_timers.begin(),
zone_timers.end(),
[&name](ZoneTimer e) {
return e.name == name;
}
);
return e != zone_timers.end() ? e->timer_.GetRemainingTime() : 0;
}
bool Zone::HasTimer(std::string name)
{
const auto& e = std::find_if(
zone_timers.begin(),
zone_timers.end(),
[&name](ZoneTimer e) {
return e.name == name;
}
);
return e != zone_timers.end();
}
bool Zone::IsPausedTimer(std::string name)
{
const auto& e = std::find_if(
paused_zone_timers.begin(),
paused_zone_timers.end(),
[&name](PausedZoneTimer e) {
return e.name == name;
}
);
return e != paused_zone_timers.end();
}
void Zone::PauseTimer(std::string name)
{
if (zone_timers.empty()) {
return;
}
uint32 remaining_time = 0;
if (!zone_timers.empty()) {
for (auto e = zone_timers.begin(); e != zone_timers.end(); e++) {
if (e->name == name) {
remaining_time = e->timer_.GetRemainingTime();
zone_timers.erase(e);
break;
}
}
}
paused_zone_timers.emplace_back(
PausedZoneTimer{
.name = name,
.remaining_time = remaining_time
}
);
}
void Zone::ResumeTimer(std::string name)
{
if (paused_zone_timers.empty()) {
return;
}
uint32 remaining_time = 0;
if (!paused_zone_timers.empty()) {
for (auto e = paused_zone_timers.begin(); e != paused_zone_timers.end(); e++) {
if (e->name == name) {
remaining_time = e->remaining_time;
paused_zone_timers.erase(e);
break;
}
}
}
if (!remaining_time) {
LogQuests("Paused timer [{}] not found or has expired.", name);
return;
}
const std::string& export_string = fmt::format(
"{} {}",
name,
remaining_time
);
const bool has_resume_event = parse->ZoneHasQuestSub(EVENT_TIMER_RESUME);
if (!zone_timers.empty()) {
for (auto e : zone_timers) {
if (e.name == name) {
e.timer_.Enable();
e.timer_.Start(remaining_time, false);
LogQuests(
"Resuming timer [{}] with [{}] ms remaining",
name,
remaining_time
);
if (has_resume_event) {
parse->EventZone(EVENT_TIMER_RESUME, this, export_string);
}
}
}
}
zone_timers.emplace_back(ZoneTimer(name, remaining_time));
if (has_resume_event) {
parse->EventZone(EVENT_TIMER_RESUME, this, export_string);
}
LogQuests(
"Creating a new timer and resuming [{}] with [{}] ms remaining",
name,
remaining_time
);
}
void Zone::SetTimer(std::string name, uint32 duration)
{
zone_timers.emplace_back(ZoneTimer(name, duration));
if (parse->ZoneHasQuestSub(EVENT_TIMER_START)) {
parse->EventZone(EVENT_TIMER_START, this, name);
}
}
void Zone::StopTimer(std::string name)
{
if (zone_timers.empty()) {
return;
}
const bool has_stop_event = parse->ZoneHasQuestSub(EVENT_TIMER_STOP);
for (auto e = zone_timers.begin(); e != zone_timers.end(); e++) {
if (e->name == name) {
if (has_stop_event) {
parse->EventZone(EVENT_TIMER_STOP, this, name);
}
zone_timers.erase(e);
break;
}
}
}
void Zone::StopAllTimers()
{
if (zone_timers.empty()) {
return;
}
const bool has_stop_event = parse->ZoneHasQuestSub(EVENT_TIMER_STOP);
for (auto e = zone_timers.begin(); e != zone_timers.end(); e++) {
if (has_stop_event) {
parse->EventZone(EVENT_TIMER_STOP, this, e->name);
}
e = zone_timers.erase(e);
}
}
#include "zone_loot.cpp"

View File

@ -486,6 +486,21 @@ public:
static void ClearZoneState(uint32 zone_id, uint32 instance_id);
void ReloadMaps();
struct PausedZoneTimer {
std::string name;
uint32 remaining_time;
};
uint32 GetTimerDuration(std::string name);
uint32 GetTimerRemainingTime(std::string name);
bool HasTimer(std::string name);
bool IsPausedTimer(std::string name);
void PauseTimer(std::string name);
void ResumeTimer(std::string name);
void SetTimer(std::string name, uint32 duration);
void StopTimer(std::string name);
void StopAllTimers();
private:
bool allow_mercs;
bool can_bind;
@ -552,6 +567,17 @@ private:
std::vector<BaseDataRepository::BaseData> m_base_data = { };
uint32_t m_zone_server_id = 0;
class ZoneTimer {
public:
inline ZoneTimer(std::string _name, uint32 duration)
: name(_name), timer_(duration) { timer_.Start(duration, false); }
std::string name;
Timer timer_;
};
std::vector<ZoneTimer> zone_timers;
std::vector<PausedZoneTimer> paused_zone_timers;
};
#endif