[Bots] Add support for Bot scripting. (#2515)

* [Bots] Add support for Bot scripting.

# Perl
- Add support for `zone/bot.pl` and `zone/bot_v#.pl`.
- Add support for `global/global_bot.pl`.
- Add `$bot->SignalBot(signal_id)` to Perl.
- Add `$bot->OwnerMessage(message)` to Perl.
- Add `$entity_list->SignalAllBotsByOwnerCharacterID(character_id, signal_id)` to Perl.
- Add `$entity_list->SignalBotByBotID(bot_id, signal_id)` to Perl.
- Add `$entity_list->SignalBotByBotName(bot_name, signal_id)` to Perl.
- Add `EVENT_SPELL_EFFECT_BOT` to Perl.
- Add `EVENT_SPELL_EFFECT_BUFF_TIC_BOT` to Perl.

# Lua
- Add support for `zone/bot.lua` and `zone/bot_v#.lua`.
- Add support for `global/global_bot.lua`.
- Add `bot:SignalBot(signal_id)` to Lua.
- Add `bot:OwnerMessage(message)` to Lua.
- Add `entity_list:SignalAllBotsByOwnerCharacterID(character_id, signal_id)` to Lua.
- Add `entity_list:SignalBotByBotID(bot_id, signal_id)` to Lua.
- Add `entity_list:SignalBotByBotName(bot_name, signal_id)` to Lua.
- Add `EVENT_SPELL_EFFECT_BOT` to Lua.
- Add `EVENT_SPELL_EFFECT_BUFF_TIC_BOT` to Lua.

# Supported Bot Events
1. `EVENT_CAST`
2. `EVENT_CAST_BEGIN`
3. `EVENT_CAST_ON`
4. `EVENT_COMBAT`
5. `EVENT_DEATH`
6. `EVENT_DEATH_COMPLETE`
7. `EVENT_SAY`
8. `EVENT_SIGNAL`
9. `EVENT_SLAY`
10. `EVENT_SLAY_NPC`
11. `EVENT_SPAWN`
12. `EVENT_TARGET_CHANGE`
13. `EVENT_TIMER`
14. `EVENT_USE_SKILL`

# Common
- Convert NPC pointers in common events to Mob pointers so bots are supported.
- Convert signal IDs to `int` where it wasn't already, allowing negative signals to be sent properly.

* Add EVENT_POPUP_RESPONSE.

* Cleanup and fix EVENT_COMBAT/EVENT_SLAY/EVENT_NPC_SLAY.

* Fix DoNPCEmote calls.

* Update attack.cpp

* Update event_codes.h

* Update bot_command.cpp
This commit is contained in:
Kinglykrab 2022-11-16 22:02:16 -05:00 committed by GitHub
parent 7ea77ee027
commit 856aa51cb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 2709 additions and 621 deletions

View File

@ -1528,7 +1528,7 @@ struct CZSetEntityVariable_Struct {
struct CZSignal_Struct { struct CZSignal_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name
uint32 signal; int signal;
char client_name[64]; // Only used by Character Name Type, else empty char client_name[64]; // Only used by Character Name Type, else empty
}; };
@ -1601,7 +1601,7 @@ struct WWSetEntityVariable_Struct {
struct WWSignal_Struct { struct WWSignal_Struct {
uint8 update_type; // 0 - Character, 1 - NPC uint8 update_type; // 0 - Character, 1 - NPC
uint32 signal; int signal;
uint8 min_status; uint8 min_status;
uint8 max_status; uint8 max_status;
}; };

View File

@ -1454,6 +1454,17 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
LogCombat("Attack missed. Damage set to 0"); LogCombat("Attack missed. Damage set to 0");
hit.damage_done = 0; hit.damage_done = 0;
} }
#ifdef BOTS
if (IsBot()) {
const auto export_string = fmt::format(
"{} {}",
hit.skill,
GetSkill(hit.skill)
);
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
}
#endif
} }
} }
@ -1707,22 +1718,26 @@ void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::Skill
bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill) bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill)
{ {
if (!ClientFinishedLoading()) if (!ClientFinishedLoading()) {
return false; return false;
}
if (dead) if (dead) {
return false; //cant die more than once... return false; //cant die more than once...
}
if (!spell) if (!spell) {
spell = SPELL_UNKNOWN; spell = SPELL_UNKNOWN;
}
std::string export_string = fmt::format( auto export_string = fmt::format(
"{} {} {} {}", "{} {} {} {}",
killerMob ? killerMob->GetID() : 0, killerMob ? killerMob->GetID() : 0,
damage, damage,
spell, spell,
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) { if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
@ -1793,24 +1808,36 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
GetMerc()->Suspend(); GetMerc()->Suspend();
} }
if (killerMob != nullptr) if (killerMob) {
{
if (killerMob->IsNPC()) { if (killerMob->IsNPC()) {
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
mod_client_death_npc(killerMob); mod_client_death_npc(killerMob);
uint32 emoteid = killerMob->GetEmoteID(); auto emote_id = killerMob->GetEmoteID();
if (emoteid != 0) if (emote_id) {
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid); killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid);
}
killerMob->TrySpellOnKill(killed_level, spell); killerMob->TrySpellOnKill(killed_level, spell);
#ifdef BOTS
} else if (killerMob->IsBot()) {
parse->EventBot(EVENT_SLAY, killerMob->CastToBot(), this, "", 0);
killerMob->TrySpellOnKill(killed_level, spell);
#endif
} }
if (killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) { if (
killerMob->IsClient() &&
(IsDueling() || killerMob->CastToClient()->IsDueling())
) {
SetDueling(false); SetDueling(false);
SetDuelTarget(0); SetDuelTarget(0);
if (killerMob->IsClient() && killerMob->CastToClient()->IsDueling() && killerMob->CastToClient()->GetDuelTarget() == GetID()) if (
{ killerMob->IsClient() &&
killerMob->CastToClient()->IsDueling() &&
killerMob->CastToClient()->GetDuelTarget() == GetID()
) {
//if duel opponent killed us... //if duel opponent killed us...
killerMob->CastToClient()->SetDueling(false); killerMob->CastToClient()->SetDueling(false);
killerMob->CastToClient()->SetDuelTarget(0); killerMob->CastToClient()->SetDuelTarget(0);
@ -1843,16 +1870,19 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
// figure out if they should lose exp // figure out if they should lose exp
if (RuleB(Character, UseDeathExpLossMult)) { if (RuleB(Character, UseDeathExpLossMult)) {
float GetNum[] = { 0.005f,0.015f,0.025f,0.035f,0.045f,0.055f,0.065f,0.075f,0.085f,0.095f,0.110f }; float exp_losses[] = { 0.005f, 0.015f, 0.025f, 0.035f, 0.045f, 0.055f, 0.065f, 0.075f, 0.085f, 0.095f, 0.110f };
int Num = RuleI(Character, DeathExpLossMultiplier); int exp_loss = RuleI(Character, DeathExpLossMultiplier);
if ((Num < 0) || (Num > 10)) if (!EQ::ValueWithin(exp_loss, 0, 10)) {
Num = 3; exp_loss = 3;
float loss = GetNum[Num]; }
exploss = (int)((float)GetEXP() * (loss)); //loose % of total XP pending rule (choose 0-10)
auto current_exp_loss = exp_losses[exp_loss];
exploss = static_cast<int>(static_cast<float>(GetEXP()) * current_exp_loss); //loose % of total XP pending rule (choose 0-10)
} }
if (!RuleB(Character, UseDeathExpLossMult)) { if (!RuleB(Character, UseDeathExpLossMult)) {
exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); exploss = static_cast<int>(GetLevel() * (GetLevel() / 18.0) * 12000);
} }
if (RuleB(Zone, LevelBasedEXPMods)) { if (RuleB(Zone, LevelBasedEXPMods)) {
@ -1873,56 +1903,49 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
} }
} }
if ((GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC()) if ((GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC()) {
{
exploss = 0; exploss = 0;
} } else if (killerMob) {
else if (killerMob) if (killerMob->IsClient()) {
{
if (killerMob->IsClient())
{
exploss = 0; exploss = 0;
} } else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient()) {
else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient())
{
exploss = 0; exploss = 0;
#ifdef BOTS
} else if (killerMob->IsBot()) {
exploss = 0;
#endif
} }
} }
if (spell != SPELL_UNKNOWN) if (spell != SPELL_UNKNOWN) {
{
uint32 buff_count = GetMaxTotalSlots(); uint32 buff_count = GetMaxTotalSlots();
for (uint16 buffIt = 0; buffIt < buff_count; buffIt++) for (uint16 buffIt = 0; buffIt < buff_count; buffIt++) {
{ if (buffs[buffIt].spellid == spell && buffs[buffIt].client) {
if (buffs[buffIt].spellid == spell && buffs[buffIt].client)
{
exploss = 0; // no exp loss for pvp dot exploss = 0; // no exp loss for pvp dot
break; break;
} }
} }
} }
bool LeftCorpse = false; bool leave_corpse = false;
Corpse* new_corpse = nullptr; Corpse* new_corpse = nullptr;
// now we apply the exp loss, unmem their spells, and make a corpse // now we apply the exp loss, unmem their spells, and make a corpse
// unless they're a GM (or less than lvl 10 // unless they're a GM (or less than lvl 10
if (!GetGM()) if (!GetGM()) {
{
if (exploss > 0) { if (exploss > 0) {
int32 newexp = GetEXP(); int32 newexp = GetEXP();
if (exploss > newexp) { if (exploss > newexp) {
//lost more than we have... wtf.. //lost more than we have... wtf..
newexp = 1; newexp = 1;
} } else {
else {
newexp -= exploss; newexp -= exploss;
} }
SetEXP(newexp, GetAAXP()); SetEXP(newexp, GetAAXP());
//m_epp.perAA = 0; //reset to no AA exp on death. //m_epp.perAA = 0; //reset to no AA exp on death.
} }
int32 illusion_spell_id = spellbonuses.Illusion; auto illusion_spell_id = spellbonuses.Illusion;
//this generates a lot of 'updates' to the client that the client does not need //this generates a lot of 'updates' to the client that the client does not need
if (RuleB(Spells, BuffsFadeOnDeath)) { if (RuleB(Spells, BuffsFadeOnDeath)) {
@ -1930,58 +1953,61 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
} }
if (RuleB(Character, UnmemSpellsOnDeath)) { if (RuleB(Character, UnmemSpellsOnDeath)) {
if ((ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) if ((ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) {
UnmemSpellAll(true); UnmemSpellAll(true);
else } else {
UnmemSpellAll(false); UnmemSpellAll(false);
}
} }
if ((RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) || RuleB(Character, LeaveNakedCorpses)) if (
{ (RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) ||
RuleB(Character, LeaveNakedCorpses)
) {
// creating the corpse takes the cash/items off the player too // creating the corpse takes the cash/items off the player too
new_corpse = new Corpse(this, exploss); new_corpse = new Corpse(this, exploss);
std::string tmp; std::string tmp;
database.GetVariable("ServerType", tmp); database.GetVariable("ServerType", tmp);
if (tmp[0] == '1' && tmp[1] == '\0' && killerMob != nullptr && killerMob->IsClient()) { if (tmp[0] == '1' && tmp[1] == '\0' && killerMob && killerMob->IsClient()) {
database.GetVariable("PvPreward", tmp); database.GetVariable("PvPreward", tmp);
int reward = atoi(tmp.c_str()); auto reward = atoi(tmp.c_str());
if (reward == 3) { if (reward == 3) {
database.GetVariable("PvPitem", tmp); database.GetVariable("PvPitem", tmp);
int pvpitem = atoi(tmp.c_str()); auto pvp_item_id = atoi(tmp.c_str());
if (pvpitem>0 && pvpitem<200000) const auto* item = database.GetItem(pvp_item_id);
new_corpse->SetPlayerKillItemID(pvpitem); if (item) {
} new_corpse->SetPlayerKillItemID(pvp_item_id);
else if (reward == 2) }
} else if (reward == 2) {
new_corpse->SetPlayerKillItemID(-1); new_corpse->SetPlayerKillItemID(-1);
else if (reward == 1) } else if (reward == 1) {
new_corpse->SetPlayerKillItemID(1); new_corpse->SetPlayerKillItemID(1);
else } else {
new_corpse->SetPlayerKillItemID(0); new_corpse->SetPlayerKillItemID(0);
}
if (killerMob->CastToClient()->isgrouped) { if (killerMob->CastToClient()->isgrouped) {
Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); auto* group = entity_list.GetGroupByClient(killerMob->CastToClient());
if (group != 0) if (group) {
{ for (int i = 0; i < 6; i++) {
for (int i = 0; i<6; i++) if (group->members[i]) {
{
if (group->members[i] != nullptr)
{
new_corpse->AllowPlayerLoot(group->members[i], i); new_corpse->AllowPlayerLoot(group->members[i], i);
} }
} }
} }
} }
} }
entity_list.AddCorpse(new_corpse, GetID()); entity_list.AddCorpse(new_corpse, GetID());
SetID(0); SetID(0);
//send the become corpse packet to everybody else in the zone. //send the become corpse packet to everybody else in the zone.
entity_list.QueueClients(this, &app2, true); entity_list.QueueClients(this, &app2, true);
ApplyIllusionToCorpse(illusion_spell_id, new_corpse); ApplyIllusionToCorpse(illusion_spell_id, new_corpse);
LeftCorpse = true; leave_corpse = true;
} }
} } else {
else {
BuffFadeDetrimental(); BuffFadeDetrimental();
} }
@ -1993,8 +2019,9 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
/* /*
Reset reuse timer for classic skill based Lay on Hands (For tit I guess) Reset reuse timer for classic skill based Lay on Hands (For tit I guess)
*/ */
if (GetClass() == PALADIN) // we could check if it's not expired I guess, but should be fine not to if (GetClass() == PALADIN) { // we could check if it's not expired I guess, but should be fine not to
p_timers.Clear(&database, pTimerLayHands); p_timers.Clear(&database, pTimerLayHands);
}
/* /*
Finally, send em home Finally, send em home
@ -2003,25 +2030,23 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
from these and overwrite what we set in pp anyway from these and overwrite what we set in pp anyway
*/ */
if (LeftCorpse && (ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) if (leave_corpse && (ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) {
{
ClearDraggedCorpses(); ClearDraggedCorpses();
RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000);
SendRespawnBinds(); SendRespawnBinds();
} } else {
else if (isgrouped) {
{ auto* g = GetGroup();
if (isgrouped) if (g) {
{
Group *g = GetGroup();
if (g)
g->MemberZoned(this); g->MemberZoned(this);
}
} }
Raid* r = entity_list.GetRaidByClient(this); auto* r = entity_list.GetRaidByClient(this);
if (r) if (r) {
r->MemberZoned(this); r->MemberZoned(this);
}
dead_timer.Start(5000, true); dead_timer.Start(5000, true);
m_pp.zone_id = m_pp.binds[0].zone_id; m_pp.zone_id = m_pp.binds[0].zone_id;
@ -2320,9 +2345,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]",
((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill);
Mob* oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; Mob *oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
auto export_string = fmt::format(
std::string export_string = fmt::format(
"{} {} {} {}", "{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0, killer_mob ? killer_mob->GetID() : 0,
damage, damage,
@ -2330,12 +2354,24 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
// todo: multiple attacks causes this to fire multiple times (DoAttackRounds, DoMain/OffHandAttackRounds, DoRiposte, spells?) if (IsNPC()) {
if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) { if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
}
return false;
} }
return false; #ifdef BOTS
} else if (IsBot()) {
if (parse->EventBot(EVENT_DEATH, CastToBot(), oos, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
return false;
}
#endif
} }
if (killer_mob && (killer_mob->IsClient() || killer_mob->IsBot()) && (spell != SPELL_UNKNOWN) && damage > 0) { if (killer_mob && (killer_mob->IsClient() || killer_mob->IsBot()) && (spell != SPELL_UNKNOWN) && damage > 0) {
@ -2364,23 +2400,25 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
SetPet(0); SetPet(0);
if (GetSwarmOwner()) { if (GetSwarmOwner()) {
Mob* owner = entity_list.GetMobID(GetSwarmOwner()); auto* owner = entity_list.GetMobID(GetSwarmOwner());
if (owner) if (owner) {
owner->SetTempPetCount(owner->GetTempPetCount() - 1); owner->SetTempPetCount(owner->GetTempPetCount() - 1);
}
} }
Mob* killer = GetHateDamageTop(this); auto* killer = GetHateDamageTop(this);
entity_list.RemoveFromTargets(this, p_depop); entity_list.RemoveFromTargets(this, p_depop);
if (p_depop == true) if (p_depop) {
return false; return false;
}
int32 illusion_spell_id = spellbonuses.Illusion; const auto illusion_spell_id = spellbonuses.Illusion;
HasAISpellEffects = false; HasAISpellEffects = false;
BuffFadeAll(); BuffFadeAll();
uint8 killed_level = GetLevel(); const auto killed_level = GetLevel();
if (GetClass() == LDON_TREASURE) { // open chest if (GetClass() == LDON_TREASURE) { // open chest
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct)); auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
@ -2393,7 +2431,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct)); auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
Death_Struct* d = (Death_Struct*)app->pBuffer; auto* d = (Death_Struct*) app->pBuffer;
d->spawn_id = GetID(); d->spawn_id = GetID();
d->killer_id = killer_mob ? killer_mob->GetID() : 0; d->killer_id = killer_mob ? killer_mob->GetID() : 0;
d->bindzoneid = 0; d->bindzoneid = 0;
@ -2409,16 +2447,17 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
respawn2->DeathReset(1); respawn2->DeathReset(1);
} }
if (killer_mob && GetClass() != LDON_TREASURE) if (killer_mob && GetClass() != LDON_TREASURE) {
hate_list.AddEntToHateList(killer_mob, damage); hate_list.AddEntToHateList(killer_mob, damage);
}
Mob *give_exp = hate_list.GetDamageTopOnHateList(this); Mob *give_exp = hate_list.GetDamageTopOnHateList(this);
if (give_exp == nullptr) if (give_exp) {
give_exp = killer; give_exp = killer;
}
if (give_exp && give_exp->HasOwner()) { if (give_exp && give_exp->HasOwner()) {
bool ownerInGroup = false; bool ownerInGroup = false;
if ((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())) if ((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner()))
|| (give_exp->IsPet() && (give_exp->GetOwner()->IsClient() || (give_exp->IsPet() && (give_exp->GetOwner()->IsClient()
@ -2721,18 +2760,33 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (oos) { if (oos) {
mod_npc_killed(oos); mod_npc_killed(oos);
uint32 emoteid = GetEmoteID(); if (IsNPC()) {
if (emoteid != 0) auto emote_id = GetEmoteID();
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid); if (emote_id) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid);
}
}
if (oos->IsNPC()) { if (oos->IsNPC()) {
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0);
uint32 emoteid = oos->GetEmoteID(); auto emote_id = oos->GetEmoteID();
if (emoteid != 0) if (emote_id) {
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emoteid); oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
} }
#ifdef BOTS
if (killer_mob->IsBot()) {
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
killer_mob->TrySpellOnKill(killed_level, spell);
}
#endif
LogInfo("[Attack] Should have attempted to do OOS check");
WipeHateList(); WipeHateList();
p_depop = true; p_depop = true;

View File

@ -2663,7 +2663,13 @@ void Bot::AI_Process()
auto pull_target = bot_owner->GetTarget(); auto pull_target = bot_owner->GetTarget();
if (pull_target) { if (pull_target) {
Bot::BotGroupSay(this, "Pulling %s to the group..", pull_target->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Pulling {}.",
pull_target->GetCleanName()
).c_str()
);
InterruptSpell(); InterruptSpell();
WipeHateList(); WipeHateList();
AddToHateList(pull_target, 1); AddToHateList(pull_target, 1);
@ -2822,14 +2828,20 @@ void Bot::AI_Process()
bool find_target = true; bool find_target = true;
if (assist_mob) { if (assist_mob) {
if (assist_mob->GetTarget()) { if (assist_mob->GetTarget()) {
if (assist_mob != this) { if (assist_mob != this) {
if (GetTarget() != assist_mob->GetTarget()) {
SetTarget(assist_mob->GetTarget());
}
SetTarget(assist_mob->GetTarget()); if (
if (HasPet() && (GetClass() != ENCHANTER || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { HasPet() &&
(
GetClass() != ENCHANTER ||
GetPet()->GetPetType() != petAnimation ||
GetAA(aaAnimationEmpathy) >= 2
)
) {
// This artificially inflates pet's target aggro..but, less expensive than checking hate each AI process // This artificially inflates pet's target aggro..but, less expensive than checking hate each AI process
GetPet()->AddToHateList(assist_mob->GetTarget(), 1); GetPet()->AddToHateList(assist_mob->GetTarget(), 1);
GetPet()->SetTarget(assist_mob->GetTarget()); GetPet()->SetTarget(assist_mob->GetTarget());
@ -2837,12 +2849,19 @@ void Bot::AI_Process()
} }
find_target = false; find_target = false;
} } else if (assist_mob != this) {
else if (assist_mob != this) { if (GetTarget()) {
SetTarget(nullptr);
SetTarget(nullptr); }
if (HasPet() && (GetClass() != ENCHANTER || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) {
if (
HasPet() &&
(
GetClass() != ENCHANTER ||
GetPet()->GetPetType() != petAnimation ||
GetAA(aaAnimationEmpathy) >= 1
)
) {
GetPet()->WipeHateList(); GetPet()->WipeHateList();
GetPet()->SetTarget(nullptr); GetPet()->SetTarget(nullptr);
} }
@ -2852,16 +2871,23 @@ void Bot::AI_Process()
} }
if (find_target) { if (find_target) {
if (IsRooted()) { if (IsRooted()) {
SetTarget(hate_list.GetClosestEntOnHateList(this, true)); auto closest = hate_list.GetClosestEntOnHateList(this, true);
} if (closest) {
else { SetTarget(closest);
}
} else {
// This will keep bots on target for now..but, future updates will allow for rooting/stunning // This will keep bots on target for now..but, future updates will allow for rooting/stunning
SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance)); auto escaping = hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance);
if (escaping) {
SetTarget(escaping);
}
if (!GetTarget()) { if (!GetTarget()) {
SetTarget(hate_list.GetEntWithMostHateOnList(this, nullptr, true)); auto most_hate = hate_list.GetEntWithMostHateOnList(this, nullptr, true);
if (most_hate) {
SetTarget(most_hate);
}
} }
} }
} }
@ -2884,8 +2910,10 @@ void Bot::AI_Process()
Mob* tar = GetTarget(); // We should have a target..if not, we're awaiting new orders Mob* tar = GetTarget(); // We should have a target..if not, we're awaiting new orders
if (!tar || PASSIVE) { if (!tar || PASSIVE) {
if (GetTarget()) {
SetTarget(nullptr);
}
SetTarget(nullptr);
WipeHateList(); WipeHateList();
SetAttackFlag(false); SetAttackFlag(false);
SetAttackingFlag(false); SetAttackingFlag(false);
@ -4929,12 +4957,10 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
//} //}
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) { if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
client->Message( OwnerMessage(
Chat::White,
fmt::format( fmt::format(
"Failed to delete item by slot from slot {} for {}.", "Failed to delete item by slot from slot {}.",
return_iterator.from_bot_slot, return_iterator.from_bot_slot
GetCleanName()
).c_str() ).c_str()
); );
} }
@ -4947,13 +4973,11 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
linker.SetItemInst(return_instance); linker.SetItemInst(return_instance);
auto item_link = linker.GenerateLink(); auto item_link = linker.GenerateLink();
client->Message( OwnerMessage(
Chat::Tell,
fmt::format( fmt::format(
"{} tells you, 'I have returned {}.'", "I have returned {}.",
GetCleanName(),
item_link item_link
).c_str() )
); );
client->PutItemInInventory(return_iterator.to_client_slot, *return_instance, true); client->PutItemInInventory(return_iterator.to_client_slot, *return_instance, true);
@ -4969,12 +4993,10 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
// TODO: code for stackables // TODO: code for stackables
if (!database.botdb.SaveItemBySlot(this, trade_iterator.to_bot_slot, trade_iterator.trade_item_instance)) { if (!database.botdb.SaveItemBySlot(this, trade_iterator.to_bot_slot, trade_iterator.trade_item_instance)) {
client->Message( OwnerMessage(
Chat::White,
fmt::format( fmt::format(
"Failed to save item by slot to slot {} for {}.", "Failed to save item by slot to slot {}.",
trade_iterator.to_bot_slot, trade_iterator.to_bot_slot
GetCleanName()
).c_str() ).c_str()
); );
} }
@ -4984,13 +5006,11 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
linker.SetItemInst(trade_iterator.trade_item_instance); linker.SetItemInst(trade_iterator.trade_item_instance);
auto item_link = linker.GenerateLink(); auto item_link = linker.GenerateLink();
client->Message( OwnerMessage(
Chat::Tell,
fmt::format( fmt::format(
"{} tells you, 'I have accepted {}.'", "I have accepted {}.",
GetCleanName(),
item_link item_link
).c_str() )
); );
m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance); m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance);
@ -5101,6 +5121,16 @@ bool Bot::Death(Mob *killerMob, int64 damage, uint16 spell_id, EQ::skills::Skill
my_owner->CastToClient()->SetBotPulling(false); my_owner->CastToClient()->SetBotPulling(false);
} }
const auto export_string = fmt::format(
"{} {} {} {}",
killerMob ? killerMob->GetID() : 0,
damage,
spell_id,
static_cast<int>(attack_skill)
);
parse->EventBot(EVENT_DEATH_COMPLETE, this, killerMob, export_string, 0);
entity_list.RemoveBot(GetID()); entity_list.RemoveBot(GetID());
return true; return true;
} }
@ -5112,7 +5142,7 @@ void Bot::Damage(Mob *from, int64 damage, uint16 spell_id, EQ::skills::SkillType
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if(attacked_timer.Check()) { if(attacked_timer.Check()) {
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", from->GetName()); LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", from->GetName());
parse->EventNPC(EVENT_ATTACK, this, from, "", 0); parse->EventBot(EVENT_ATTACK, this, from, "", 0);
} }
attacked_timer.Start(CombatEventTimer_expire); attacked_timer.Start(CombatEventTimer_expire);
@ -6554,7 +6584,13 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
if(taunting && target && target->IsNPC() && taunt_time) { if(taunting && target && target->IsNPC() && taunt_time) {
if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) {
BotGroupSay(this, "Taunting %s", target->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Taunting {}.",
target->GetCleanName()
).c_str()
);
Taunt(target->CastToNPC(), false); Taunt(target->CastToNPC(), false);
taunt_timer.Start(TauntReuseTime * 1000); taunt_timer.Start(TauntReuseTime * 1000);
} }
@ -7512,27 +7548,49 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
Mob::DoBuffTic(buff, slot, caster); Mob::DoBuffTic(buff, slot, caster);
} }
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot, int32 cast_time, int32 mana_cost, bool Bot::CastSpell(
uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) { uint16 spell_id,
uint16 target_id,
EQ::spells::CastingSlot slot,
int32 cast_time,
int32 mana_cost,
uint32* oSpellWillFinish,
uint32 item_slot,
int16 *resist_adjust,
uint32 aa_id
) {
bool Result = false; bool Result = false;
if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { if (zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) {
// LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
if(casting_spell_id == spell_id) if (casting_spell_id == spell_id) {
ZeroCastingVars(); ZeroCastingVars();
}
if(GetClass() != BARD) { if (GetClass() != BARD) {
if(!IsValidSpell(spell_id) || casting_spell_id || delaytimer || spellend_timer.Enabled() || IsStunned() || IsFeared() || IsMezzed() || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id))) { if (
!IsValidSpell(spell_id) ||
casting_spell_id ||
delaytimer ||
spellend_timer.Enabled() ||
IsStunned() ||
IsFeared() ||
IsMezzed() ||
(IsSilenced() && !IsDiscipline(spell_id)) ||
(IsAmnesiad() && IsDiscipline(spell_id))
) {
LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() );
if(IsSilenced() && !IsDiscipline(spell_id)) if (IsSilenced() && !IsDiscipline(spell_id)) {
MessageString(Chat::White, SILENCED_STRING); MessageString(Chat::White, SILENCED_STRING);
}
if(IsAmnesiad() && IsDiscipline(spell_id)) if (IsAmnesiad() && IsDiscipline(spell_id)) {
MessageString(Chat::White, MELEE_SILENCE); MessageString(Chat::White, MELEE_SILENCE);
}
if(casting_spell_id) if (casting_spell_id) {
AI_Bot_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot)); AI_Bot_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
}
return false; return false;
} }
@ -7540,19 +7598,20 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot s
if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) {
MessageString(Chat::White, SPELL_WOULDNT_HOLD); MessageString(Chat::White, SPELL_WOULDNT_HOLD);
if(casting_spell_id) if (casting_spell_id) {
AI_Bot_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot)); AI_Bot_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
}
return false; return false;
} }
if(DivineAura()) { if (DivineAura()) {
LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect");
InterruptSpell(173, 0x121, false); InterruptSpell(173, 0x121, false);
return false; return false;
} }
if(slot < EQ::spells::CastingSlot::MaxGems && !CheckFizzle(spell_id)) { if (slot < EQ::spells::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE; int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id); InterruptSpell(fizzle_msg, 0x121, spell_id);
@ -7878,7 +7937,13 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) { bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) {
bool isMainGroupMGB = false; bool isMainGroupMGB = false;
if(isMainGroupMGB && (GetClass() != BARD)) { if(isMainGroupMGB && (GetClass() != BARD)) {
BotGroupSay(this, "MGB %s", spells[spell_id].name); BotGroupSay(
this,
fmt::format(
"Casting {} as a Mass Group Buff.",
spells[spell_id].name
).c_str()
);
SpellOnTarget(spell_id, this); SpellOnTarget(spell_id, this);
entity_list.AESpell(this, this, spell_id, true); entity_list.AESpell(this, this, spell_id, true);
} else { } else {
@ -9564,28 +9629,30 @@ Client* EntityList::GetBotOwnerByBotEntityID(uint16 entityID) {
return Result; return Result;
} }
void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) {
if(newBot) { if (new_bot) {
newBot->SetID(GetFreeID()); new_bot->SetID(GetFreeID());
newBot->SetSpawned(); new_bot->SetSpawned();
if(SendSpawnPacket) { if (send_spawn_packet) {
if(dontqueue) { if (dont_queue) {
EQApplicationPacket* outapp = new EQApplicationPacket(); EQApplicationPacket* outapp = new EQApplicationPacket();
newBot->CreateSpawnPacket(outapp); new_bot->CreateSpawnPacket(outapp);
outapp->priority = 6; outapp->priority = 6;
QueueClients(newBot, outapp, true); QueueClients(new_bot, outapp, true);
safe_delete(outapp); safe_delete(outapp);
} else { } else {
NewSpawn_Struct* ns = new NewSpawn_Struct; NewSpawn_Struct* ns = new NewSpawn_Struct;
memset(ns, 0, sizeof(NewSpawn_Struct)); memset(ns, 0, sizeof(NewSpawn_Struct));
newBot->FillSpawnStruct(ns, newBot); new_bot->FillSpawnStruct(ns, new_bot);
AddToSpawnQueue(newBot->GetID(), &ns); AddToSpawnQueue(new_bot->GetID(), &ns);
safe_delete(ns); safe_delete(ns);
} }
parse->EventNPC(EVENT_SPAWN, newBot, nullptr, "", 0);
parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0);
} }
bot_list.push_back(newBot);
mob_list.insert(std::pair<uint16, Mob*>(newBot->GetID(), newBot)); bot_list.push_back(new_bot);
mob_list.insert(std::pair<uint16, Mob*>(new_bot->GetID(), new_bot));
} }
} }
@ -10379,6 +10446,28 @@ void Bot::SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leade
); );
} }
void Bot::SignalBot(int signal_id)
{
const auto export_string = fmt::format("{}", signal_id);
parse->EventBot(EVENT_SIGNAL, this, nullptr, export_string, 0);
}
void Bot::OwnerMessage(std::string message)
{
if (!GetBotOwner() || !GetBotOwner()->IsClient()) {
return;
}
GetBotOwner()->Message(
Chat::Tell,
fmt::format(
"{} tells you, '{}'",
GetCleanName(),
message
).c_str()
);
}
bool Bot::GetBotOwnerDataBuckets() bool Bot::GetBotOwnerDataBuckets()
{ {
auto bot_owner = GetBotOwner(); auto bot_owner = GetBotOwner();

View File

@ -681,6 +681,9 @@ public:
int32 GetBaseDR() { return _baseDR; } int32 GetBaseDR() { return _baseDR; }
int32 GetBaseCorrup() { return _baseCorrup; } int32 GetBaseCorrup() { return _baseCorrup; }
void SignalBot(int signal_id);
void OwnerMessage(std::string message);
protected: protected:
virtual void PetAIProcess(); virtual void PetAIProcess();
virtual void BotMeditate(bool isSitting); virtual void BotMeditate(bool isSitting);
@ -700,6 +703,7 @@ protected:
//void SetRaidSlower(bool flag = true) { m_CastingRoles.RaidSlower = flag; } //void SetRaidSlower(bool flag = true) { m_CastingRoles.RaidSlower = flag; }
//void SetRaidNuker(bool flag = true) { m_CastingRoles.RaidNuker = flag; } //void SetRaidNuker(bool flag = true) { m_CastingRoles.RaidNuker = flag; }
//void SetRaidDoter(bool flag = true) { m_CastingRoles.RaidDoter = flag; } //void SetRaidDoter(bool flag = true) { m_CastingRoles.RaidDoter = flag; }
std::deque<int> bot_signal_q;
private: private:
// Class Members // Class Members

View File

@ -2585,8 +2585,16 @@ void bot_command_aggressive(Client *c, const Seperator *sep)
} }
my_bot->InterruptSpell(); my_bot->InterruptSpell();
if (candidate_count == 1) if (candidate_count == 1) {
Bot::BotGroupSay(my_bot, "Using '%s'", spells[local_entry->spell_id].name); Bot::BotGroupSay(
my_bot,
fmt::format(
"Using {}.",
spells[local_entry->spell_id].name
).c_str()
);
}
my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID()); my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID());
++success_count; ++success_count;
@ -2803,10 +2811,22 @@ void bot_command_attack(Client *c, const Seperator *sep)
} }
if (attacker_count == 1 && first_attacker) { if (attacker_count == 1 && first_attacker) {
Bot::BotGroupSay(first_attacker, "Attacking %s!", target_mob->GetCleanName()); Bot::BotGroupSay(
} first_attacker,
else { fmt::format(
c->Message(Chat::White, "%i of your bots are attacking %s!", sbl.size(), target_mob->GetCleanName()); "Attacking {}.",
target_mob->GetCleanName()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are attacking {}.",
sbl.size(),
target_mob->GetCleanName()
).c_str()
);
} }
} }
@ -3065,8 +3085,16 @@ void bot_command_defensive(Client *c, const Seperator *sep)
} }
my_bot->InterruptSpell(); my_bot->InterruptSpell();
if (candidate_count == 1) if (candidate_count == 1) {
Bot::BotGroupSay(my_bot, "Using '%s'", spells[local_entry->spell_id].name); Bot::BotGroupSay(
my_bot,
fmt::format(
"Using {}.",
spells[local_entry->spell_id].name
).c_str()
);
}
my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID()); my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID());
++success_count; ++success_count;
@ -3285,15 +3313,35 @@ void bot_command_follow(Client *c, const Seperator *sep)
bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->WipeHateList();
bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); bot_iter->GetPet()->SetFollowID(bot_iter->GetID());
} }
if (sbl.size() == 1) { if (sbl.size() == 1) {
Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID());
Bot::BotGroupSay(sbl.front(), "Following %s", ((follow_mob) ? (follow_mob->GetCleanName()) : ("'nullptr'"))); Bot::BotGroupSay(
} sbl.front(),
else { fmt::format(
if (reset) "Following {}.",
c->Message(Chat::White, "%i of your bots are following their default assignments", sbl.size()); follow_mob ? follow_mob->GetCleanName() : "no one"
else ).c_str()
c->Message(Chat::White, "%i of your bots are following %s", sbl.size(), target_mob->GetCleanName()); );
} else {
if (reset) {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are following their default assignments.",
sbl.size()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are following {}.",
sbl.size(),
target_mob->GetCleanName()
).c_str()
);
}
} }
} }
@ -3338,9 +3386,14 @@ void bot_command_guard(Client *c, const Seperator *sep)
} }
if (sbl.size() == 1) { if (sbl.size() == 1) {
Bot::BotGroupSay(sbl.front(), "%suarding this position.", (clear ? "No longer g" : "G")); Bot::BotGroupSay(
} sbl.front(),
else { fmt::format(
"{}uarding this position.",
clear ? "No longer g" : "G"
).c_str()
);
} else {
c->Message(Chat::White, "%i of your bots are %sguarding their positions.", sbl.size(), (clear ? "no longer " : "")); c->Message(Chat::White, "%i of your bots are %sguarding their positions.", sbl.size(), (clear ? "no longer " : ""));
} }
} }
@ -3469,10 +3522,22 @@ void bot_command_hold(Client *c, const Seperator *sep)
} }
if (sbl.size() == 1) { if (sbl.size() == 1) {
Bot::BotGroupSay(sbl.front(), "%solding my attacks.", (clear ? "No longer h" : "H")); Bot::BotGroupSay(
} sbl.front(),
else { fmt::format(
c->Message(Chat::White, "%i of your bots are %sholding their attacks.", sbl.size(), (clear ? "no longer " : "")); "{}olding my attacks.",
clear ? "No longer h" : "H"
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are {}holding their attacks.",
sbl.size(),
clear ? "no longer " : ""
).c_str()
);
} }
} }
@ -4224,7 +4289,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
Bot* my_bot = sbl.front(); Bot* my_bot = sbl.front();
my_bot->InterruptSpell(); my_bot->InterruptSpell();
Bot::BotGroupSay(my_bot, "Attempting to pick the lock.."); Bot::BotGroupSay(my_bot, "Attempting to pick the lock.");
std::list<Doors*> door_list; std::list<Doors*> door_list;
entity_list.GetDoorsList(door_list); entity_list.GetDoorsList(door_list);
@ -4251,7 +4316,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
++open_count; ++open_count;
} }
else { else {
Bot::BotGroupSay(my_bot, "I am not skilled enough for this lock..."); Bot::BotGroupSay(my_bot, "I am not skilled enough for this lock.");
} }
} }
c->Message(Chat::White, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : (""))); c->Message(Chat::White, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : ("")));
@ -4791,38 +4856,78 @@ void bot_command_taunt(Client *c, const Seperator *sep)
int taunting_count = 0; int taunting_count = 0;
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) {
continue; continue;
}
if (toggle_taunt) if (toggle_taunt) {
bot_iter->SetTaunting(!bot_iter->IsTaunting()); bot_iter->SetTaunting(!bot_iter->IsTaunting());
else } else {
bot_iter->SetTaunting(taunt_state); bot_iter->SetTaunting(taunt_state);
}
if (sbl.size() == 1) if (sbl.size() == 1) {
Bot::BotGroupSay(bot_iter, "I am %s taunting", bot_iter->IsTaunting() ? "now" : "no longer"); Bot::BotGroupSay(
bot_iter,
fmt::format(
"I am {} taunting.",
bot_iter->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count; ++taunting_count;
} }
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (!bot_iter->HasPet()) if (!bot_iter->HasPet()) {
continue; continue;
if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) }
if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) {
continue; continue;
if (toggle_taunt) }
if (toggle_taunt) {
bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting()); bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting());
else } else {
bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state); bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state);
if (sbl.size() == 1) }
Bot::BotGroupSay(bot_iter, "My Pet is %s taunting", bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer");
if (sbl.size() == 1) {
Bot::BotGroupSay(
bot_iter,
fmt::format(
"My Pet is {} taunting.",
bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count; ++taunting_count;
} }
if (taunting_count) { if (taunting_count) {
if (toggle_taunt) if (toggle_taunt) {
c->Message(Chat::White, "%i of your bots and their pets %s toggled their taunting state", taunting_count, ((taunting_count != 1) ? ("have") : ("has"))); c->Message(
else Chat::White,
c->Message(Chat::White, "%i of your bots and their pets %s %s taunting", taunting_count, ((taunting_count != 1) ? ("have") : ("has")), ((taunt_state) ? ("started") : ("stopped"))); fmt::format(
"{} of your bots and their pets {} toggled their taunting state",
taunting_count,
taunting_count != 1 ? "have" : "has"
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots and their pets {} {} taunting.",
taunting_count,
taunting_count != 1 ? "have" : "has",
taunt_state ? "started" : "stopped"
).c_str()
);
}
} }
else { else {
c->Message(Chat::White, "None of your bots are capable of taunting"); c->Message(Chat::White, "None of your bots are capable of taunting");
@ -6600,14 +6705,7 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep)
if (c->GetBotOption(Client::booSpawnMessageSay)) { if (c->GetBotOption(Client::booSpawnMessageSay)) {
Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str());
} else if (c->GetBotOption(Client::booSpawnMessageTell)) { } else if (c->GetBotOption(Client::booSpawnMessageTell)) {
c->Message( my_bot->OwnerMessage(bot_spawn_message[message_index]);
Chat::Tell,
fmt::format(
"{} tells you, \"{}\"",
my_bot->GetCleanName(),
bot_spawn_message[message_index]
).c_str()
);
} }
} }
@ -6663,9 +6761,11 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
Bot::BotGroupSay( Bot::BotGroupSay(
bot_iter, bot_iter,
"My current stance is '%s' (%i)", fmt::format(
EQ::constants::GetStanceName(bot_iter->GetBotStance()), "My current stance is {} ({}).",
bot_iter->GetBotStance() EQ::constants::GetStanceName(bot_iter->GetBotStance()),
bot_iter->GetBotStance()
).c_str()
); );
} }
} }
@ -6719,41 +6819,63 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep)
void bot_subcommand_bot_summon(Client *c, const Seperator *sep) void bot_subcommand_bot_summon(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_subcommand_bot_summon", sep->arg[0], "botsummon")) if (helper_command_alias_fail(c, "bot_subcommand_bot_summon", sep->arg[0], "botsummon")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | botgroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); c->Message(
Chat::White,
fmt::format(
"Usage: {} ([actionable: target | byname | ownergroup | botgroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))",
sep->arg[0]
).c_str()
);
return; return;
} }
const int ab_mask = ActionableBots::ABM_NoFilter; const int ab_mask = ActionableBots::ABM_NoFilter;
std::list<Bot*> sbl; std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return; return;
}
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (!bot_iter) if (!bot_iter) {
continue; continue;
}
//Bot::BotGroupSay(bot_iter, "Whee!");
bot_iter->WipeHateList(); bot_iter->WipeHateList();
bot_iter->SetTarget(nullptr); bot_iter->SetTarget(nullptr);
bot_iter->Teleport(c->GetPosition()); bot_iter->Teleport(c->GetPosition());
bot_iter->DoAnim(0); bot_iter->DoAnim(0);
if (!bot_iter->HasPet()) if (!bot_iter->HasPet()) {
continue; continue;
}
bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->WipeHateList();
bot_iter->GetPet()->SetTarget(nullptr); bot_iter->GetPet()->SetTarget(nullptr);
bot_iter->GetPet()->Teleport(c->GetPosition()); bot_iter->GetPet()->Teleport(c->GetPosition());
} }
if (sbl.size() == 1) if (sbl.size() == 1) {
c->Message(Chat::White, "Summoned %s to you", ((sbl.front()) ? (sbl.front()->GetCleanName()) : ("'nullptr'"))); c->Message(
else Chat::White,
c->Message(Chat::White, "Summoned %i bots to you", sbl.size()); fmt::format(
"Summoned {} to you.",
sbl.front() ? sbl.front()->GetCleanName() : "no one"
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"Summoned {} bots to you.",
sbl.size()
).c_str()
);
}
} }
void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep) void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep)
@ -9182,15 +9304,13 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
break; break;
} }
c->Message( my_bot->OwnerMessage(
Chat::Tell,
fmt::format( fmt::format(
"{} tells you, 'My {} (Slot {}) {} already unequipped.'", "My {} (Slot {}) {} already unequipped.",
my_bot->GetCleanName(),
EQ::invslot::GetInvPossessionsSlotName(slot_id), EQ::invslot::GetInvPossessionsSlotName(slot_id),
slot_id, slot_id,
slot_message slot_message
).c_str() )
); );
return; return;
} }
@ -9247,15 +9367,13 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
my_bot->BotRemoveEquipItem(slot_id); my_bot->BotRemoveEquipItem(slot_id);
my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
c->Message( my_bot->OwnerMessage(
Chat::Tell,
fmt::format( fmt::format(
"{} tells you, 'I have unequipped {} from my {} (Slot {}).'", "I have unequipped {} from my {} (Slot {}).",
my_bot->GetCleanName(),
linker.GenerateLink(), linker.GenerateLink(),
EQ::invslot::GetInvPossessionsSlotName(slot_id), EQ::invslot::GetInvPossessionsSlotName(slot_id),
slot_id slot_id
).c_str() )
); );
} }
} }
@ -9890,8 +10008,16 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
return false; return false;
casting_bot->InterruptSpell(); casting_bot->InterruptSpell();
if (annouce_cast) if (annouce_cast) {
Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName()); Bot::BotGroupSay(
casting_bot,
fmt::format(
"Attempting to cast {} on {}.",
spells[spell_id].name,
target_mob->GetCleanName()
).c_str()
);
}
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1, dont_root_before); return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1, dont_root_before);
} }
@ -9919,54 +10045,115 @@ bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, c
void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag) void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag)
{ {
if (!bot_owner) if (!bot_owner) {
return; return;
}
if (!MyBots::IsMyBot(bot_owner, druid_bot)) if (!MyBots::IsMyBot(bot_owner, druid_bot)) {
druid_bot = nullptr; druid_bot = nullptr;
if (!MyBots::IsMyBot(bot_owner, wizard_bot)) }
if (!MyBots::IsMyBot(bot_owner, wizard_bot)) {
wizard_bot = nullptr; wizard_bot = nullptr;
}
if (!druid_bot && !wizard_bot) { if (!druid_bot && !wizard_bot) {
bot_owner->Message(Chat::White, "No bots are capable of performing this action"); bot_owner->Message(Chat::White, "No bots are capable of performing this action");
return; return;
} }
bot_owner->Message(Chat::White, "The following destinations are available:");
if (!local_list) { if (!local_list) {
bot_owner->Message(Chat::White, "None"); bot_owner->Message(Chat::White, "There are no destinations you can be taken to.");
return; return;
} }
std::string msg; std::string msg;
std::string text_link; std::string text_link;
int destinations = 0; auto destination_count = 0;
auto destination_number = 1;
for (auto list_iter : *local_list) { for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToDepart(); auto local_entry = list_iter->SafeCastToDepart();
if (!local_entry) if (!local_entry) {
continue;
if (druid_bot && druid_bot->GetClass() == local_entry->caster_class && druid_bot->GetLevel() >= local_entry->spell_level) {
if (local_entry->single != single_flag)
continue;
msg = StringFormat("%ccircle %s%s", BOT_COMMAND_CHAR, spells[local_entry->spell_id].teleport_zone, ((single_flag) ? (" single") : ("")));
text_link = druid_bot->CreateSayLink(bot_owner, msg.c_str(), local_entry->long_name.c_str());
Bot::BotGroupSay(druid_bot, "dest: '%s' click: %s", spells[local_entry->spell_id].teleport_zone, text_link.c_str());
++destinations;
continue; continue;
} }
if (wizard_bot && wizard_bot->GetClass() == local_entry->caster_class && wizard_bot->GetLevel() >= local_entry->spell_level) {
if (local_entry->single != single_flag) if (
druid_bot &&
druid_bot->GetClass() == local_entry->caster_class &&
druid_bot->GetLevel() >= local_entry->spell_level
) {
if (local_entry->single != single_flag) {
continue; continue;
msg = StringFormat("%cportal %s%s", BOT_COMMAND_CHAR, spells[local_entry->spell_id].teleport_zone, ((single_flag) ? (" single") : (""))); }
text_link = wizard_bot->CreateSayLink(bot_owner, msg.c_str(), local_entry->long_name.c_str());
Bot::BotGroupSay(wizard_bot, "dest: '%s' click: %s", spells[local_entry->spell_id].teleport_zone, text_link.c_str()); msg = fmt::format(
++destinations; "{}circle {}{}",
std::to_string(BOT_COMMAND_CHAR),
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
);
text_link = druid_bot->CreateSayLink(
bot_owner,
msg.c_str(),
"Goto"
);
druid_bot->OwnerMessage(
fmt::format(
"Destination {} | {} | {}",
destination_number,
local_entry->long_name,
text_link
).c_str()
);
destination_count++;
destination_number++;
continue;
}
if (
wizard_bot &&
wizard_bot->GetClass() == local_entry->caster_class &&
wizard_bot->GetLevel() >= local_entry->spell_level
) {
if (local_entry->single != single_flag) {
continue;
}
msg = fmt::format(
"{}portal {}{}",
std::to_string(BOT_COMMAND_CHAR),
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
);
text_link = wizard_bot->CreateSayLink(
bot_owner,
msg.c_str(),
"Goto"
);
wizard_bot->OwnerMessage(
fmt::format(
"Destination {} | {} | {}",
destination_number,
local_entry->long_name,
text_link
).c_str()
);
destination_count++;
destination_number++;
continue; continue;
} }
} }
if (!destinations)
bot_owner->Message(Chat::White, "None"); if (!destination_count) {
bot_owner->Message(Chat::White, "There are no destinations you can be taken to.");
}
} }
bool helper_is_help_or_usage(const char* arg) bool helper_is_help_or_usage(const char* arg)

View File

@ -98,8 +98,16 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
} }
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
if(castedSpell) if (castedSpell) {
BotGroupSay(this, "Attempting to mez %s.", addMob->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Attempting to mesmerize {} with {}.",
addMob->GetCleanName(),
spells[botSpell.SpellId].name
).c_str()
);
}
} }
break; break;
} }
@ -272,7 +280,13 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
Group *g = GetGroup(); Group *g = GetGroup();
if(g) { if(g) {
BotGroupSay(this, "Casting %s.", spells[botSpell.SpellId].name); BotGroupSay(
this,
fmt::format(
"Casting {}.",
spells[botSpell.SpellId].name
).c_str()
);
for( int i = 0; i<MAX_GROUP_MEMBERS; i++) { for( int i = 0; i<MAX_GROUP_MEMBERS; i++) {
if(g->members[i] && !g->members[i]->qglobal) { if(g->members[i] && !g->members[i]->qglobal) {
@ -281,10 +295,17 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
} }
} }
} }
} } else {
else { if (tar != this) { //we don't need spam of bots healing themselves
if(tar != this) //we don't need spam of bots healing themselves BotGroupSay(
BotGroupSay(this, "Casting %s on %s", spells[botSpell.SpellId].name, tar->GetCleanName()); this,
fmt::format(
"Casting {} on {}.",
spells[botSpell.SpellId].name,
tar->GetCleanName()
).c_str()
);
}
tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000); tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000);
} }
@ -892,8 +913,16 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
if(castedSpell && GetClass() != BARD) if (castedSpell && GetClass() != BARD) {
BotGroupSay(this, "Attempting to slow %s.", tar->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Attempting to slow {} with {}.",
tar->GetCleanName(),
spells[botSpell.SpellId].name
).c_str()
);
}
} }
break; break;
} }
@ -982,7 +1011,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
if (castedSpell) { if (castedSpell) {
BotGroupSay(this, "Attempting to reduce hate on %s.", tar->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Attempting to reduce hate on {} with {}.",
tar->GetCleanName(),
spells[iter.SpellId].name
).c_str()
);
break; break;
} }
} }
@ -1596,8 +1632,16 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
if(castedSpell) if (castedSpell) {
Say("Casting %s on %s, please stay in range!", spells[botSpell.SpellId].name, tar->GetCleanName()); BotGroupSay(
this,
fmt::format(
"Casting {} on {}, please stay in range!",
spells[botSpell.SpellId].name,
tar->GetCleanName()
).c_str()
);
}
return castedSpell; return castedSpell;
} }

View File

@ -1170,8 +1170,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
} }
Mob* sender = this; Mob* sender = this;
if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) {
sender = GetPet(); sender = GetPet();
}
if (!is_silent) { if (!is_silent) {
entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message);
@ -1179,36 +1180,51 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
parse->EventPlayer(EVENT_SAY, this, message, language); parse->EventPlayer(EVENT_SAY, this, message, language);
if (sender != this) if (sender != this) {
break; break;
}
if(quest_manager.ProximitySayInUse()) if (quest_manager.ProximitySayInUse()) {
entity_list.ProcessProximitySay(message, this, language); entity_list.ProcessProximitySay(message, this, language);
}
if (GetTarget() != 0 && GetTarget()->IsNPC() && if (
!IsInvisible(GetTarget())) { GetTarget() &&
if(!GetTarget()->CastToNPC()->IsEngaged()) { GetTarget()->IsNPC() &&
CheckLDoNHail(GetTarget()); !IsInvisible(GetTarget())
CheckEmoteHail(GetTarget(), message); ) {
auto* t = GetTarget()->CastToNPC();
if (!t->IsEngaged()) {
CheckLDoNHail(t);
CheckEmoteHail(t, message);
if(DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
NPC *tar = GetTarget()->CastToNPC();
parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); parse->EventNPC(EVENT_SAY, t, this, message, language);
if(RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(tar)) { if (UpdateTasksOnSpeakWith(t)) {
tar->DoQuestPause(this); t->DoQuestPause(this);
} }
} }
} }
} } else {
else { if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
if (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
parse->EventNPC(EVENT_AGGRO_SAY, GetTarget()->CastToNPC(), this, message, language);
} }
} }
} }
#ifdef BOTS
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) {
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
parse->EventBot(GetTarget()->IsEngaged() ? EVENT_AGGRO_SAY : EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
}
}
#endif
break; break;
} }
case ChatChannel_UCSRelay: case ChatChannel_UCSRelay:
@ -5470,9 +5486,9 @@ void Client::ShowSkillsWindow()
); );
} }
void Client::Signal(uint32 data) void Client::Signal(int signal_id)
{ {
std::string export_string = fmt::format("{}", data); const auto export_string = fmt::format("{}", signal_id);
parse->EventPlayer(EVENT_SIGNAL, this, export_string, 0); parse->EventPlayer(EVENT_SIGNAL, this, export_string, 0);
} }

View File

@ -1408,7 +1408,7 @@ public:
void SuspendMinion(int value); void SuspendMinion(int value);
void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration);
void NotifyNewTitlesAvailable(); void NotifyNewTitlesAvailable();
void Signal(uint32 data); void Signal(int signal_id);
Mob *GetBindSightTarget() { return bind_sight_target; } Mob *GetBindSightTarget() { return bind_sight_target; }
void SetBindSightTarget(Mob *n) { bind_sight_target = n; } void SetBindSightTarget(Mob *n) { bind_sight_target = n; }
const uint16 GetBoatID() const { return controlling_boat_id; } const uint16 GetBoatID() const { return controlling_boat_id; }

View File

@ -11200,13 +11200,19 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
break; break;
} }
std::string export_string = fmt::format("{}", popup_response->popupid); const auto export_string = fmt::format("{}", popup_response->popupid);
parse->EventPlayer(EVENT_POPUP_RESPONSE, this, export_string, 0); parse->EventPlayer(EVENT_POPUP_RESPONSE, this, export_string, 0);
Mob *Target = GetTarget(); auto t = GetTarget();
if (Target && Target->IsNPC()) { if (t) {
parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, export_string, 0); if (t->IsNPC()) {
parse->EventNPC(EVENT_POPUP_RESPONSE, t->CastToNPC(), this, export_string, 0);
#ifdef BOTS
} else if (t->IsBot()) {
parse->EventBot(EVENT_POPUP_RESPONSE, t->CastToBot(), this, export_string, 0);
#endif
}
} }
} }

View File

@ -163,6 +163,11 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_TASK_BEFORE_UPDATE", "EVENT_TASK_BEFORE_UPDATE",
"EVENT_AA_BUY", "EVENT_AA_BUY",
"EVENT_AA_GAIN" "EVENT_AA_GAIN"
#ifdef BOTS
,
"EVENT_SPELL_EFFECT_BOT",
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
#endif
}; };
PerlembParser::PerlembParser() : perl(nullptr) PerlembParser::PerlembParser() : perl(nullptr)
@ -170,6 +175,11 @@ PerlembParser::PerlembParser() : perl(nullptr)
global_npc_quest_status_ = questUnloaded; global_npc_quest_status_ = questUnloaded;
player_quest_status_ = questUnloaded; player_quest_status_ = questUnloaded;
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
#ifdef BOTS
bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded;
#endif
} }
PerlembParser::~PerlembParser() PerlembParser::~PerlembParser()
@ -203,15 +213,28 @@ void PerlembParser::ReloadQuests()
global_npc_quest_status_ = questUnloaded; global_npc_quest_status_ = questUnloaded;
player_quest_status_ = questUnloaded; player_quest_status_ = questUnloaded;
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
#ifdef BOTS
bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded;
#endif
item_quest_status_.clear(); item_quest_status_.clear();
spell_quest_status_.clear(); spell_quest_status_.clear();
} }
int PerlembParser::EventCommon( int PerlembParser::EventCommon(
QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct* spell, Mob *mob, QuestEventID event,
uint32 extradata, bool global, std::vector<std::any> *extra_pointers uint32 objid,
) const char *data,
{ Mob *npcmob,
EQ::ItemInstance *item_inst,
const SPDat_Spell_Struct* spell,
Mob *mob,
uint32 extradata,
bool global,
std::vector<std::any> *extra_pointers
) {
if (!perl) { if (!perl) {
return 0; return 0;
} }
@ -220,20 +243,46 @@ int PerlembParser::EventCommon(
return 0; return 0;
} }
bool isPlayerQuest = false; bool isPlayerQuest = false;
bool isGlobalPlayerQuest = false; bool isGlobalPlayerQuest = false;
bool isGlobalNPC = false; bool isGlobalNPC = false;
bool isItemQuest = false; bool isBotQuest = false;
bool isSpellQuest = false; bool isGlobalBotQuest = false;
bool isItemQuest = false;
bool isSpellQuest = false;
std::string package_name; std::string package_name;
GetQuestTypes( GetQuestTypes(
isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, isPlayerQuest,
event, npcmob, item_inst, mob, global isGlobalPlayerQuest,
isBotQuest,
isGlobalBotQuest,
isGlobalNPC,
isItemQuest,
isSpellQuest,
event,
npcmob,
item_inst,
mob,
global
); );
GetQuestPackageName( GetQuestPackageName(
isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, isPlayerQuest,
package_name, event, objid, data, npcmob, item_inst, global isGlobalPlayerQuest,
isBotQuest,
isGlobalBotQuest,
isGlobalNPC,
isItemQuest,
isSpellQuest,
package_name,
event,
objid,
data,
npcmob,
item_inst,
global
); );
const char *sub_name = QuestEventSubroutines[event]; const char *sub_name = QuestEventSubroutines[event];
@ -249,6 +298,8 @@ int PerlembParser::EventCommon(
ExportQGlobals( ExportQGlobals(
isPlayerQuest, isPlayerQuest,
isGlobalPlayerQuest, isGlobalPlayerQuest,
isBotQuest,
isGlobalBotQuest,
isGlobalNPC, isGlobalNPC,
isItemQuest, isItemQuest,
isSpellQuest, isSpellQuest,
@ -264,6 +315,8 @@ int PerlembParser::EventCommon(
ExportMobVariables( ExportMobVariables(
isPlayerQuest, isPlayerQuest,
isGlobalPlayerQuest, isGlobalPlayerQuest,
isBotQuest,
isGlobalBotQuest,
isGlobalNPC, isGlobalNPC,
isItemQuest, isItemQuest,
isSpellQuest, isSpellQuest,
@ -290,6 +343,8 @@ int PerlembParser::EventCommon(
if (isPlayerQuest || isGlobalPlayerQuest) { if (isPlayerQuest || isGlobalPlayerQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, nullptr);
} else if (isBotQuest || isGlobalBotQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr, nullptr);
} else if (isItemQuest) { } else if (isItemQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst, nullptr); return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst, nullptr);
} else if (isSpellQuest) { } else if (isSpellQuest) {
@ -304,19 +359,19 @@ int PerlembParser::EventCommon(
} }
int PerlembParser::EventNPC( int PerlembParser::EventNPC(
QuestEventID evt, NPC *npc, Mob *init, std::string data, uint32 extra_data, QuestEventID evt, NPC *npc, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
) )
{ {
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, false, extra_pointers); return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, mob, extra_data, false, extra_pointers);
} }
int PerlembParser::EventGlobalNPC( int PerlembParser::EventGlobalNPC(
QuestEventID evt, NPC *npc, Mob *init, std::string data, uint32 extra_data, QuestEventID evt, NPC *npc, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
) )
{ {
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, true, extra_pointers); return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, mob, extra_data, true, extra_pointers);
} }
int PerlembParser::EventPlayer( int PerlembParser::EventPlayer(
@ -345,11 +400,11 @@ int PerlembParser::EventItem(
} }
int PerlembParser::EventSpell( int PerlembParser::EventSpell(
QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, QuestEventID evt, Mob *mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
) )
{ {
return EventCommon(evt, spell_id, data.c_str(), npc, nullptr, &spells[spell_id], client, extra_data, false, extra_pointers); return EventCommon(evt, spell_id, data.c_str(), mob, nullptr, &spells[spell_id], client, extra_data, false, extra_pointers);
} }
bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt) bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt)
@ -860,77 +915,93 @@ int PerlembParser::SendCommands(
std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem";
std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell";
std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list";
#ifdef BOTS
std::string bot = (std::string) "$" + (std::string) pkgprefix + (std::string) "::bot";
#endif
if (clear_vars_.find(cl) != clear_vars_.end()) { if (clear_vars_.find(cl) != clear_vars_.end()) {
std::string eval_str = cl; auto e = fmt::format("{} = undef;", cl);
eval_str += " = undef;"; perl->eval(e.c_str());
perl->eval(eval_str.c_str());
} }
if (clear_vars_.find(np) != clear_vars_.end()) { if (clear_vars_.find(np) != clear_vars_.end()) {
std::string eval_str = np; auto e = fmt::format("{} = undef;", np);
eval_str += " = undef;"; perl->eval(e.c_str());
perl->eval(eval_str.c_str());
} }
if (clear_vars_.find(qi) != clear_vars_.end()) { if (clear_vars_.find(qi) != clear_vars_.end()) {
std::string eval_str = qi; auto e = fmt::format("{} = undef;", qi);
eval_str += " = undef;"; perl->eval(e.c_str());
perl->eval(eval_str.c_str());
} }
if (clear_vars_.find(sp) != clear_vars_.end()) { if (clear_vars_.find(sp) != clear_vars_.end()) {
std::string eval_str = sp; auto e = fmt::format("{} = undef;", sp);
eval_str += " = undef;"; perl->eval(e.c_str());
perl->eval(eval_str.c_str());
} }
if (clear_vars_.find(enl) != clear_vars_.end()) { if (clear_vars_.find(enl) != clear_vars_.end()) {
std::string eval_str = enl; auto e = fmt::format("{} = undef;", enl);
eval_str += " = undef;"; perl->eval(e.c_str());
perl->eval(eval_str.c_str());
} }
#ifdef BOTS
if (clear_vars_.find(bot) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", bot);
perl->eval(e.c_str());
}
#endif
} }
char namebuf[64]; std::string buf;
//init a couple special vars: client, npc, entity_list //init a couple special vars: client, npc, entity_list
Client *curc = quest_manager.GetInitiator(); Client *curc = quest_manager.GetInitiator();
snprintf(namebuf, 64, "%s::client", pkgprefix); buf = fmt::format("{}::client", pkgprefix);
SV *client = get_sv(namebuf, true); SV *client = get_sv(buf.c_str(), true);
if (curc != nullptr) { if (curc) {
sv_setref_pv(client, "Client", curc); sv_setref_pv(client, "Client", curc);
} } else {
else {
//clear out the value, mainly to get rid of blessedness //clear out the value, mainly to get rid of blessedness
sv_setsv(client, _empty_sv); sv_setsv(client, _empty_sv);
} }
//only export NPC if it's a npc quest //only export NPC if it's a npc quest
if (!other->IsClient()) { if (!other->IsClient() && other->IsNPC()) {
NPC *curn = quest_manager.GetNPC(); NPC *curn = quest_manager.GetNPC();
snprintf(namebuf, 64, "%s::npc", pkgprefix); buf = fmt::format("{}::npc", pkgprefix);
SV *npc = get_sv(namebuf, true); SV *npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", curn); sv_setref_pv(npc, "NPC", curn);
} }
#ifdef BOTS
if (!other->IsClient() && other->IsBot()) {
Bot *curb = quest_manager.GetBot();
buf = fmt::format("{}::bot", pkgprefix);
SV *bot = get_sv(buf.c_str(), true);
sv_setref_pv(bot, "Bot", curb);
}
#endif
//only export QuestItem if it's an item quest //only export QuestItem if it's an item quest
if (item_inst) { if (item_inst) {
EQ::ItemInstance *curi = quest_manager.GetQuestItem(); EQ::ItemInstance *curi = quest_manager.GetQuestItem();
snprintf(namebuf, 64, "%s::questitem", pkgprefix); buf = fmt::format("{}::questitem", pkgprefix);
SV *questitem = get_sv(namebuf, true); SV *questitem = get_sv(buf.c_str(), true);
sv_setref_pv(questitem, "QuestItem", curi); sv_setref_pv(questitem, "QuestItem", curi);
} }
if (spell) { if (spell) {
const SPDat_Spell_Struct* current_spell = quest_manager.GetQuestSpell(); const SPDat_Spell_Struct* current_spell = quest_manager.GetQuestSpell();
SPDat_Spell_Struct* real_spell = const_cast<SPDat_Spell_Struct*>(current_spell); SPDat_Spell_Struct* real_spell = const_cast<SPDat_Spell_Struct*>(current_spell);
snprintf(namebuf, 64, "%s::spell", pkgprefix); buf = fmt::format("{}::spell", pkgprefix);
SV *spell = get_sv(namebuf, true); SV *spell = get_sv(buf.c_str(), true);
sv_setref_pv(spell, "Spell", (void *) real_spell); sv_setref_pv(spell, "Spell", (void *) real_spell);
} }
snprintf(namebuf, 64, "%s::entity_list", pkgprefix);
SV *el = get_sv(namebuf, true); buf = fmt::format("{}::entity_list", pkgprefix);
SV *el = get_sv(buf.c_str(), true);
sv_setref_pv(el, "EntityList", &entity_list); sv_setref_pv(el, "EntityList", &entity_list);
#endif #endif
@ -944,11 +1015,20 @@ int PerlembParser::SendCommands(
std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem";
std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell";
std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list";
#ifdef BOTS
std::string bot = (std::string) "$" + (std::string) pkgprefix + (std::string) "::bot";
#endif
clear_vars_[cl] = 1; clear_vars_[cl] = 1;
clear_vars_[np] = 1; clear_vars_[np] = 1;
clear_vars_[qi] = 1; clear_vars_[qi] = 1;
clear_vars_[sp] = 1; clear_vars_[sp] = 1;
clear_vars_[enl] = 1; clear_vars_[enl] = 1;
#ifdef BOTS
clear_vars_[bot] = 1;
#endif
} }
#endif #endif
@ -967,19 +1047,16 @@ int PerlembParser::SendCommands(
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
if (!quest_manager.QuestsRunning()) { if (!quest_manager.QuestsRunning()) {
auto iter = clear_vars_.begin();
std::string eval_str; std::string eval_str;
while (iter != clear_vars_.end()) { for (const auto &v : clear_vars_) {
eval_str += iter->first; eval_str += fmt::format("{} = undef;", v.first);
eval_str += " = undef;";
++iter;
} }
clear_vars_.clear(); clear_vars_.clear();
try { try {
perl->eval(eval_str.c_str()); perl->eval(eval_str.c_str());
} } catch (std::string e) {
catch (std::string e) {
AddError( AddError(
fmt::format( fmt::format(
"Script Clear Error | Error [{}]", "Script Clear Error | Error [{}]",
@ -1023,29 +1100,61 @@ void PerlembParser::MapFunctions()
} }
void PerlembParser::GetQuestTypes( void PerlembParser::GetQuestTypes(
bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, bool &isPlayerQuest,
bool &isSpellQuest, QuestEventID event, NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob, bool global bool &isGlobalPlayerQuest,
) bool &isBotQuest,
{ bool &isGlobalBotQuest,
if (event == EVENT_SPELL_EFFECT_CLIENT || bool &isGlobalNPC,
bool &isItemQuest,
bool &isSpellQuest,
QuestEventID event,
Mob *npcmob,
EQ::ItemInstance *item_inst,
Mob *mob,
bool global
) {
if (
event == EVENT_SPELL_EFFECT_CLIENT ||
event == EVENT_SPELL_EFFECT_NPC || event == EVENT_SPELL_EFFECT_NPC ||
#ifdef BOTS
event == EVENT_SPELL_EFFECT_BOT ||
#endif
event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT || event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT ||
event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC || event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC ||
#ifdef BOTS
event == EVENT_SPELL_EFFECT_BUFF_TIC_BOT ||
#endif
event == EVENT_SPELL_FADE || event == EVENT_SPELL_FADE ||
event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) { event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE
) {
isSpellQuest = true; isSpellQuest = true;
} } else {
else { if (npcmob) {
if (!npcmob && mob) {
if (!item_inst) { if (!item_inst) {
if (global) { if (global) {
isGlobalPlayerQuest = true; if (npcmob->IsBot()) {
} isGlobalBotQuest = true;
else { }
isPlayerQuest = true; } else {
if (npcmob->IsBot()) {
isBotQuest = true;
}
} }
} else {
isItemQuest = true;
} }
else { } else if (!npcmob && mob) {
if (!item_inst) {
if (global) {
if (mob->IsClient()) {
isGlobalPlayerQuest = true;
}
} else {
if (mob->IsClient()) {
isPlayerQuest = true;
}
}
} else {
isItemQuest = true; isItemQuest = true;
} }
} }
@ -1055,6 +1164,8 @@ void PerlembParser::GetQuestTypes(
void PerlembParser::GetQuestPackageName( void PerlembParser::GetQuestPackageName(
bool &isPlayerQuest, bool &isPlayerQuest,
bool &isGlobalPlayerQuest, bool &isGlobalPlayerQuest,
bool &isBotQuest,
bool &isGlobalBotQuest,
bool &isGlobalNPC, bool &isGlobalNPC,
bool &isItemQuest, bool &isItemQuest,
bool &isSpellQuest, bool &isSpellQuest,
@ -1062,7 +1173,7 @@ void PerlembParser::GetQuestPackageName(
QuestEventID event, QuestEventID event,
uint32 objid, uint32 objid,
const char *data, const char *data,
NPC *npcmob, Mob *npcmob,
EQ::ItemInstance *item_inst, EQ::ItemInstance *item_inst,
bool global bool global
) )
@ -1070,6 +1181,8 @@ void PerlembParser::GetQuestPackageName(
if ( if (
!isPlayerQuest && !isPlayerQuest &&
!isGlobalPlayerQuest && !isGlobalPlayerQuest &&
!isBotQuest &&
!isGlobalBotQuest &&
!isItemQuest && !isItemQuest &&
!isSpellQuest !isSpellQuest
) { ) {
@ -1077,30 +1190,30 @@ void PerlembParser::GetQuestPackageName(
isGlobalNPC = true; isGlobalNPC = true;
package_name = "qst_global_npc"; package_name = "qst_global_npc";
} else { } else {
package_name = "qst_npc_"; package_name = fmt::format("qst_npc_{}", npcmob->GetNPCTypeID());
package_name += std::to_string(npcmob->GetNPCTypeID());
} }
} else if (isItemQuest) { } else if (isItemQuest) {
// need a valid EQ::ItemInstance pointer check here..unsure how to cancel this process // need a valid EQ::ItemInstance pointer check here..unsure how to cancel this process
const EQ::ItemData *item = item_inst->GetItem(); const EQ::ItemData *item = item_inst->GetItem();
package_name = "qst_item_"; package_name = fmt::format("qst_item_{}", item->ID);
package_name += std::to_string(item->ID);
} else if (isPlayerQuest) { } else if (isPlayerQuest) {
package_name = "qst_player"; package_name = "qst_player";
} else if (isGlobalPlayerQuest) { } else if (isGlobalPlayerQuest) {
package_name = "qst_global_player"; package_name = "qst_global_player";
} else if (isBotQuest) {
package_name = "qst_bot";
} else if (isGlobalBotQuest) {
package_name = "qst_global_bot";
} else { } else {
package_name = "qst_spell_"; package_name = fmt::format("qst_spell_{}", objid);
package_name += std::to_string(objid);
} }
} }
void PerlembParser::ExportCharID(const std::string &package_name, int &char_id, NPC *npcmob, Mob *mob) void PerlembParser::ExportCharID(const std::string &package_name, int &char_id, Mob *npcmob, Mob *mob)
{ {
if (mob && mob->IsClient()) { // some events like waypoint and spawn don't have a player involved if (mob && mob->IsClient()) { // some events like waypoint and spawn don't have a player involved
char_id = mob->CastToClient()->CharacterID(); char_id = mob->CastToClient()->CharacterID();
} } else {
else {
if (npcmob) { if (npcmob) {
char_id = -static_cast<int>(npcmob->GetNPCTypeID()); // make char id negative npc id as a fudge char_id = -static_cast<int>(npcmob->GetNPCTypeID()); // make char id negative npc id as a fudge
} }
@ -1112,29 +1225,54 @@ void PerlembParser::ExportCharID(const std::string &package_name, int &char_id,
} }
void PerlembParser::ExportQGlobals( void PerlembParser::ExportQGlobals(
bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, bool isPlayerQuest,
bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id bool isGlobalPlayerQuest,
) bool isBotQuest,
{ bool isGlobalBotQuest,
bool isGlobalNPC,
bool isItemQuest,
bool isSpellQuest,
std::string &package_name,
Mob *npcmob,
Mob *mob,
int char_id
) {
//NPC quest //NPC quest
if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { if (
!isPlayerQuest &&
!isGlobalPlayerQuest &&
!isBotQuest &&
!isGlobalBotQuest &&
!isItemQuest &&
!isSpellQuest
) {
//only export for npcs that are global enabled. //only export for npcs that are global enabled.
if (npcmob && npcmob->GetQglobal()) { if (npcmob && npcmob->GetQglobal()) {
std::map<std::string, std::string> globhash; std::map<std::string, std::string> globhash;
QGlobalCache *npc_c = nullptr; QGlobalCache *npc_c = nullptr;
QGlobalCache *char_c = nullptr; QGlobalCache *char_c = nullptr;
QGlobalCache *zone_c = nullptr; QGlobalCache *zone_c = nullptr;
//retrieve our globals //retrieve our globals
npc_c = npcmob->GetQGlobals(); if (npcmob) {
if (npcmob->IsNPC()) {
npc_c = npcmob->CastToNPC()->GetQGlobals();
} else if (npcmob->IsClient()) {
char_c = npcmob->CastToClient()->GetQGlobals();
}
}
if (mob && mob->IsClient()) { if (mob && mob->IsClient()) {
char_c = mob->CastToClient()->GetQGlobals(); char_c = mob->CastToClient()->GetQGlobals();
} }
zone_c = zone->GetQGlobals(); zone_c = zone->GetQGlobals();
if (!npc_c) { if (!npc_c) {
npc_c = npcmob->CreateQGlobals(); if (npcmob && npcmob->IsNPC()) {
npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); npc_c = npcmob->CastToNPC()->CreateQGlobals();
npc_c->LoadByNPCID(npcmob->GetNPCTypeID());
}
} }
if (!char_c) { if (!char_c) {
@ -1157,7 +1295,8 @@ void PerlembParser::ExportQGlobals(
npc_c->GetBucket(), npc_c->GetBucket(),
npcmob->GetNPCTypeID(), npcmob->GetNPCTypeID(),
char_id, char_id,
zone->GetZoneID()); zone->GetZoneID())
;
} }
if (char_c) { if (char_c) {
@ -1166,7 +1305,8 @@ void PerlembParser::ExportQGlobals(
char_c->GetBucket(), char_c->GetBucket(),
npcmob->GetNPCTypeID(), npcmob->GetNPCTypeID(),
char_id, char_id,
zone->GetZoneID()); zone->GetZoneID()
);
} }
if (zone_c) { if (zone_c) {
@ -1175,7 +1315,8 @@ void PerlembParser::ExportQGlobals(
zone_c->GetBucket(), zone_c->GetBucket(),
npcmob->GetNPCTypeID(), npcmob->GetNPCTypeID(),
char_id, char_id,
zone->GetZoneID()); zone->GetZoneID()
);
} }
auto iter = globalMap.begin(); auto iter = globalMap.begin();
@ -1184,19 +1325,20 @@ void PerlembParser::ExportQGlobals(
ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str());
++iter; ++iter;
} }
ExportHash(package_name.c_str(), "qglobals", globhash); ExportHash(package_name.c_str(), "qglobals", globhash);
} }
} } else {
else {
std::map<std::string, std::string> globhash; std::map<std::string, std::string> globhash;
QGlobalCache *char_c = nullptr; QGlobalCache *char_c = nullptr;
QGlobalCache *zone_c = nullptr; QGlobalCache *zone_c = nullptr;
//retrieve our globals //retrieve our globals
if (mob && mob->IsClient()) { if (mob && mob->IsClient()) {
char_c = mob->CastToClient()->GetQGlobals(); char_c = mob->CastToClient()->GetQGlobals();
} }
zone_c = zone->GetQGlobals();
zone_c = zone->GetQGlobals();
if (!char_c) { if (!char_c) {
if (mob && mob->IsClient()) { if (mob && mob->IsClient()) {
@ -1226,15 +1368,23 @@ void PerlembParser::ExportQGlobals(
ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str());
++iter; ++iter;
} }
ExportHash(package_name.c_str(), "qglobals", globhash); ExportHash(package_name.c_str(), "qglobals", globhash);
} }
} }
void PerlembParser::ExportMobVariables( void PerlembParser::ExportMobVariables(
bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, bool isPlayerQuest,
bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob bool isGlobalPlayerQuest,
) bool isBotQuest,
{ bool isGlobalBotQuest,
bool isGlobalNPC,
bool isItemQuest,
bool isSpellQuest,
std::string &package_name,
Mob *mob,
Mob *npcmob
) {
uint8 fac = 0; uint8 fac = 0;
if (mob && mob->IsClient()) { if (mob && mob->IsClient()) {
ExportVar(package_name.c_str(), "uguild_id", mob->CastToClient()->GuildID()); ExportVar(package_name.c_str(), "uguild_id", mob->CastToClient()->GuildID());
@ -1242,8 +1392,21 @@ void PerlembParser::ExportMobVariables(
ExportVar(package_name.c_str(), "status", mob->CastToClient()->Admin()); ExportVar(package_name.c_str(), "status", mob->CastToClient()->Admin());
} }
if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest) { #ifdef BOTS
if (mob && npcmob && mob->IsClient()) { if (mob && mob->IsBot()) {
ExportVar(package_name.c_str(), "bot_id", mob->CastToBot()->GetBotID());
ExportVar(package_name.c_str(), "bot_owner_char_id", mob->CastToBot()->GetBotOwnerCharacterID());
}
#endif
if (
!isPlayerQuest &&
!isGlobalPlayerQuest &&
!isBotQuest &&
!isGlobalBotQuest &&
!isItemQuest
) {
if (mob && mob->IsClient() && npcmob && npcmob->IsNPC()) {
Client *client = mob->CastToClient(); Client *client = mob->CastToClient();
fac = client->GetFactionLevel( fac = client->GetFactionLevel(
@ -1261,8 +1424,15 @@ void PerlembParser::ExportMobVariables(
ExportVar(package_name.c_str(), "userid", mob->GetID()); ExportVar(package_name.c_str(), "userid", mob->GetID());
} }
if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { if (
if (npcmob) { !isPlayerQuest &&
!isGlobalPlayerQuest &&
!isBotQuest &&
!isGlobalBotQuest &&
!isItemQuest &&
!isSpellQuest
) {
if (npcmob->IsNPC()) {
ExportVar(package_name.c_str(), "mname", npcmob->GetName()); ExportVar(package_name.c_str(), "mname", npcmob->GetName());
ExportVar(package_name.c_str(), "mobid", npcmob->GetID()); ExportVar(package_name.c_str(), "mobid", npcmob->GetID());
ExportVar(package_name.c_str(), "mlevel", npcmob->GetLevel()); ExportVar(package_name.c_str(), "mlevel", npcmob->GetLevel());
@ -1331,14 +1501,20 @@ void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob)
} }
void PerlembParser::ExportEventVariables( void PerlembParser::ExportEventVariables(
std::string &package_name, QuestEventID event, uint32 objid, const char *data, std::string &package_name,
NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob, uint32 extradata, std::vector<std::any> *extra_pointers QuestEventID event,
) uint32 objid,
{ const char *data,
Mob *npcmob,
EQ::ItemInstance *item_inst,
Mob *mob,
uint32 extradata,
std::vector<std::any> *extra_pointers
) {
switch (event) { switch (event) {
case EVENT_SAY: { case EVENT_SAY: {
if (npcmob && mob) { if (npcmob && npcmob->IsNPC() && mob) {
npcmob->DoQuestPause(mob); npcmob->CastToNPC()->DoQuestPause(mob);
} }
ExportVar(package_name.c_str(), "data", objid); ExportVar(package_name.c_str(), "data", objid);
@ -1568,8 +1744,15 @@ void PerlembParser::ExportEventVariables(
break; break;
} }
#ifdef BOTS
case EVENT_SPELL_EFFECT_BUFF_TIC_BOT:
#endif
case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT:
case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: case EVENT_SPELL_EFFECT_BUFF_TIC_NPC:
#ifdef BOTS
case EVENT_SPELL_EFFECT_BOT:
#endif
case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_CLIENT:
case EVENT_SPELL_EFFECT_NPC: case EVENT_SPELL_EFFECT_NPC:
case EVENT_SPELL_FADE: { case EVENT_SPELL_FADE: {
@ -1799,4 +1982,122 @@ void PerlembParser::ExportEventVariables(
} }
} }
#ifdef BOTS
void PerlembParser::LoadBotScript(std::string filename)
{
if (!perl) {
return;
}
if (bot_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_bot", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Bot Quest File [{}] Error [{}]",
filename,
e
)
);
bot_quest_status_ = questFailedToLoad;
return;
}
bot_quest_status_ = questLoaded;
}
void PerlembParser::LoadGlobalBotScript(std::string filename)
{
if (!perl) {
return;
}
if (global_bot_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_global_bot", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Global Bot Quest File [{}] Error [{}]",
filename,
e
)
);
global_bot_quest_status_ = questFailedToLoad;
return;
}
global_bot_quest_status_ = questLoaded;
}
bool PerlembParser::BotHasQuestSub(QuestEventID evt)
{
if (!perl) {
return false;
}
if (bot_quest_status_ != questLoaded) {
return false;
}
if (evt >= _LargestEventID) {
return false;
}
const char *subname = QuestEventSubroutines[evt];
return (perl->SubExists("qst_bot", subname));
}
bool PerlembParser::GlobalBotHasQuestSub(QuestEventID evt)
{
if (!perl) {
return false;
}
if (global_bot_quest_status_ != questLoaded) {
return false;
}
if (evt >= _LargestEventID) {
return false;
}
const char *subname = QuestEventSubroutines[evt];
return (perl->SubExists("qst_global_bot", subname));
}
int PerlembParser::EventBot(
QuestEventID evt,
Bot *bot,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
return EventCommon(evt, 0, data.c_str(), bot, nullptr, nullptr, mob, extra_data, false, extra_pointers);
}
int PerlembParser::EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
return EventCommon(evt, 0, data.c_str(), bot, nullptr, nullptr, mob, extra_data, true, extra_pointers);
}
#endif
#endif #endif

View File

@ -48,18 +48,73 @@ public:
PerlembParser(); PerlembParser();
~PerlembParser(); ~PerlembParser();
virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, virtual int EventNPC(
std::vector<std::any> *extra_pointers); QuestEventID evt,
virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, NPC* npc,
std::vector<std::any> *extra_pointers); Mob *init,
virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::string data,
std::vector<std::any> *extra_pointers); uint32 extra_data,
virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers
std::vector<std::any> *extra_pointers); );
virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, virtual int EventGlobalNPC(
std::vector<std::any> *extra_pointers); QuestEventID evt,
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, NPC* npc,
std::vector<std::any> *extra_pointers); Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventPlayer(
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventGlobalPlayer(
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventItem(
QuestEventID evt,
Client *client,
EQ::ItemInstance *item,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventSpell(
QuestEventID evt,
Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#ifdef BOTS
virtual int EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
virtual bool HasQuestSub(uint32 npcid, QuestEventID evt); virtual bool HasQuestSub(uint32 npcid, QuestEventID evt);
virtual bool HasGlobalQuestSub(QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt);
@ -68,6 +123,11 @@ public:
virtual bool SpellHasQuestSub(uint32 spell_id, QuestEventID evt); virtual bool SpellHasQuestSub(uint32 spell_id, QuestEventID evt);
virtual bool ItemHasQuestSub(EQ::ItemInstance *itm, QuestEventID evt); virtual bool ItemHasQuestSub(EQ::ItemInstance *itm, QuestEventID evt);
#ifdef BOTS
virtual bool BotHasQuestSub(QuestEventID evt);
virtual bool GlobalBotHasQuestSub(QuestEventID evt);
#endif
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
virtual void LoadPlayerScript(std::string filename); virtual void LoadPlayerScript(std::string filename);
@ -75,6 +135,11 @@ public:
virtual void LoadItemScript(std::string filename, EQ::ItemInstance *item); virtual void LoadItemScript(std::string filename, EQ::ItemInstance *item);
virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadSpellScript(std::string filename, uint32 spell_id);
#ifdef BOTS
virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename);
#endif
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
virtual void ReloadQuests(); virtual void ReloadQuests();
@ -91,25 +156,98 @@ private:
void ExportVar(const char* pkgprefix, const char* varname, const char* classname, void* value); void ExportVar(const char* pkgprefix, const char* varname, const char* classname, void* value);
void ExportVarComplex(const char *pkgprefix, const char *varname, const char *value); void ExportVarComplex(const char *pkgprefix, const char *varname, const char *value);
int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQ::ItemInstance* item_inst, const SPDat_Spell_Struct* spell, Mob* mob, int EventCommon(
uint32 extradata, bool global, std::vector<std::any> *extra_pointers); QuestEventID event,
int SendCommands(const char *pkgprefix, const char *event, uint32 spell_id, Mob* other, Mob* mob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct *spell); uint32 objid,
const char* data,
Mob* npcmob,
EQ::ItemInstance* item_inst,
const SPDat_Spell_Struct* spell,
Mob* mob,
uint32 extradata,
bool global,
std::vector<std::any> *extra_pointers
);
int SendCommands(
const char *pkgprefix,
const char *event,
uint32 spell_id,
Mob* other,
Mob* mob,
EQ::ItemInstance *item_inst,
const SPDat_Spell_Struct *spell
);
void MapFunctions(); void MapFunctions();
void GetQuestTypes(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, void GetQuestTypes(
bool &isSpellQuest, QuestEventID event, NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob, bool global); bool &isPlayerQuest,
void GetQuestPackageName(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, bool &isGlobalPlayerQuest,
bool &isSpellQuest, std::string &package_name, QuestEventID event, uint32 objid, const char * data, bool &isBotQuest,
NPC* npcmob, EQ::ItemInstance* item_inst, bool global); bool &isGlobalBotQuest,
void ExportCharID(const std::string &package_name, int &char_id, NPC *npcmob, Mob *mob); bool &isGlobalNPC,
void ExportQGlobals(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, bool &isItemQuest,
bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id); bool &isSpellQuest,
void ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, QuestEventID event,
bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob); Mob* npcmob,
EQ::ItemInstance* item_inst,
Mob* mob,
bool global
);
void GetQuestPackageName(
bool &isPlayerQuest,
bool &isGlobalPlayerQuest,
bool &isBotQuest,
bool &isGlobalBotQuest,
bool &isGlobalNPC,
bool &isItemQuest,
bool &isSpellQuest,
std::string &package_name,
QuestEventID event,
uint32 objid,
const char * data,
Mob* npcmob,
EQ::ItemInstance* item_inst,
bool global
);
void ExportCharID(const std::string &package_name, int &char_id, Mob *npcmob, Mob *mob);
void ExportQGlobals(
bool isPlayerQuest,
bool isGlobalPlayerQuest,
bool isBotQuest,
bool isGlobalBotQuest,
bool isGlobalNPC,
bool isItemQuest,
bool isSpellQuest,
std::string &package_name,
Mob *npcmob,
Mob *mob,
int char_id
);
void ExportMobVariables(
bool isPlayerQuest,
bool isGlobalPlayerQuest,
bool isBotQuest,
bool isGlobalBotQuest,
bool isGlobalNPC,
bool isItemQuest,
bool isSpellQuest,
std::string &package_name,
Mob *mob,
Mob *npcmob
);
void ExportZoneVariables(std::string &package_name); void ExportZoneVariables(std::string &package_name);
void ExportItemVariables(std::string &package_name, Mob *mob); void ExportItemVariables(std::string &package_name, Mob *mob);
void ExportEventVariables(std::string &package_name, QuestEventID event, uint32 objid, const char * data, void ExportEventVariables(
NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob, uint32 extradata, std::vector<std::any> *extra_pointers); std::string &package_name,
QuestEventID event,
uint32 objid,
const char* data,
Mob* npcmob,
EQ::ItemInstance* item_inst,
Mob* mob,
uint32 extradata,
std::vector<std::any> *extra_pointers
);
std::map<uint32, PerlQuestStatus> npc_quest_status_; std::map<uint32, PerlQuestStatus> npc_quest_status_;
PerlQuestStatus global_npc_quest_status_; PerlQuestStatus global_npc_quest_status_;
@ -118,6 +256,11 @@ private:
std::map<uint32, PerlQuestStatus> item_quest_status_; std::map<uint32, PerlQuestStatus> item_quest_status_;
std::map<uint32, PerlQuestStatus> spell_quest_status_; std::map<uint32, PerlQuestStatus> spell_quest_status_;
#ifdef BOTS
PerlQuestStatus bot_quest_status_;
PerlQuestStatus global_bot_quest_status_;
#endif
std::map<std::string, std::string> vars_; std::map<std::string, std::string> vars_;
SV *_empty_sv; SV *_empty_sv;
std::map<std::string, int> clear_vars_; std::map<std::string, int> clear_vars_;

View File

@ -3134,38 +3134,38 @@ void Perl__crosszonesetentityvariablebynpctypeid(int npc_id, const char* variabl
quest_manager.CrossZoneSetEntityVariable(CZUpdateType_NPC, npc_id, variable_name, variable_value); quest_manager.CrossZoneSetEntityVariable(CZUpdateType_NPC, npc_id, variable_name, variable_value);
} }
void Perl__crosszonesignalclientbycharid(int character_id, uint32 signal) void Perl__crosszonesignalclientbycharid(int character_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_Character, character_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_Character, character_id, signal);
} }
void Perl__crosszonesignalclientbygroupid(int group_id, uint32 signal) void Perl__crosszonesignalclientbygroupid(int group_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_Group, group_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_Group, group_id, signal);
} }
void Perl__crosszonesignalclientbyraidid(int raid_id, uint32 signal) void Perl__crosszonesignalclientbyraidid(int raid_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_Raid, raid_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_Raid, raid_id, signal);
} }
void Perl__crosszonesignalclientbyguildid(int guild_id, uint32 signal) void Perl__crosszonesignalclientbyguildid(int guild_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_Guild, guild_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_Guild, guild_id, signal);
} }
void Perl__crosszonesignalclientbyexpeditionid(uint32 expedition_id, uint32 signal) void Perl__crosszonesignalclientbyexpeditionid(uint32 expedition_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_Expedition, expedition_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_Expedition, expedition_id, signal);
} }
void Perl__crosszonesignalclientbyname(const char* client_name, uint32 signal) void Perl__crosszonesignalclientbyname(const char* client_name, int signal)
{ {
int update_identifier = 0; int update_identifier = 0;
quest_manager.CrossZoneSignal(CZUpdateType_Expedition, update_identifier, signal, client_name); quest_manager.CrossZoneSignal(CZUpdateType_Expedition, update_identifier, signal, client_name);
} }
void Perl__crosszonesignalnpcbynpctypeid(uint32 npc_id, uint32 signal) void Perl__crosszonesignalnpcbynpctypeid(uint32 npc_id, int signal)
{ {
quest_manager.CrossZoneSignal(CZUpdateType_NPC, npc_id, signal); quest_manager.CrossZoneSignal(CZUpdateType_NPC, npc_id, signal);
} }
@ -3599,22 +3599,22 @@ void Perl__worldwidesetentityvariablenpc(const char* variable_name, const char*
quest_manager.WorldWideSetEntityVariable(WWSetEntityVariableUpdateType_NPC, variable_name, variable_value); quest_manager.WorldWideSetEntityVariable(WWSetEntityVariableUpdateType_NPC, variable_name, variable_value);
} }
void Perl__worldwidesignalnpc(uint32 signal) void Perl__worldwidesignalnpc(int signal)
{ {
quest_manager.WorldWideSignal(WWSignalUpdateType_NPC, signal); quest_manager.WorldWideSignal(WWSignalUpdateType_NPC, signal);
} }
void Perl__worldwidesignalclient(uint32 signal) void Perl__worldwidesignalclient(int signal)
{ {
quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal); quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal);
} }
void Perl__worldwidesignalclient(uint32 signal, uint8 min_status) void Perl__worldwidesignalclient(int signal, uint8 min_status)
{ {
quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal, min_status); quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal, min_status);
} }
void Perl__worldwidesignalclient(uint32 signal, uint8 min_status, uint8 max_status) void Perl__worldwidesignalclient(int signal, uint8 min_status, uint8 max_status)
{ {
quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal, min_status, max_status); quest_manager.WorldWideSignal(WWSignalUpdateType_Character, signal, min_status, max_status);
} }
@ -4163,9 +4163,9 @@ void perl_register_quest()
package.add("worldwidesetentityvariableclient", (void(*)(const char*, const char*, uint8))&Perl__worldwidesetentityvariableclient); package.add("worldwidesetentityvariableclient", (void(*)(const char*, const char*, uint8))&Perl__worldwidesetentityvariableclient);
package.add("worldwidesetentityvariableclient", (void(*)(const char*, const char*, uint8, uint8))&Perl__worldwidesetentityvariableclient); package.add("worldwidesetentityvariableclient", (void(*)(const char*, const char*, uint8, uint8))&Perl__worldwidesetentityvariableclient);
package.add("worldwidesetentityvariablenpc", &Perl__worldwidesetentityvariablenpc); package.add("worldwidesetentityvariablenpc", &Perl__worldwidesetentityvariablenpc);
package.add("worldwidesignalclient", (void(*)(uint32))&Perl__worldwidesignalclient); package.add("worldwidesignalclient", (void(*)(int))&Perl__worldwidesignalclient);
package.add("worldwidesignalclient", (void(*)(uint32, uint8))&Perl__worldwidesignalclient); package.add("worldwidesignalclient", (void(*)(int, uint8))&Perl__worldwidesignalclient);
package.add("worldwidesignalclient", (void(*)(uint32, uint8, uint8))&Perl__worldwidesignalclient); package.add("worldwidesignalclient", (void(*)(int, uint8, uint8))&Perl__worldwidesignalclient);
package.add("worldwidesignalnpc", &Perl__worldwidesignalnpc); package.add("worldwidesignalnpc", &Perl__worldwidesignalnpc);
package.add("worldwideupdateactivity", (void(*)(uint32, int))&Perl__worldwideupdateactivity); package.add("worldwideupdateactivity", (void(*)(uint32, int))&Perl__worldwideupdateactivity);
package.add("worldwideupdateactivity", (void(*)(uint32, int, int))&Perl__worldwideupdateactivity); package.add("worldwideupdateactivity", (void(*)(uint32, int, int))&Perl__worldwideupdateactivity);

View File

@ -5100,14 +5100,12 @@ void EntityList::GateAllClients()
} }
} }
void EntityList::SignalAllClients(uint32 data) void EntityList::SignalAllClients(int signal_id)
{ {
auto it = client_list.begin(); for (const auto& c : client_list) {
while (it != client_list.end()) { if (c.second) {
Client *ent = it->second; c.second->Signal(signal_id);
if (ent) }
ent->Signal(data);
++it;
} }
} }
@ -5159,8 +5157,9 @@ void EntityList::GetClientList(std::list<Client *> &c_list)
void EntityList::GetBotList(std::list<Bot *> &b_list) void EntityList::GetBotList(std::list<Bot *> &b_list)
{ {
b_list.clear(); b_list.clear();
for (auto bot : bot_list) {
b_list.push_back(bot); for (const auto& b : bot_list) {
b_list.push_back(b);
} }
} }
@ -5172,16 +5171,12 @@ std::vector<Bot *> EntityList::GetBotListByCharacterID(uint32 character_id, uint
return client_bot_list; return client_bot_list;
} }
for (auto bot : bot_list) { for (const auto& b : bot_list) {
if ( if (
bot->GetOwner() && b->GetOwner() &&
bot->GetBotOwnerCharacterID() == character_id && b->GetBotOwnerCharacterID() == character_id
(
!class_id ||
bot->GetClass() == class_id
)
) { ) {
client_bot_list.push_back(bot); client_bot_list.push_back(b);
} }
} }
@ -5196,14 +5191,45 @@ std::vector<Bot *> EntityList::GetBotListByClientName(std::string client_name)
return client_bot_list; return client_bot_list;
} }
for (auto bot : bot_list) { for (const auto& b : bot_list) {
if (bot->GetOwner() && Strings::ToLower(bot->GetOwner()->GetCleanName()) == Strings::ToLower(client_name)) { if (
client_bot_list.push_back(bot); b->GetOwner() &&
Strings::ToLower(b->GetOwner()->GetCleanName()) == Strings::ToLower(client_name)
) {
client_bot_list.push_back(b);
} }
} }
return client_bot_list; return client_bot_list;
} }
void EntityList::SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id)
{
auto client_bot_list = GetBotListByCharacterID(character_id);
if (client_bot_list.empty()) {
return;
}
for (const auto& b : client_bot_list) {
b->SignalBot(signal_id);
}
}
void EntityList::SignalBotByBotID(uint32 bot_id, int signal_id)
{
auto b = GetBotByBotID(bot_id);
if (b) {
b->SignalBot(signal_id);
}
}
void EntityList::SignalBotByBotName(std::string bot_name, int signal_id)
{
auto b = GetBotByBotName(bot_name);
if (b) {
b->SignalBot(signal_id);
}
}
#endif #endif
void EntityList::GetCorpseList(std::list<Corpse *> &c_list) void EntityList::GetCorpseList(std::list<Corpse *> &c_list)

View File

@ -508,7 +508,7 @@ public:
void UnMarkNPC(uint16 ID); void UnMarkNPC(uint16 ID);
void GateAllClients(); void GateAllClients();
void SignalAllClients(uint32 data); void SignalAllClients(int signal_id);
void UpdateQGlobal(uint32 qid, QGlobal newGlobal); void UpdateQGlobal(uint32 qid, QGlobal newGlobal);
void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID);
void SendFindableNPCList(Client *c); void SendFindableNPCList(Client *c);
@ -537,6 +537,9 @@ public:
inline const std::list<Bot *> &GetBotList() { return bot_list; } inline const std::list<Bot *> &GetBotList() { return bot_list; }
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = 0); std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = 0);
std::vector<Bot *> GetBotListByClientName(std::string client_name); std::vector<Bot *> GetBotListByClientName(std::string client_name);
void SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id);
void SignalBotByBotID(uint32 bot_id, int signal_id);
void SignalBotByBotName(std::string bot_name, int signal_id);
#endif #endif
inline const std::unordered_map<uint16, Corpse *> &GetCorpseList() { return corpse_list; } inline const std::unordered_map<uint16, Corpse *> &GetCorpseList() { return corpse_list; }
inline const std::unordered_map<uint16, Object *> &GetObjectList() { return object_list; } inline const std::unordered_map<uint16, Object *> &GetObjectList() { return object_list; }
@ -605,7 +608,7 @@ private:
// Please Do Not Declare Any EntityList Class Members After This Comment // Please Do Not Declare Any EntityList Class Members After This Comment
#ifdef BOTS #ifdef BOTS
public: public:
void AddBot(Bot* newBot, bool SendSpawnPacket = true, bool dontqueue = false); void AddBot(Bot* new_bot, bool send_spawn_packet = true, bool dont_queue = false);
bool RemoveBot(uint16 entityID); bool RemoveBot(uint16 entityID);
Mob* GetMobByBotID(uint32 botID); Mob* GetMobByBotID(uint32 botID);
Bot* GetBotByBotID(uint32 botID); Bot* GetBotByBotID(uint32 botID);

View File

@ -106,6 +106,10 @@ typedef enum {
EVENT_TASK_BEFORE_UPDATE, EVENT_TASK_BEFORE_UPDATE,
EVENT_AA_BUY, EVENT_AA_BUY,
EVENT_AA_GAIN, EVENT_AA_GAIN,
#ifdef BOTS
EVENT_SPELL_EFFECT_BOT,
EVENT_SPELL_EFFECT_BUFF_TIC_BOT,
#endif
_LargestEventID _LargestEventID
} QuestEventID; } QuestEventID;

View File

@ -84,6 +84,16 @@ uint32 Lua_Bot::GetBotItemIDBySlot(uint16 slot_id) {
return self->GetBotItemBySlot(slot_id); return self->GetBotItemBySlot(slot_id);
} }
void Lua_Bot::SignalBot(int signal_id) {
Lua_Safe_Call_Void();
self->SignalBot(signal_id);
}
void Lua_Bot::OwnerMessage(std::string message) {
Lua_Safe_Call_Void();
self->OwnerMessage(message);
}
int Lua_Bot::GetExpansionBitmask() { int Lua_Bot::GetExpansionBitmask() {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->GetExpansionBitmask(); return self->GetExpansionBitmask();
@ -117,9 +127,11 @@ luabind::scope lua_register_bot() {
.def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask) .def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask)
.def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner) .def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner)
.def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem) .def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
.def("OwnerMessage", (void(Lua_Bot::*)(std::string))&Lua_Bot::OwnerMessage)
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem) .def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask) .def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask); .def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask)
.def("SignalBot", (void(Lua_Bot::*)(int))&Lua_Bot::SignalBot);
} }
#endif #endif

View File

@ -42,9 +42,11 @@ public:
int GetExpansionBitmask(); int GetExpansionBitmask();
Lua_Mob GetOwner(); Lua_Mob GetOwner();
bool HasBotItem(uint32 item_id); bool HasBotItem(uint32 item_id);
void OwnerMessage(std::string message);
void RemoveBotItem(uint32 item_id); void RemoveBotItem(uint32 item_id);
void SetExpansionBitmask(int expansion_bitmask); void SetExpansionBitmask(int expansion_bitmask);
void SetExpansionBitmask(int expansion_bitmask, bool save); void SetExpansionBitmask(int expansion_bitmask, bool save);
void SignalBot(int signal_id);
}; };
#endif #endif

View File

@ -1515,9 +1515,9 @@ void Lua_Client::NotifyNewTitlesAvailable() {
self->NotifyNewTitlesAvailable(); self->NotifyNewTitlesAvailable();
} }
void Lua_Client::Signal(uint32 id) { void Lua_Client::Signal(int signal_id) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->Signal(id); self->Signal(signal_id);
} }
void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) { void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) {
@ -3108,7 +3108,7 @@ luabind::scope lua_register_client() {
.def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint) .def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint)
.def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix) .def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix)
.def("SetZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::SetZoneFlag) .def("SetZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::SetZoneFlag)
.def("Signal", (void(Lua_Client::*)(uint32))&Lua_Client::Signal) .def("Signal", (void(Lua_Client::*)(int))&Lua_Client::Signal)
.def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit) .def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit)
.def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand)
.def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems)

View File

@ -363,7 +363,7 @@ public:
uint32 GetMoney(uint8 type, uint8 subtype); uint32 GetMoney(uint8 type, uint8 subtype);
void OpenLFGuildWindow(); void OpenLFGuildWindow();
void NotifyNewTitlesAvailable(); void NotifyNewTitlesAvailable();
void Signal(uint32 id); void Signal(int signal_id);
void AddAlternateCurrencyValue(uint32 currency, int amount); void AddAlternateCurrencyValue(uint32 currency, int amount);
void SetAlternateCurrencyValue(uint32 currency, int amount); void SetAlternateCurrencyValue(uint32 currency, int amount);
int GetAlternateCurrencyValue(uint32 currency); int GetAlternateCurrencyValue(uint32 currency);

View File

@ -440,6 +440,21 @@ Lua_Bot_List Lua_EntityList::GetBotListByClientName(std::string client_name) {
return ret; return ret;
} }
void Lua_EntityList::SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id) {
Lua_Safe_Call_Void();
self->SignalAllBotsByOwnerCharacterID(character_id, signal_id);
}
void Lua_EntityList::SignalBotByBotID(uint32 bot_id, int signal_id) {
Lua_Safe_Call_Void();
self->SignalBotByBotID(bot_id, signal_id);
}
void Lua_EntityList::SignalBotByBotName(std::string bot_name, int signal_id) {
Lua_Safe_Call_Void();
self->SignalBotByBotName(bot_name, signal_id);
}
#endif #endif
Lua_Client_List Lua_EntityList::GetShuffledClientList() { Lua_Client_List Lua_EntityList::GetShuffledClientList() {
@ -690,7 +705,14 @@ luabind::scope lua_register_entity_list() {
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets) .def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets)
.def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers) .def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers)
.def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget) .def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget)
#ifdef BOTS
.def("SignalAllBotsByOwnerCharacterID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalAllBotsByOwnerCharacterID)
#endif
.def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients) .def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients)
#ifdef BOTS
.def("SignalBotByBotID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalBotByBotID)
.def("SignalBotByBotName", (void(Lua_EntityList::*)(std::string, int))&Lua_EntityList::SignalBotByBotName)
#endif
.def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID); .def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID);
} }

View File

@ -140,6 +140,9 @@ public:
Lua_Bot GetRandomBot(); Lua_Bot GetRandomBot();
Lua_Bot GetRandomBot(float x, float y, float z, float distance); Lua_Bot GetRandomBot(float x, float y, float z, float distance);
Lua_Bot GetRandomBot(float x, float y, float z, float distance, Lua_Bot exclude_bot); Lua_Bot GetRandomBot(float x, float y, float z, float distance, Lua_Bot exclude_bot);
void SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id);
void SignalBotByBotID(uint32 bot_id, int signal_id);
void SignalBotByBotName(std::string bot_name, int signal_id);
#endif #endif
}; };

View File

@ -6,6 +6,7 @@
#include "client.h" #include "client.h"
#include "npc.h" #include "npc.h"
#ifdef BOTS #ifdef BOTS
#include "bot.h"
#include "lua_bot.h" #include "lua_bot.h"
#endif #endif
#include "lua_item.h" #include "lua_item.h"
@ -1371,13 +1372,17 @@ bool Lua_Mob::EntityVariableExists(const char *name) {
return self->EntityVariableExists(name); return self->EntityVariableExists(name);
} }
void Lua_Mob::Signal(uint32 id) { void Lua_Mob::Signal(int signal_id) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
if(self->IsClient()) { if (self->IsClient()) {
self->CastToClient()->Signal(id); self->CastToClient()->Signal(signal_id);
} else if(self->IsNPC()) { } else if (self->IsNPC()) {
self->CastToNPC()->SignalNPC(id); self->CastToNPC()->SignalNPC(signal_id);
#ifdef BOTS
} else if (self->IsBot()) {
self->CastToBot()->SignalBot(signal_id);
#endif
} }
} }
@ -3100,7 +3105,7 @@ luabind::scope lua_register_mob() {
.def("SetTexture", (void(Lua_Mob::*)(int))&Lua_Mob::SetTexture) .def("SetTexture", (void(Lua_Mob::*)(int))&Lua_Mob::SetTexture)
.def("Shout", (void(Lua_Mob::*)(const char*))& Lua_Mob::Shout) .def("Shout", (void(Lua_Mob::*)(const char*))& Lua_Mob::Shout)
.def("Shout", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Shout) .def("Shout", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Shout)
.def("Signal", (void(Lua_Mob::*)(uint32))&Lua_Mob::Signal) .def("Signal", (void(Lua_Mob::*)(int))&Lua_Mob::Signal)
.def("SpellEffect", &Lua_Mob::SpellEffect) .def("SpellEffect", &Lua_Mob::SpellEffect)
.def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::SpellFinished)
.def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int))&Lua_Mob::SpellFinished)

View File

@ -304,7 +304,7 @@ public:
const char* GetEntityVariable(const char *name); const char* GetEntityVariable(const char *name);
void SetEntityVariable(const char *name, const char *value); void SetEntityVariable(const char *name, const char *value);
bool EntityVariableExists(const char *name); bool EntityVariableExists(const char *name);
void Signal(uint32 id); void Signal(int signal_id);
bool CombatRange(Lua_Mob other); bool CombatRange(Lua_Mob other);
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage); void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage);
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage); void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage);

View File

@ -12,9 +12,9 @@ struct Lua_NPC_Loot_List {
std::vector<uint32> entries; std::vector<uint32> entries;
}; };
void Lua_NPC::Signal(int id) { void Lua_NPC::Signal(int signal_id) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->SignalNPC(id); self->SignalNPC(signal_id);
} }
int Lua_NPC::CheckNPCFactionAlly(int faction) { int Lua_NPC::CheckNPCFactionAlly(int faction) {

View File

@ -29,7 +29,7 @@ public:
return reinterpret_cast<NPC*>(GetLuaPtrData()); return reinterpret_cast<NPC*>(GetLuaPtrData());
} }
void Signal(int id); void Signal(int signal_id);
int CheckNPCFactionAlly(int faction); int CheckNPCFactionAlly(int faction);
void AddItem(int item_id, int charges); void AddItem(int item_id, int charges);
void AddItem(int item_id, int charges, bool equip); void AddItem(int item_id, int charges, bool equip);

View File

@ -171,6 +171,10 @@ LuaParser::LuaParser() {
ItemArgumentDispatch[i] = handle_item_null; ItemArgumentDispatch[i] = handle_item_null;
SpellArgumentDispatch[i] = handle_spell_null; SpellArgumentDispatch[i] = handle_spell_null;
EncounterArgumentDispatch[i] = handle_encounter_null; EncounterArgumentDispatch[i] = handle_encounter_null;
#ifdef BOTS
BotArgumentDispatch[i] = handle_bot_null;
#endif
} }
NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say; NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say;
@ -277,6 +281,22 @@ LuaParser::LuaParser() {
EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load; EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load;
EncounterArgumentDispatch[EVENT_ENCOUNTER_UNLOAD] = handle_encounter_unload; EncounterArgumentDispatch[EVENT_ENCOUNTER_UNLOAD] = handle_encounter_unload;
#ifdef BOTS
BotArgumentDispatch[EVENT_CAST] = handle_bot_cast;
BotArgumentDispatch[EVENT_CAST_BEGIN] = handle_bot_cast;
BotArgumentDispatch[EVENT_CAST_ON] = handle_bot_cast;
BotArgumentDispatch[EVENT_COMBAT] = handle_bot_combat;
BotArgumentDispatch[EVENT_DEATH] = handle_bot_death;
BotArgumentDispatch[EVENT_DEATH_COMPLETE] = handle_bot_death;
BotArgumentDispatch[EVENT_POPUP_RESPONSE] = handle_bot_popup_response;
BotArgumentDispatch[EVENT_SAY] = handle_bot_say;
BotArgumentDispatch[EVENT_SIGNAL] = handle_bot_signal;
BotArgumentDispatch[EVENT_SLAY] = handle_bot_slay;
BotArgumentDispatch[EVENT_TARGET_CHANGE] = handle_bot_target_change;
BotArgumentDispatch[EVENT_TIMER] = handle_bot_timer;
BotArgumentDispatch[EVENT_USE_SKILL] = handle_bot_use_skill;
#endif
L = nullptr; L = nullptr;
} }
@ -566,7 +586,7 @@ int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *cl
return 0; return 0;
} }
int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int LuaParser::EventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt); evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) { if(evt >= _LargestEventID) {
@ -579,10 +599,10 @@ int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spe
return 0; return 0;
} }
return _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers); return _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers);
} }
int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) { std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) {
const char *sub_name = LuaEvents[evt]; const char *sub_name = LuaEvents[evt];
@ -613,9 +633,9 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc,
lua_setfield(L, -2, "self"); lua_setfield(L, -2, "self");
auto arg_function = SpellArgumentDispatch[evt]; auto arg_function = SpellArgumentDispatch[evt];
arg_function(this, L, npc, client, spell_id, data, extra_data, extra_pointers); arg_function(this, L, mob, client, spell_id, data, extra_data, extra_pointers);
quest_manager.StartQuest(npc, client, nullptr, const_cast<SPDat_Spell_Struct*>(&spells[spell_id])); quest_manager.StartQuest(mob, client, nullptr, const_cast<SPDat_Spell_Struct*>(&spells[spell_id]));
if(lua_pcall(L, 1, 1, 0)) { if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1); std::string error = lua_tostring(L, -1);
AddError(error); AddError(error);
@ -1318,7 +1338,7 @@ int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInsta
return ret; return ret;
} }
int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int LuaParser::DispatchEventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt); evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) { if(evt >= _LargestEventID) {
@ -1334,7 +1354,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui
while(riter != iter->second.end()) { while(riter != iter->second.end()) {
if(riter->event_id == evt) { if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name; std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference); int i = _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0) { if(i != 0) {
ret = i; ret = i;
} }
@ -1352,7 +1372,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui
while(riter != iter->second.end()) { while(riter != iter->second.end()) {
if(riter->event_id == evt) { if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name; std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference); int i = _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0) if(i != 0)
ret = i; ret = i;
} }
@ -1367,10 +1387,16 @@ QuestEventID LuaParser::ConvertLuaEvent(QuestEventID evt) {
case EVENT_NPC_SLAY: case EVENT_NPC_SLAY:
return EVENT_SLAY; return EVENT_SLAY;
break; break;
#ifdef BOTS
case EVENT_SPELL_EFFECT_BOT:
#endif
case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_CLIENT:
case EVENT_SPELL_EFFECT_NPC: case EVENT_SPELL_EFFECT_NPC:
return EVENT_SPELL_EFFECT_CLIENT; return EVENT_SPELL_EFFECT_CLIENT;
break; break;
#ifdef BOTS
case EVENT_SPELL_EFFECT_BUFF_TIC_BOT:
#endif
case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT:
case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: case EVENT_SPELL_EFFECT_BUFF_TIC_NPC:
return EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT; return EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT;
@ -1457,4 +1483,186 @@ uint32 LuaParser::GetExperienceForKill(Client *self, Mob *against, bool &ignoreD
return retval; return retval;
} }
#ifdef BOTS
int LuaParser::EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!bot) {
return 0;
}
if (!BotHasQuestSub(evt)) {
return 0;
}
return _EventBot("bot", evt, bot, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!bot) {
return 0;
}
if (!GlobalBotHasQuestSub(evt)) {
return 0;
}
return _EventBot("global_bot", evt, bot, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventBot(
std::string package_name,
QuestEventID evt,
Bot *bot,
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 = 1;
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 = 2;
}
lua_createtable(L, 0, 0);
//push self
Lua_Bot l_bot(bot);
luabind::adl::object l_bot_o = luabind::adl::object(L, l_bot);
l_bot_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = BotArgumentDispatch[evt];
arg_function(this, L, bot, init, data, extra_data, extra_pointers);
auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(bot, c);
if(lua_pcall(L, 1, 1, 0)) {
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) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//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::DispatchEventBot(
QuestEventID evt,
Bot *bot,
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 = "bot";
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 = _EventBot(package_name, evt, bot, init, data, extra_data, extra_pointers, &riter->lua_reference);
if (i != 0) {
ret = i;
}
}
++riter;
}
return ret;
}
bool LuaParser::BotHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "bot");
}
bool LuaParser::GlobalBotHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_bot");
}
void LuaParser::LoadBotScript(std::string filename) {
LoadScript(filename, "bot");
}
void LuaParser::LoadGlobalBotScript(std::string filename) {
LoadScript(filename, "global_bot");
}
#endif
#endif #endif

View File

@ -36,20 +36,80 @@ class LuaParser : public QuestInterface {
public: public:
~LuaParser(); ~LuaParser();
virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, virtual int EventNPC(
std::vector<std::any> *extra_pointers); QuestEventID evt,
virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, NPC* npc,
std::vector<std::any> *extra_pointers); Mob *init,
virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::string data,
std::vector<std::any> *extra_pointers); uint32 extra_data,
virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers
std::vector<std::any> *extra_pointers); );
virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, virtual int EventGlobalNPC(
std::vector<std::any> *extra_pointers); QuestEventID evt,
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, NPC* npc,
std::vector<std::any> *extra_pointers); Mob *init,
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::string data,
std::vector<std::any> *extra_pointers); uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventPlayer(
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventGlobalPlayer(
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventItem(
QuestEventID evt,
Client *client,
EQ::ItemInstance *item,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventSpell(
QuestEventID evt,
Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventEncounter(
QuestEventID evt,
std::string encounter_name,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#ifdef BOTS
virtual int EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt);
virtual bool HasGlobalQuestSub(QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt);
@ -60,6 +120,11 @@ public:
virtual bool EncounterHasQuestSub(std::string encounter_name, QuestEventID evt); virtual bool EncounterHasQuestSub(std::string encounter_name, QuestEventID evt);
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt); virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt);
#ifdef BOTS
virtual bool BotHasQuestSub(QuestEventID evt);
virtual bool GlobalBotHasQuestSub(QuestEventID evt);
#endif
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
virtual void LoadPlayerScript(std::string filename); virtual void LoadPlayerScript(std::string filename);
@ -68,6 +133,11 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadSpellScript(std::string filename, uint32 spell_id);
virtual void LoadEncounterScript(std::string filename, std::string encounter_name); virtual void LoadEncounterScript(std::string filename, std::string encounter_name);
#ifdef BOTS
virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename);
#endif
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
virtual void Init(); virtual void Init();
@ -75,14 +145,50 @@ public:
virtual void RemoveEncounter(const std::string &name); virtual void RemoveEncounter(const std::string &name);
virtual uint32 GetIdentifier() { return 0xb0712acc; } virtual uint32 GetIdentifier() { return 0xb0712acc; }
virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, virtual int DispatchEventNPC(
std::vector<std::any> *extra_pointers); QuestEventID evt,
virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, NPC* npc,
std::vector<std::any> *extra_pointers); Mob *init,
virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::string data,
std::vector<std::any> *extra_pointers); uint32 extra_data,
virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers
std::vector<std::any> *extra_pointers); );
virtual int DispatchEventPlayer(
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int DispatchEventItem(
QuestEventID evt,
Client *client,
EQ::ItemInstance *item,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
virtual int DispatchEventSpell(
QuestEventID evt,
Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#ifdef BOTS
virtual int DispatchEventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
static LuaParser* Instance() { static LuaParser* Instance() {
static LuaParser inst; static LuaParser inst;
@ -107,16 +213,68 @@ private:
LuaParser(const LuaParser&); LuaParser(const LuaParser&);
LuaParser& operator=(const LuaParser&); LuaParser& operator=(const LuaParser&);
int _EventNPC(std::string package_name, QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, int _EventNPC(
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func = nullptr); std::string package_name,
int _EventPlayer(std::string package_name, QuestEventID evt, Client *client, std::string data, uint32 extra_data, QuestEventID evt,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func = nullptr); NPC* npc,
int _EventItem(std::string package_name, QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, Mob *init,
uint32 extra_data, std::vector<std::any> *extra_pointers, luabind::adl::object *l_func = nullptr); std::string data,
int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func = nullptr); std::vector<std::any> *extra_pointers,
int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, luabind::adl::object *l_func = nullptr
std::vector<std::any> *extra_pointers); );
int _EventPlayer(
std::string package_name,
QuestEventID evt,
Client *client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr
);
int _EventItem(
std::string package_name,
QuestEventID evt,
Client *client,
EQ::ItemInstance *item,
Mob *mob,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr
);
int _EventSpell(
std::string package_name,
QuestEventID evt,
Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr
);
int _EventEncounter(
std::string package_name,
QuestEventID evt,
std::string encounter_name,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#ifdef BOTS
int _EventBot(
std::string package_name,
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr
);
#endif
void LoadScript(std::string filename, std::string package_name); void LoadScript(std::string filename, std::string package_name);
void MapFunctions(lua_State *L); void MapFunctions(lua_State *L);
@ -133,6 +291,10 @@ private:
SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; SpellArgumentHandler SpellArgumentDispatch[_LargestEventID];
EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID]; EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID];
#ifdef BOTS
BotArgumentHandler BotArgumentDispatch[_LargestEventID];
#endif
}; };
#endif #endif

View File

@ -668,6 +668,26 @@ void handle_player_inspect(QuestInterface* parse, lua_State* L, Client* client,
lua_setfield(L, -2, "other"); lua_setfield(L, -2, "other");
} }
void handle_player_aa_buy(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<std::any>* extra_pointers) {
Seperator sep(data.c_str());
lua_pushinteger(L, std::stoi(sep.arg[0]));
lua_setfield(L, -2, "aa_cost");
lua_pushinteger(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "aa_id");
lua_pushinteger(L, std::stoi(sep.arg[2]));
lua_setfield(L, -2, "aa_previous_id");
lua_pushinteger(L, std::stoi(sep.arg[3]));
lua_setfield(L, -2, "aa_next_id");
}
void handle_player_aa_gain(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<std::any>* extra_pointers) {
lua_pushinteger(L, std::stoi(data));
lua_setfield(L, -2, "aa_gained");
}
//Item //Item
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { std::vector<std::any> *extra_pointers) {
@ -764,11 +784,11 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I
} }
//Spell //Spell
void handle_spell_event(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) { void handle_spell_event(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) {
if(npc) { if (mob) {
Lua_Mob l_npc(npc); Lua_Mob l_mob(mob);
luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_npc_o.push(L); l_mob_o.push(L);
} else if(client) { } else if(client) {
Lua_Mob l_client(client); Lua_Mob l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client); luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
@ -804,11 +824,11 @@ void handle_spell_event(QuestInterface *parse, lua_State* L, NPC* npc, Client* c
lua_setfield(L, -2, "spell"); lua_setfield(L, -2, "spell");
} }
void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) { void handle_translocate_finish(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) {
if(npc) { if (mob) {
Lua_Mob l_npc(npc); Lua_Mob l_mob(mob);
luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_npc_o.push(L); l_mob_o.push(L);
} else if(client) { } else if(client) {
Lua_Mob l_client(client); Lua_Mob l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client); luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
@ -840,7 +860,7 @@ void handle_player_equip_item(QuestInterface *parse, lua_State* L, Client* clien
lua_setfield(L, -2, "item"); lua_setfield(L, -2, "item");
} }
void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) { } void handle_spell_null(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) { }
void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { std::vector<std::any> *extra_pointers) {
@ -940,24 +960,216 @@ void handle_player_merchant(QuestInterface* parse, lua_State* L, Client* client,
lua_setfield(L, -2, "item_cost"); lua_setfield(L, -2, "item_cost");
} }
void handle_player_aa_buy(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<std::any>* extra_pointers) { #ifdef BOTS
Seperator sep(data.c_str()); void handle_bot_null(
lua_pushinteger(L, std::stoi(sep.arg[0])); QuestInterface *parse,
lua_setfield(L, -2, "aa_cost"); lua_State* L,
Bot* bot,
lua_pushinteger(L, std::stoi(sep.arg[1])); Mob* init,
lua_setfield(L, -2, "aa_id"); std::string data,
uint32 extra_data,
lua_pushinteger(L, std::stoi(sep.arg[2])); std::vector<std::any> *extra_pointers
lua_setfield(L, -2, "aa_previous_id"); ) {
lua_pushinteger(L, std::stoi(sep.arg[3]));
lua_setfield(L, -2, "aa_next_id");
} }
void handle_player_aa_gain(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<std::any>* extra_pointers) { void handle_bot_cast(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Seperator sep(data.c_str());
int spell_id = std::stoi(sep.arg[0]);
if (IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
} else {
Lua_Spell l_spell(nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
}
lua_setfield(L, -2, "spell");
lua_pushinteger(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "caster_id");
lua_pushinteger(L, std::stoi(sep.arg[2]));
lua_setfield(L, -2, "caster_level");
}
void handle_bot_combat(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Lua_Mob l_mob(init);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
lua_pushboolean(L, std::stoi(data) == 0 ? false : true);
lua_setfield(L, -2, "joined");
}
void handle_bot_death(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Seperator sep(data.c_str());
Mob *o = entity_list.GetMobID(std::stoi(sep.arg[0]));
Lua_Mob l_mob(o);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
lua_pushinteger(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "damage");
int spell_id = std::stoi(sep.arg[2]);
if (IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
} else {
Lua_Spell l_spell(nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
}
lua_pushinteger(L, std::stoi(sep.arg[3]));
lua_setfield(L, -2, "skill");
}
void handle_bot_popup_response(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Lua_Mob l_mob(init);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
lua_pushinteger(L, std::stoi(data)); lua_pushinteger(L, std::stoi(data));
lua_setfield(L, -2, "aa_gained"); lua_setfield(L, -2, "popup_id");
}
void handle_bot_say(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Lua_Client l_client(reinterpret_cast<Client*>(init));
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
l_client_o.push(L);
lua_setfield(L, -2, "other");
lua_pushstring(L, data.c_str());
lua_setfield(L, -2, "message");
lua_pushinteger(L, extra_data);
lua_setfield(L, -2, "language");
}
void handle_bot_signal(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushinteger(L, std::stoi(data));
lua_setfield(L, -2, "signal");
}
void handle_bot_slay(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Lua_Mob l_mob(init);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
}
void handle_bot_target_change(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Lua_Mob l_mob(init);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
}
void handle_bot_timer(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushstring(L, data.c_str());
lua_setfield(L, -2, "timer");
}
void handle_bot_use_skill(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
Seperator sep(data.c_str());
lua_pushinteger(L, std::stoi(sep.arg[0]));
lua_setfield(L, -2, "skill_id");
lua_pushinteger(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "skill_level");
} }
#endif #endif
#endif

View File

@ -5,9 +5,13 @@
typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector<std::any>*); typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector<std::any>*);
typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector<std::any>*); typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector<std::any>*);
typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::ItemInstance*, Mob*, std::string, uint32, std::vector<std::any>*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::ItemInstance*, Mob*, std::string, uint32, std::vector<std::any>*);
typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, std::string, uint32, std::vector<std::any>*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*);
typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*);
#ifdef BOTS
typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*);
#endif
//NPC //NPC
void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
@ -153,11 +157,11 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
//Spell //Spell
void handle_spell_event(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, void handle_spell_event(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, void handle_translocate_finish(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, void handle_spell_null(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
@ -171,5 +175,118 @@ void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* enc
void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
#ifdef BOTS
void handle_bot_null(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_cast(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_combat(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_death(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_popup_response(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_say(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_signal(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_slay(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_target_change(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_timer(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_use_skill(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
#endif #endif
#endif #endif

View File

@ -4300,6 +4300,8 @@ void Mob::SetTarget(Mob *mob)
#ifdef BOTS #ifdef BOTS
CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above) CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above)
} else if (IsBot()) {
parse->EventBot(EVENT_TARGET_CHANGE, CastToBot(), mob, "", 0);
#endif #endif
} }

View File

@ -32,6 +32,10 @@
#include "fastmath.h" #include "fastmath.h"
#include "../common/data_verification.h" #include "../common/data_verification.h"
#ifdef BOTS
#include "bot.h"
#endif
#include <glm/gtx/projection.hpp> #include <glm/gtx/projection.hpp>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -1923,10 +1927,11 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
if (attacker->GetHP() > 0) { if (attacker->GetHP() > 0) {
if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) {
parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0);
uint32 emoteid = GetEmoteID(); auto emote_id = GetEmoteID();
if (emoteid != 0) { if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid); CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid);
} }
std::string mob_name = GetCleanName(); std::string mob_name = GetCleanName();
combat_record.Start(mob_name); combat_record.Start(mob_name);
CastToNPC()->SetCombatEvent(true); CastToNPC()->SetCombatEvent(true);
@ -1934,18 +1939,28 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
} }
} }
} }
#ifdef BOTS
if (IsBot()) {
parse->EventBot(EVENT_COMBAT, CastToBot(), attacker, "1", 0);
}
#endif
} }
// Note: Hate list may not be actually clear until after this function call completes // Note: Hate list may not be actually clear until after this function call completes
void Mob::AI_Event_NoLongerEngaged() { void Mob::AI_Event_NoLongerEngaged() {
if (!IsAIControlled()) if (!IsAIControlled()) {
return; return;
}
AI_walking_timer->Start(RandomTimer(3000,20000)); AI_walking_timer->Start(RandomTimer(3000,20000));
time_until_can_move = Timer::GetCurrentTime(); time_until_can_move = Timer::GetCurrentTime();
if (minLastFightingDelayMoving == maxLastFightingDelayMoving)
if (minLastFightingDelayMoving == maxLastFightingDelayMoving) {
time_until_can_move += minLastFightingDelayMoving; time_until_can_move += minLastFightingDelayMoving;
else } else {
time_until_can_move += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving); time_until_can_move += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
}
StopNavigation(); StopNavigation();
ClearRampage(); ClearRampage();
@ -1954,16 +1969,21 @@ void Mob::AI_Event_NoLongerEngaged() {
SetPrimaryAggro(false); SetPrimaryAggro(false);
SetAssistAggro(false); SetAssistAggro(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(this->GetID())) { if (entity_list.GetNPCByID(GetID())) {
uint32 emoteid = CastToNPC()->GetEmoteID(); auto emote_id = CastToNPC()->GetEmoteID();
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
if (emoteid != 0) { if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emoteid); CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emoteid);
} }
combat_record.Stop(); combat_record.Stop();
CastToNPC()->SetCombatEvent(false); CastToNPC()->SetCombatEvent(false);
} }
} }
#ifdef BOTS
} else if (IsBot()) {
parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0);
#endif
} }
} }

View File

@ -81,6 +81,16 @@ uint32 Perl_Bot_GetBotItemIDBySlot(Bot* self, uint16 slot_id)
return self->GetBotItemBySlot(slot_id); return self->GetBotItemBySlot(slot_id);
} }
void Perl_Bot_SignalBot(Bot* self, int signal_id)
{
self->SignalBot(signal_id);
}
void Perl_Bot_OwnerMessage(Bot* self, std::string message)
{
self->OwnerMessage(message);
}
int Perl_Bot_GetExpansionBitmask(Bot* self) int Perl_Bot_GetExpansionBitmask(Bot* self)
{ {
return self->GetExpansionBitmask(); return self->GetExpansionBitmask();
@ -117,9 +127,11 @@ void perl_register_bot()
package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask); package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask);
package.add("GetOwner", &Perl_Bot_GetOwner); package.add("GetOwner", &Perl_Bot_GetOwner);
package.add("HasBotItem", &Perl_Bot_HasBotItem); package.add("HasBotItem", &Perl_Bot_HasBotItem);
package.add("OwnerMessage", &Perl_Bot_OwnerMessage);
package.add("RemoveBotItem", &Perl_Bot_RemoveBotItem); package.add("RemoveBotItem", &Perl_Bot_RemoveBotItem);
package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask); package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask);
package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask); package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask);
package.add("SignalBot", &Perl_Bot_SignalBot);
} }
#endif //EMBPERL_XS_CLASSES #endif //EMBPERL_XS_CLASSES

View File

@ -449,6 +449,21 @@ perl::array Perl_EntityList_GetBotListByClientName(EntityList* self, std::string
} }
return result; return result;
} }
void Perl_EntityList_SignalAllBotsByOwnerCharacterID(EntityList* self, uint32_t character_id, int signal_id) // @categories Script Utility
{
entity_list.SignalAllBotsByOwnerCharacterID(character_id, signal_id);
}
void Perl_EntityList_SignalBotByBotID(EntityList* self, uint32_t bot_id, int signal_id) // @categories Script Utility
{
entity_list.SignalBotByBotID(bot_id, signal_id);
}
void Perl_EntityList_SignalBotByBotName(EntityList* self, std::string bot_name, int signal_id) // @categories Script Utility
{
entity_list.SignalBotByBotName(bot_name, signal_id);
}
#endif #endif
perl::array Perl_EntityList_GetNPCList(EntityList* self) // @categories Script Utility perl::array Perl_EntityList_GetNPCList(EntityList* self) // @categories Script Utility
@ -675,7 +690,14 @@ void perl_register_entitylist()
package.add("RemoveObject", &Perl_EntityList_RemoveObject); package.add("RemoveObject", &Perl_EntityList_RemoveObject);
package.add("RemoveTrap", &Perl_EntityList_RemoveTrap); package.add("RemoveTrap", &Perl_EntityList_RemoveTrap);
package.add("ReplaceWithTarget", &Perl_EntityList_ReplaceWithTarget); package.add("ReplaceWithTarget", &Perl_EntityList_ReplaceWithTarget);
#ifdef BOTS
package.add("SignalAllBotsByOwnerCharacterID", &Perl_EntityList_SignalAllBotsByOwnerCharacterID);
#endif
package.add("SignalAllClients", &Perl_EntityList_SignalAllClients); package.add("SignalAllClients", &Perl_EntityList_SignalAllClients);
#ifdef BOTS
package.add("SignalBotByBotID", &Perl_EntityList_SignalBotByBotID);
package.add("SignalBotByBotName", &Perl_EntityList_SignalBotByBotName);
#endif
package.add("SignalMobsByNPCID", &Perl_EntityList_SignalMobsByNPCID); package.add("SignalMobsByNPCID", &Perl_EntityList_SignalMobsByNPCID);
} }

View File

@ -1492,9 +1492,9 @@ perl::array Perl_Mob_GetHateList(Mob* self)
return result; return result;
} }
void Perl_Mob_SignalClient(Mob* self, Client* client, uint32 data) // @categories Script Utility void Perl_Mob_SignalClient(Mob* self, Client* client, int signal_id) // @categories Script Utility
{ {
client->Signal(data); client->Signal(signal_id);
} }
bool Perl_Mob_CombatRange(Mob* self, Mob* target) // @categories Script Utility bool Perl_Mob_CombatRange(Mob* self, Mob* target) // @categories Script Utility

View File

@ -43,10 +43,34 @@ public:
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, virtual int EventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
#ifdef BOTS
virtual int EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointer
) {
return 0;
}
virtual int EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
return 0;
}
#endif
virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; } virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; }
virtual bool HasGlobalQuestSub(QuestEventID evt) { return false; } virtual bool HasGlobalQuestSub(QuestEventID evt) { return false; }
@ -57,6 +81,11 @@ public:
virtual bool EncounterHasQuestSub(std::string encounter_name, QuestEventID evt) { return false; } virtual bool EncounterHasQuestSub(std::string encounter_name, QuestEventID evt) { return false; }
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt) { return false; } virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt) { return false; }
#ifdef BOTS
virtual bool BotHasQuestSub(QuestEventID evt) { return false; }
virtual bool GlobalBotHasQuestSub(QuestEventID evt) { return false; }
#endif
virtual void LoadNPCScript(std::string filename, int npc_id) { } virtual void LoadNPCScript(std::string filename, int npc_id) { }
virtual void LoadGlobalNPCScript(std::string filename) { } virtual void LoadGlobalNPCScript(std::string filename) { }
virtual void LoadPlayerScript(std::string filename) { } virtual void LoadPlayerScript(std::string filename) { }
@ -65,14 +94,32 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id) { } virtual void LoadSpellScript(std::string filename, uint32 spell_id) { }
virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { } virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { }
#ifdef BOTS
virtual void LoadBotScript(std::string filename) { }
virtual void LoadGlobalBotScript(std::string filename) { }
#endif
virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, virtual int DispatchEventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) { return 0; } std::vector<std::any> *extra_pointers) { return 0; }
#ifdef BOTS
virtual int DispatchEventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
return 0;
}
#endif
virtual void AddVar(std::string name, std::string val) { } virtual void AddVar(std::string name, std::string val) { }
virtual std::string GetVar(std::string name) { return std::string(); } virtual std::string GetVar(std::string name) { return std::string(); }

View File

@ -36,6 +36,11 @@ QuestParserCollection::QuestParserCollection() {
_player_quest_status = QuestUnloaded; _player_quest_status = QuestUnloaded;
_global_player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded;
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
#ifdef BOTS
_bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded;
#endif
} }
QuestParserCollection::~QuestParserCollection() { QuestParserCollection::~QuestParserCollection() {
@ -70,7 +75,7 @@ void QuestParserCollection::Init() {
} }
void QuestParserCollection::ReloadQuests(bool reset_timers) { void QuestParserCollection::ReloadQuests(bool reset_timers) {
if(reset_timers) { if (reset_timers) {
quest_manager.ClearAllTimers(); quest_manager.ClearAllTimers();
} }
@ -79,11 +84,17 @@ void QuestParserCollection::ReloadQuests(bool reset_timers) {
_player_quest_status = QuestUnloaded; _player_quest_status = QuestUnloaded;
_global_player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded;
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
#ifdef BOTS
_bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded;
#endif
_spell_quest_status.clear(); _spell_quest_status.clear();
_item_quest_status.clear(); _item_quest_status.clear();
_encounter_quest_status.clear(); _encounter_quest_status.clear();
auto iter = _load_precedence.begin(); auto iter = _load_precedence.begin();
while(iter != _load_precedence.end()) { while (iter != _load_precedence.end()) {
(*iter)->ReloadQuests(); (*iter)->ReloadQuests();
++iter; ++iter;
} }
@ -452,38 +463,46 @@ int QuestParserCollection::EventItem(QuestEventID evt, Client *client, EQ::ItemI
return 0; return 0;
} }
int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int QuestParserCollection::EventSpell(
std::vector<std::any> *extra_pointers) { QuestEventID evt,
Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
auto iter = _spell_quest_status.find(spell_id); auto iter = _spell_quest_status.find(spell_id);
if(iter != _spell_quest_status.end()) { if (iter != _spell_quest_status.end()) {
//loaded or failed to load //loaded or failed to load
if(iter->second != QuestFailedToLoad) { if (iter->second != QuestFailedToLoad) {
auto qiter = _interfaces.find(iter->second); auto qi = _interfaces.find(iter->second);
int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); int ret = DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
int i = qiter->second->EventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); int i = qi->second->EventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
if(i != 0) { if (i != 0) {
ret = i; ret = i;
} }
return ret; return ret;
} }
return DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers);
} return DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { } else if (_spell_quest_status[spell_id] != QuestFailedToLoad) {
std::string filename; std::string filename;
QuestInterface *qi = GetQIBySpellQuest(spell_id, filename); QuestInterface *qi = GetQIBySpellQuest(spell_id, filename);
if (qi) { if (qi) {
_spell_quest_status[spell_id] = qi->GetIdentifier(); _spell_quest_status[spell_id] = qi->GetIdentifier();
qi->LoadSpellScript(filename, spell_id); qi->LoadSpellScript(filename, spell_id);
int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); int ret = DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
int i = qi->EventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); int i = qi->EventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
if (i != 0) { if (i != 0) {
ret = i; ret = i;
} }
return ret; return ret;
} } else {
else {
_spell_quest_status[spell_id] = QuestFailedToLoad; _spell_quest_status[spell_id] = QuestFailedToLoad;
return DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); return DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
} }
} }
return 0; return 0;
@ -1022,18 +1041,25 @@ int QuestParserCollection::DispatchEventItem(QuestEventID evt, Client *client, E
return ret; return ret;
} }
int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int QuestParserCollection::DispatchEventSpell(
std::vector<std::any> *extra_pointers) { QuestEventID evt,
int ret = 0; Mob* mob,
Client *client,
uint32 spell_id,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
int ret = 0;
auto iter = _load_precedence.begin(); auto iter = _load_precedence.begin();
while(iter != _load_precedence.end()) { while(iter != _load_precedence.end()) {
int i = (*iter)->DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); int i = (*iter)->DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers);
if(i != 0) { if(i != 0) {
ret = i; ret = i;
} }
++iter; ++iter;
} }
return ret; return ret;
} }
void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* perl_event_export_settings) { void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* perl_event_export_settings) {
@ -1074,3 +1100,228 @@ void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings*
} }
} }
#ifdef BOTS
int QuestParserCollection::DispatchEventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
int ret = 0;
auto iter = _load_precedence.begin();
while (iter != _load_precedence.end()) {
int i = (*iter)->DispatchEventBot(evt, bot, init, data, extra_data, extra_pointers);
if (i != 0) {
ret = i;
}
++iter;
}
return ret;
}
int QuestParserCollection::EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
auto rd = DispatchEventBot(evt, bot, init, data, extra_data, extra_pointers);
auto rl = EventBotLocal(evt, bot, init, data, extra_data, extra_pointers);
auto rg = EventBotGlobal(evt, bot, init, data, extra_data, extra_pointers);
//Local quests returning non-default values have priority over global quests
if (rl != 0) {
return rl;
} else if (rg != 0) {
return rg;
} else if (rd != 0) {
return rd;
}
return 0;
}
int QuestParserCollection::EventBotLocal(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
if (_bot_quest_status == QuestUnloaded) {
std::string filename;
QuestInterface *qi = GetQIByBotQuest(filename);
if (qi) {
_bot_quest_status = qi->GetIdentifier();
qi->LoadBotScript(filename);
return qi->EventBot(evt, bot, init, data, extra_data, extra_pointers);
}
} else {
if (_bot_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_bot_quest_status);
return iter->second->EventBot(evt, bot, init, data, extra_data, extra_pointers);
}
}
return 0;
}
int QuestParserCollection::EventBotGlobal(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
if (_global_bot_quest_status == QuestUnloaded) {
std::string filename;
QuestInterface *qi = GetQIByGlobalBotQuest(filename);
if (qi) {
_global_bot_quest_status = qi->GetIdentifier();
qi->LoadGlobalBotScript(filename);
return qi->EventGlobalBot(evt, bot, init, data, extra_data, extra_pointers);
}
} else {
if (_global_bot_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_bot_quest_status);
return iter->second->EventGlobalBot(evt, bot, init, data, extra_data, extra_pointers);
}
}
return 0;
}
bool QuestParserCollection::BotHasQuestSubLocal(QuestEventID evt) {
if (_bot_quest_status == QuestUnloaded) {
std::string filename;
QuestInterface *qi = GetQIByBotQuest(filename);
if (qi) {
_bot_quest_status = qi->GetIdentifier();
qi->LoadBotScript(filename);
return qi->BotHasQuestSub(evt);
}
} else if (_bot_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_bot_quest_status);
return iter->second->BotHasQuestSub(evt);
}
return false;
}
bool QuestParserCollection::BotHasQuestSubGlobal(QuestEventID evt) {
if (_global_bot_quest_status == QuestUnloaded) {
std::string filename;
QuestInterface *qi = GetQIByGlobalBotQuest(filename);
if (qi) {
_global_bot_quest_status = qi->GetIdentifier();
qi->LoadGlobalBotScript(filename);
return qi->GlobalBotHasQuestSub(evt);
}
} else if (_global_bot_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_bot_quest_status);
return iter->second->GlobalBotHasQuestSub(evt);
}
return false;
}
QuestInterface *QuestParserCollection::GetQIByBotQuest(std::string &filename) {
if (!zone || !zone->IsLoaded()) {
return nullptr;
}
// first look for /quests/zone/bot_v[instance_version].ext (precedence)
filename = fmt::format("{}/{}/bot_v{}", path.GetQuestsPath(), zone->GetShortName(), zone->GetInstanceVersion());
std::string tmp;
FILE *f = nullptr;
auto iter = _load_precedence.begin();
while (iter != _load_precedence.end()) {
auto ext = _extensions.find((*iter)->GetIdentifier());
tmp = fmt::format("{}.{}", filename, ext->second);
f = fopen(tmp.c_str(), "r");
if (f) {
fclose(f);
filename = tmp;
return (*iter);
}
++iter;
}
// second look for /quests/zone/bot.ext (precedence)
filename = fmt::format("{}/{}/bot", path.GetQuestsPath(), zone->GetShortName());
iter = _load_precedence.begin();
while(iter != _load_precedence.end()) {
auto ext = _extensions.find((*iter)->GetIdentifier());
tmp = fmt::format("{}.{}", filename, ext->second);
f = fopen(tmp.c_str(), "r");
if (f) {
fclose(f);
filename = tmp;
return (*iter);
}
++iter;
}
// third look for /quests/global/bot.ext (precedence)
filename = fmt::format("{}/{}/bot", path.GetQuestsPath(), QUEST_GLOBAL_DIRECTORY);
iter = _load_precedence.begin();
while (iter != _load_precedence.end()) {
auto ext = _extensions.find((*iter)->GetIdentifier());
tmp = fmt::format("{}.{}", filename, ext->second);
f = fopen(tmp.c_str(), "r");
if (f) {
fclose(f);
filename = tmp;
return (*iter);
}
++iter;
}
return nullptr;
}
QuestInterface *QuestParserCollection::GetQIByGlobalBotQuest(std::string &filename) {
// first look for /quests/global/global_bot.ext (precedence)
filename = fmt::format("{}/{}/global_bot", path.GetQuestsPath(), QUEST_GLOBAL_DIRECTORY);
std::string tmp;
FILE *f = nullptr;
auto iter = _load_precedence.begin();
while (iter != _load_precedence.end()) {
auto ext = _extensions.find((*iter)->GetIdentifier());
tmp = fmt::format("{}.{}", filename, ext->second);
f = fopen(tmp.c_str(), "r");
if (f) {
fclose(f);
filename = tmp;
return (*iter);
}
++iter;
}
return nullptr;
}
#endif

View File

@ -72,16 +72,31 @@ public:
bool SpellHasQuestSub(uint32 spell_id, QuestEventID evt, bool check_encounters = false); bool SpellHasQuestSub(uint32 spell_id, QuestEventID evt, bool check_encounters = false);
bool ItemHasQuestSub(EQ::ItemInstance *itm, QuestEventID evt, bool check_encounters = false); bool ItemHasQuestSub(EQ::ItemInstance *itm, QuestEventID evt, bool check_encounters = false);
#ifdef BOTS
bool BotHasQuestSub(QuestEventID evt);
#endif
int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr); std::vector<std::any> *extra_pointers = nullptr);
int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr); std::vector<std::any> *extra_pointers = nullptr);
int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr); std::vector<std::any> *extra_pointers = nullptr);
int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int EventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr); std::vector<std::any> *extra_pointers = nullptr);
int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr); std::vector<std::any> *extra_pointers = nullptr);
#ifdef BOTS
int EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr
);
#endif
void GetErrors(std::list<std::string> &quest_errors); void GetErrors(std::list<std::string> &quest_errors);
@ -117,11 +132,35 @@ private:
bool ItemHasEncounterSub(EQ::ItemInstance* item, QuestEventID evt); bool ItemHasEncounterSub(EQ::ItemInstance* item, QuestEventID evt);
bool HasEncounterSub(QuestEventID evt, const std::string& package_name); bool HasEncounterSub(QuestEventID evt, const std::string& package_name);
#ifdef BOTS
bool BotHasQuestSubLocal(QuestEventID evt);
bool BotHasQuestSubGlobal(QuestEventID evt);
#endif
int EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers); int EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers);
int EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers); int EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers);
int EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers); int EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers);
int EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers); int EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers);
#ifdef BOTS
int EventBotLocal(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
int EventBotGlobal(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
QuestInterface *GetQIByNPCQuest(uint32 npcid, std::string &filename); QuestInterface *GetQIByNPCQuest(uint32 npcid, std::string &filename);
QuestInterface *GetQIByGlobalNPCQuest(std::string &filename); QuestInterface *GetQIByGlobalNPCQuest(std::string &filename);
QuestInterface *GetQIByPlayerQuest(std::string &filename); QuestInterface *GetQIByPlayerQuest(std::string &filename);
@ -129,6 +168,11 @@ private:
QuestInterface *GetQIBySpellQuest(uint32 spell_id, std::string &filename); QuestInterface *GetQIBySpellQuest(uint32 spell_id, std::string &filename);
QuestInterface *GetQIByItemQuest(std::string item_script, std::string &filename); QuestInterface *GetQIByItemQuest(std::string item_script, std::string &filename);
QuestInterface *GetQIByEncounterQuest(std::string encounter_name, std::string &filename); QuestInterface *GetQIByEncounterQuest(std::string encounter_name, std::string &filename);
#ifdef BOTS
QuestInterface *GetQIByBotQuest(std::string &filename);
QuestInterface *GetQIByGlobalBotQuest(std::string &filename);
#endif
int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
@ -136,9 +180,20 @@ private:
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, int DispatchEventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers); std::vector<std::any> *extra_pointers);
#ifdef BOTS
int DispatchEventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif
std::map<uint32, QuestInterface*> _interfaces; std::map<uint32, QuestInterface*> _interfaces;
std::map<uint32, std::string> _extensions; std::map<uint32, std::string> _extensions;
std::list<QuestInterface*> _load_precedence; std::list<QuestInterface*> _load_precedence;
@ -149,6 +204,13 @@ private:
uint32 _global_npc_quest_status; uint32 _global_npc_quest_status;
uint32 _player_quest_status; uint32 _player_quest_status;
uint32 _global_player_quest_status; uint32 _global_player_quest_status;
#ifdef BOTS
uint32 _bot_quest_status;
uint32 _global_bot_quest_status;
#endif
std::map<uint32, uint32> _spell_quest_status; std::map<uint32, uint32> _spell_quest_status;
std::map<uint32, uint32> _item_quest_status; std::map<uint32, uint32> _item_quest_status;
std::map<std::string, uint32> _encounter_quest_status; std::map<std::string, uint32> _encounter_quest_status;

View File

@ -94,10 +94,15 @@ void QuestManager::Process() {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
} else if (cur->mob->IsEncounter()) { } else if (cur->mob->IsEncounter()) {
parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr); parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr);
} else { } else if (cur->mob->IsClient()) {
//this is inheriently unsafe if we ever make it so more than npc/client start timers //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); parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
} }
#ifdef BOTS
else if (cur->mob->IsBot()) {
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
}
#endif
//we MUST reset our iterator since the quest could have removed/added any //we MUST reset our iterator since the quest could have removed/added any
//number of timers... worst case we have to check a bunch of timers twice //number of timers... worst case we have to check a bunch of timers twice
@ -3395,6 +3400,17 @@ NPC *QuestManager::GetNPC() const {
return nullptr; return nullptr;
} }
#ifdef BOTS
Bot *QuestManager::GetBot() const {
if (!quests_running_.empty()) {
running_quest e = quests_running_.top();
return (e.owner && e.owner->IsBot()) ? e.owner->CastToBot() : nullptr;
}
return nullptr;
}
#endif
Mob *QuestManager::GetOwner() const { Mob *QuestManager::GetOwner() const {
if(!quests_running_.empty()) { if(!quests_running_.empty()) {
running_quest e = quests_running_.top(); running_quest e = quests_running_.top();
@ -3653,7 +3669,7 @@ void QuestManager::CrossZoneSetEntityVariable(uint8 update_type, int update_iden
safe_delete(pack); safe_delete(pack);
} }
void QuestManager::CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name) { void QuestManager::CrossZoneSignal(uint8 update_type, int update_identifier, int signal, const char* client_name) {
auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct)); auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct));
CZSignal_Struct* CZS = (CZSignal_Struct*)pack->pBuffer; CZSignal_Struct* CZS = (CZSignal_Struct*)pack->pBuffer;
CZS->update_type = update_type; CZS->update_type = update_type;
@ -3763,7 +3779,7 @@ void QuestManager::WorldWideSetEntityVariable(uint8 update_type, const char* var
safe_delete(pack); safe_delete(pack);
} }
void QuestManager::WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status, uint8 max_status) { void QuestManager::WorldWideSignal(uint8 update_type, int signal, uint8 min_status, uint8 max_status) {
auto pack = new ServerPacket(ServerOP_WWSignal, sizeof(WWSignal_Struct)); auto pack = new ServerPacket(ServerOP_WWSignal, sizeof(WWSignal_Struct));
WWSignal_Struct* WWS = (WWSignal_Struct*)pack->pBuffer; WWSignal_Struct* WWS = (WWSignal_Struct*)pack->pBuffer;
WWS->update_type = update_type; WWS->update_type = update_type;

View File

@ -311,7 +311,7 @@ public:
void CrossZoneMessage(uint8 update_type, int update_identifier, uint32 type, const char* message, const char* client_name = ""); void CrossZoneMessage(uint8 update_type, int update_identifier, uint32 type, const char* message, const char* client_name = "");
void CrossZoneMove(uint8 update_type, uint8 update_subtype, int update_identifier, const char* zone_short_name, uint16 instance_id, const char* client_name = ""); void CrossZoneMove(uint8 update_type, uint8 update_subtype, int update_identifier, const char* zone_short_name, uint16 instance_id, const char* client_name = "");
void CrossZoneSetEntityVariable(uint8 update_type, int update_identifier, const char* variable_name, const char* variable_value, const char* client_name = ""); void CrossZoneSetEntityVariable(uint8 update_type, int update_identifier, const char* variable_name, const char* variable_value, const char* client_name = "");
void CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name = ""); void CrossZoneSignal(uint8 update_type, int update_identifier, int signal, const char* client_name = "");
void CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name = ""); void CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name = "");
void CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, const char* client_name = ""); void CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, const char* client_name = "");
void WorldWideDialogueWindow(const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideDialogueWindow(const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
@ -320,7 +320,7 @@ public:
void WorldWideMessage(uint32 type, const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideMessage(uint32 type, const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
void WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
void WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideSignal(uint8 update_type, int signal, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
void WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
void WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); void WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player);
bool EnableRecipe(uint32 recipe_id); bool EnableRecipe(uint32 recipe_id);
@ -345,6 +345,10 @@ public:
std::string GetRecipeName(uint32 recipe_id); std::string GetRecipeName(uint32 recipe_id);
bool HasRecipeLearned(uint32 recipe_id); bool HasRecipeLearned(uint32 recipe_id);
#ifdef BOTS
Bot *GetBot() const;
#endif
Client *GetInitiator() const; Client *GetInitiator() const;
NPC *GetNPC() const; NPC *GetNPC() const;
Mob *GetOwner() const; Mob *GetOwner() const;

View File

@ -175,11 +175,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
return true; return true;
} }
} else if (IsNPC()) { } else if (IsNPC()) {
if (parse->EventSpell(EVENT_SPELL_EFFECT_NPC, CastToNPC(), nullptr, spell_id, export_string, 0) != 0) { if (parse->EventSpell(EVENT_SPELL_EFFECT_NPC, this, nullptr, spell_id, export_string, 0) != 0) {
CalcBonuses(); CalcBonuses();
return true; return true;
} }
} #ifdef BOTS
} else if (IsBot()) {
if (parse->EventSpell(EVENT_SPELL_EFFECT_BOT, this, nullptr, spell_id, export_string, 0) != 0) {
CalcBonuses();
return true;
}
#endif
}
if(IsVirusSpell(spell_id)) { if(IsVirusSpell(spell_id)) {
@ -3782,9 +3789,15 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
return; return;
} }
} else if (IsNPC()) { } else if (IsNPC()) {
if (parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_NPC, CastToNPC(), nullptr, buff.spellid, export_string, 0) != 0) { if (parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_NPC, this, nullptr, buff.spellid, export_string, 0) != 0) {
return; return;
} }
#ifdef BOTS
} else if (IsBot()) {
if (parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_BOT, this, nullptr, buff.spellid, export_string, 0) != 0) {
return;
}
#endif
} }
for (int i = 0; i < EFFECT_COUNT; i++) { for (int i = 0; i < EFFECT_COUNT; i++) {
@ -4129,9 +4142,15 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
return; return;
} }
} else if (IsNPC()) { } else if (IsNPC()) {
if (parse->EventSpell(EVENT_SPELL_FADE, CastToNPC(), nullptr, buffs[slot].spellid, export_string, 0) != 0) { if (parse->EventSpell(EVENT_SPELL_FADE, this, nullptr, buffs[slot].spellid, export_string, 0) != 0) {
return; return;
} }
#ifdef BOTS
} else if (IsBot()) {
if (parse->EventSpell(EVENT_SPELL_FADE, this, nullptr, buffs[slot].spellid, export_string, 0) != 0) {
return;
}
#endif
} }
for (int i=0; i < EFFECT_COUNT; i++) for (int i=0; i < EFFECT_COUNT; i++)

View File

@ -245,17 +245,21 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
GetID(), GetID(),
GetCasterLevel(spell_id) GetCasterLevel(spell_id)
); );
if(IsClient()) { if (IsClient()) {
if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0) != 0) { if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0) != 0) {
if (IsDiscipline(spell_id)) { if (IsDiscipline(spell_id)) {
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0);
} else { } else {
CastToClient()->SendSpellBarEnable(spell_id); CastToClient()->SendSpellBarEnable(spell_id);
} }
return(false); return false;
} }
} else if(IsNPC()) { } else if (IsNPC()) {
parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0); parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0);
#ifdef BOTS
} else if (IsBot()) {
parse->EventBot(EVENT_CAST_BEGIN, CastToBot(), nullptr, export_string, 0);
#endif
} }
//To prevent NPC ghosting when spells are cast from scripts //To prevent NPC ghosting when spells are cast from scripts
@ -1666,10 +1670,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
GetID(), GetID(),
GetCasterLevel(spell_id) GetCasterLevel(spell_id)
); );
if(IsClient()) { if (IsClient()) {
parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0); parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0);
} else if(IsNPC()) { } else if (IsNPC()) {
parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0); parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0);
#ifdef BOTS
} else if (IsBot()) {
parse->EventBot(EVENT_CAST, CastToBot(), nullptr, export_string, 0);
#endif
} }
if(bard_song_mode) if(bard_song_mode)
@ -3628,8 +3636,13 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0); parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0);
} else if (spelltar->IsClient()) { } else if (spelltar->IsClient()) {
parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0); parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0);
#ifdef BOTS
} else if (spelltar->IsBot()) {
parse->EventBot(EVENT_CAST_ON, spelltar->CastToBot(), this, export_string, 0);
#endif
} }
mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc);
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {

View File

@ -2614,7 +2614,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer; CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer;
uint8 update_type = CZS->update_type; uint8 update_type = CZS->update_type;
int update_identifier = CZS->update_identifier; int update_identifier = CZS->update_identifier;
uint32 signal = CZS->signal; int signal = CZS->signal;
const char* client_name = CZS->client_name; const char* client_name = CZS->client_name;
if (update_type == CZUpdateType_Character) { if (update_type == CZUpdateType_Character) {
auto client = entity_list.GetClientByCharID(update_identifier); auto client = entity_list.GetClientByCharID(update_identifier);