diff --git a/common/servertalk.h b/common/servertalk.h index 939f44678..7cda29d22 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -1528,7 +1528,7 @@ struct CZSetEntityVariable_Struct { struct CZSignal_Struct { 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 - uint32 signal; + int signal; char client_name[64]; // Only used by Character Name Type, else empty }; @@ -1601,7 +1601,7 @@ struct WWSetEntityVariable_Struct { struct WWSignal_Struct { uint8 update_type; // 0 - Character, 1 - NPC - uint32 signal; + int signal; uint8 min_status; uint8 max_status; }; diff --git a/zone/attack.cpp b/zone/attack.cpp index ebf79392b..6d2b40d67 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1454,6 +1454,17 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo LogCombat("Attack missed. Damage set to 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) { - if (!ClientFinishedLoading()) + if (!ClientFinishedLoading()) { return false; + } - if (dead) + if (dead) { return false; //cant die more than once... + } - if (!spell) + if (!spell) { spell = SPELL_UNKNOWN; + } - std::string export_string = fmt::format( + auto export_string = fmt::format( "{} {} {} {}", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill) ); + if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) { if (GetHP() < 0) { SetHP(0); @@ -1793,24 +1808,36 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill GetMerc()->Suspend(); } - if (killerMob != nullptr) - { + if (killerMob) { if (killerMob->IsNPC()) { parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); mod_client_death_npc(killerMob); - uint32 emoteid = killerMob->GetEmoteID(); - if (emoteid != 0) + auto emote_id = killerMob->GetEmoteID(); + if (emote_id) { killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid); + } + 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); 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... killerMob->CastToClient()->SetDueling(false); 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 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 }; - int Num = RuleI(Character, DeathExpLossMultiplier); - if ((Num < 0) || (Num > 10)) - Num = 3; - float loss = GetNum[Num]; - exploss = (int)((float)GetEXP() * (loss)); //loose % of total XP pending rule (choose 0-10) + 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 exp_loss = RuleI(Character, DeathExpLossMultiplier); + if (!EQ::ValueWithin(exp_loss, 0, 10)) { + exp_loss = 3; + } + + auto current_exp_loss = exp_losses[exp_loss]; + + exploss = static_cast(static_cast(GetEXP()) * current_exp_loss); //loose % of total XP pending rule (choose 0-10) } if (!RuleB(Character, UseDeathExpLossMult)) { - exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); + exploss = static_cast(GetLevel() * (GetLevel() / 18.0) * 12000); } 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; - } - else if (killerMob) - { - if (killerMob->IsClient()) - { + } else if (killerMob) { + if (killerMob->IsClient()) { exploss = 0; - } - else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient()) - { + } else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient()) { exploss = 0; +#ifdef BOTS + } else if (killerMob->IsBot()) { + exploss = 0; +#endif } } - if (spell != SPELL_UNKNOWN) - { + if (spell != SPELL_UNKNOWN) { uint32 buff_count = GetMaxTotalSlots(); - for (uint16 buffIt = 0; buffIt < buff_count; buffIt++) - { - if (buffs[buffIt].spellid == spell && buffs[buffIt].client) - { + for (uint16 buffIt = 0; buffIt < buff_count; buffIt++) { + if (buffs[buffIt].spellid == spell && buffs[buffIt].client) { exploss = 0; // no exp loss for pvp dot break; } } } - bool LeftCorpse = false; + bool leave_corpse = false; Corpse* new_corpse = nullptr; // now we apply the exp loss, unmem their spells, and make a corpse // unless they're a GM (or less than lvl 10 - if (!GetGM()) - { + if (!GetGM()) { if (exploss > 0) { int32 newexp = GetEXP(); if (exploss > newexp) { //lost more than we have... wtf.. newexp = 1; - } - else { + } else { newexp -= exploss; } SetEXP(newexp, GetAAXP()); //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 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 ((ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) + if ((ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) { UnmemSpellAll(true); - else + } else { 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 new_corpse = new Corpse(this, exploss); std::string 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); - int reward = atoi(tmp.c_str()); + auto reward = atoi(tmp.c_str()); if (reward == 3) { database.GetVariable("PvPitem", tmp); - int pvpitem = atoi(tmp.c_str()); - if (pvpitem>0 && pvpitem<200000) - new_corpse->SetPlayerKillItemID(pvpitem); - } - else if (reward == 2) + auto pvp_item_id = atoi(tmp.c_str()); + const auto* item = database.GetItem(pvp_item_id); + if (item) { + new_corpse->SetPlayerKillItemID(pvp_item_id); + } + } else if (reward == 2) { new_corpse->SetPlayerKillItemID(-1); - else if (reward == 1) + } else if (reward == 1) { new_corpse->SetPlayerKillItemID(1); - else + } else { new_corpse->SetPlayerKillItemID(0); + } + if (killerMob->CastToClient()->isgrouped) { - Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); - if (group != 0) - { - for (int i = 0; i<6; i++) - { - if (group->members[i] != nullptr) - { + auto* group = entity_list.GetGroupByClient(killerMob->CastToClient()); + if (group) { + for (int i = 0; i < 6; i++) { + if (group->members[i]) { new_corpse->AllowPlayerLoot(group->members[i], i); } } } } } + entity_list.AddCorpse(new_corpse, GetID()); SetID(0); //send the become corpse packet to everybody else in the zone. entity_list.QueueClients(this, &app2, true); ApplyIllusionToCorpse(illusion_spell_id, new_corpse); - LeftCorpse = true; + leave_corpse = true; } - } - else { + } else { 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) */ - 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); + } /* 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 */ - if (LeftCorpse && (ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) - { + if (leave_corpse && (ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) { ClearDraggedCorpses(); RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); SendRespawnBinds(); - } - else - { - if (isgrouped) - { - Group *g = GetGroup(); - if (g) + } else { + if (isgrouped) { + auto* g = GetGroup(); + if (g) { g->MemberZoned(this); + } } - Raid* r = entity_list.GetRaidByClient(this); + auto* r = entity_list.GetRaidByClient(this); - if (r) + if (r) { r->MemberZoned(this); + } dead_timer.Start(5000, true); 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 [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); - Mob* oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; - - std::string export_string = fmt::format( + Mob *oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; + auto export_string = fmt::format( "{} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, @@ -2330,12 +2354,24 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy static_cast(attack_skill) ); - // todo: multiple attacks causes this to fire multiple times (DoAttackRounds, DoMain/OffHandAttackRounds, DoRiposte, spells?) - if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) { - if (GetHP() < 0) { - SetHP(0); + if (IsNPC()) { + if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) { + if (GetHP() < 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) { @@ -2364,23 +2400,25 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy SetPet(0); if (GetSwarmOwner()) { - Mob* owner = entity_list.GetMobID(GetSwarmOwner()); - if (owner) + auto* owner = entity_list.GetMobID(GetSwarmOwner()); + if (owner) { owner->SetTempPetCount(owner->GetTempPetCount() - 1); + } } - Mob* killer = GetHateDamageTop(this); + auto* killer = GetHateDamageTop(this); entity_list.RemoveFromTargets(this, p_depop); - if (p_depop == true) + if (p_depop) { return false; + } - int32 illusion_spell_id = spellbonuses.Illusion; + const auto illusion_spell_id = spellbonuses.Illusion; HasAISpellEffects = false; BuffFadeAll(); - uint8 killed_level = GetLevel(); + const auto killed_level = GetLevel(); if (GetClass() == LDON_TREASURE) { // open chest 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)); - Death_Struct* d = (Death_Struct*)app->pBuffer; + auto* d = (Death_Struct*) app->pBuffer; d->spawn_id = GetID(); d->killer_id = killer_mob ? killer_mob->GetID() : 0; d->bindzoneid = 0; @@ -2409,16 +2447,17 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy respawn2->DeathReset(1); } - if (killer_mob && GetClass() != LDON_TREASURE) + if (killer_mob && GetClass() != LDON_TREASURE) { hate_list.AddEntToHateList(killer_mob, damage); + } Mob *give_exp = hate_list.GetDamageTopOnHateList(this); - if (give_exp == nullptr) + if (give_exp) { give_exp = killer; + } if (give_exp && give_exp->HasOwner()) { - bool ownerInGroup = false; if ((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())) || (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) { mod_npc_killed(oos); - uint32 emoteid = GetEmoteID(); - if (emoteid != 0) - DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid); + if (IsNPC()) { + auto emote_id = GetEmoteID(); + if (emote_id) { + DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid); + } + } + if (oos->IsNPC()) { parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); - uint32 emoteid = oos->GetEmoteID(); - if (emoteid != 0) - oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emoteid); + auto emote_id = oos->GetEmoteID(); + if (emote_id) { + oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id); + } + 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(); p_depop = true; diff --git a/zone/bot.cpp b/zone/bot.cpp index a974b5799..279c55de0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2663,7 +2663,13 @@ void Bot::AI_Process() auto pull_target = bot_owner->GetTarget(); 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(); WipeHateList(); AddToHateList(pull_target, 1); @@ -2822,14 +2828,20 @@ void Bot::AI_Process() bool find_target = true; if (assist_mob) { - if (assist_mob->GetTarget()) { - if (assist_mob != this) { + if (GetTarget() != assist_mob->GetTarget()) { + SetTarget(assist_mob->GetTarget()); + } - SetTarget(assist_mob->GetTarget()); - if (HasPet() && (GetClass() != ENCHANTER || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - + if ( + 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 GetPet()->AddToHateList(assist_mob->GetTarget(), 1); GetPet()->SetTarget(assist_mob->GetTarget()); @@ -2837,12 +2849,19 @@ void Bot::AI_Process() } find_target = false; - } - else if (assist_mob != this) { - - SetTarget(nullptr); - if (HasPet() && (GetClass() != ENCHANTER || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + } else if (assist_mob != this) { + if (GetTarget()) { + SetTarget(nullptr); + } + if ( + HasPet() && + ( + GetClass() != ENCHANTER || + GetPet()->GetPetType() != petAnimation || + GetAA(aaAnimationEmpathy) >= 1 + ) + ) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -2852,16 +2871,23 @@ void Bot::AI_Process() } if (find_target) { - if (IsRooted()) { - SetTarget(hate_list.GetClosestEntOnHateList(this, true)); - } - else { - + auto closest = hate_list.GetClosestEntOnHateList(this, true); + if (closest) { + SetTarget(closest); + } + } else { // 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()) { - 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 if (!tar || PASSIVE) { + if (GetTarget()) { + SetTarget(nullptr); + } - SetTarget(nullptr); WipeHateList(); SetAttackFlag(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)) { - client->Message( - Chat::White, + OwnerMessage( fmt::format( - "Failed to delete item by slot from slot {} for {}.", - return_iterator.from_bot_slot, - GetCleanName() + "Failed to delete item by slot from slot {}.", + return_iterator.from_bot_slot ).c_str() ); } @@ -4947,13 +4973,11 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* linker.SetItemInst(return_instance); auto item_link = linker.GenerateLink(); - client->Message( - Chat::Tell, + OwnerMessage( fmt::format( - "{} tells you, 'I have returned {}.'", - GetCleanName(), + "I have returned {}.", item_link - ).c_str() + ) ); 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 if (!database.botdb.SaveItemBySlot(this, trade_iterator.to_bot_slot, trade_iterator.trade_item_instance)) { - client->Message( - Chat::White, + OwnerMessage( fmt::format( - "Failed to save item by slot to slot {} for {}.", - trade_iterator.to_bot_slot, - GetCleanName() + "Failed to save item by slot to slot {}.", + trade_iterator.to_bot_slot ).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); auto item_link = linker.GenerateLink(); - client->Message( - Chat::Tell, + OwnerMessage( fmt::format( - "{} tells you, 'I have accepted {}.'", - GetCleanName(), + "I have accepted {}.", item_link - ).c_str() + ) ); 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); } + const auto export_string = fmt::format( + "{} {} {} {}", + killerMob ? killerMob->GetID() : 0, + damage, + spell_id, + static_cast(attack_skill) + ); + + parse->EventBot(EVENT_DEATH_COMPLETE, this, killerMob, export_string, 0); + entity_list.RemoveBot(GetID()); 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 if(attacked_timer.Check()) { 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); @@ -6554,7 +6584,13 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if(taunting && target && target->IsNPC() && taunt_time) { 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_timer.Start(TauntReuseTime * 1000); } @@ -7512,27 +7548,49 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* 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, - uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) { +bool Bot::CastSpell( + 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; - 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); - if(casting_spell_id == spell_id) + if (casting_spell_id == spell_id) { ZeroCastingVars(); + } - 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 (GetClass() != BARD) { + 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() ); - if(IsSilenced() && !IsDiscipline(spell_id)) + if (IsSilenced() && !IsDiscipline(spell_id)) { MessageString(Chat::White, SILENCED_STRING); + } - if(IsAmnesiad() && IsDiscipline(spell_id)) - + if (IsAmnesiad() && IsDiscipline(spell_id)) { MessageString(Chat::White, MELEE_SILENCE); + } - if(casting_spell_id) + if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); + } 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()) { MessageString(Chat::White, SPELL_WOULDNT_HOLD); - if(casting_spell_id) + if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); + } return false; } - if(DivineAura()) { + if (DivineAura()) { LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, 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; 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 isMainGroupMGB = false; 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); entity_list.AESpell(this, this, spell_id, true); } else { @@ -9564,28 +9629,30 @@ Client* EntityList::GetBotOwnerByBotEntityID(uint16 entityID) { return Result; } -void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { - if(newBot) { - newBot->SetID(GetFreeID()); - newBot->SetSpawned(); - if(SendSpawnPacket) { - if(dontqueue) { +void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) { + if (new_bot) { + new_bot->SetID(GetFreeID()); + new_bot->SetSpawned(); + if (send_spawn_packet) { + if (dont_queue) { EQApplicationPacket* outapp = new EQApplicationPacket(); - newBot->CreateSpawnPacket(outapp); + new_bot->CreateSpawnPacket(outapp); outapp->priority = 6; - QueueClients(newBot, outapp, true); + QueueClients(new_bot, outapp, true); safe_delete(outapp); } else { NewSpawn_Struct* ns = new NewSpawn_Struct; memset(ns, 0, sizeof(NewSpawn_Struct)); - newBot->FillSpawnStruct(ns, newBot); - AddToSpawnQueue(newBot->GetID(), &ns); + new_bot->FillSpawnStruct(ns, new_bot); + AddToSpawnQueue(new_bot->GetID(), &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(newBot->GetID(), newBot)); + + bot_list.push_back(new_bot); + mob_list.insert(std::pair(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() { auto bot_owner = GetBotOwner(); diff --git a/zone/bot.h b/zone/bot.h index 6210c90ca..010261b69 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -681,6 +681,9 @@ public: int32 GetBaseDR() { return _baseDR; } int32 GetBaseCorrup() { return _baseCorrup; } + void SignalBot(int signal_id); + void OwnerMessage(std::string message); + protected: virtual void PetAIProcess(); virtual void BotMeditate(bool isSitting); @@ -700,6 +703,7 @@ protected: //void SetRaidSlower(bool flag = true) { m_CastingRoles.RaidSlower = flag; } //void SetRaidNuker(bool flag = true) { m_CastingRoles.RaidNuker = flag; } //void SetRaidDoter(bool flag = true) { m_CastingRoles.RaidDoter = flag; } + std::deque bot_signal_q; private: // Class Members diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 7fb10a6a8..cd1ff5706 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -2585,8 +2585,16 @@ void bot_command_aggressive(Client *c, const Seperator *sep) } my_bot->InterruptSpell(); - if (candidate_count == 1) - Bot::BotGroupSay(my_bot, "Using '%s'", spells[local_entry->spell_id].name); + if (candidate_count == 1) { + 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()); ++success_count; @@ -2803,10 +2811,22 @@ void bot_command_attack(Client *c, const Seperator *sep) } if (attacker_count == 1 && first_attacker) { - Bot::BotGroupSay(first_attacker, "Attacking %s!", target_mob->GetCleanName()); - } - else { - c->Message(Chat::White, "%i of your bots are attacking %s!", sbl.size(), target_mob->GetCleanName()); + Bot::BotGroupSay( + first_attacker, + fmt::format( + "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(); - if (candidate_count == 1) - Bot::BotGroupSay(my_bot, "Using '%s'", spells[local_entry->spell_id].name); + if (candidate_count == 1) { + 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()); ++success_count; @@ -3285,15 +3313,35 @@ void bot_command_follow(Client *c, const Seperator *sep) bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } + if (sbl.size() == 1) { Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); - Bot::BotGroupSay(sbl.front(), "Following %s", ((follow_mob) ? (follow_mob->GetCleanName()) : ("'nullptr'"))); - } - else { - if (reset) - c->Message(Chat::White, "%i of your bots are following their default assignments", sbl.size()); - else - c->Message(Chat::White, "%i of your bots are following %s", sbl.size(), target_mob->GetCleanName()); + Bot::BotGroupSay( + sbl.front(), + fmt::format( + "Following {}.", + follow_mob ? follow_mob->GetCleanName() : "no one" + ).c_str() + ); + } 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) { - Bot::BotGroupSay(sbl.front(), "%suarding this position.", (clear ? "No longer g" : "G")); - } - else { + Bot::BotGroupSay( + sbl.front(), + 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 " : "")); } } @@ -3469,10 +3522,22 @@ void bot_command_hold(Client *c, const Seperator *sep) } if (sbl.size() == 1) { - Bot::BotGroupSay(sbl.front(), "%solding my attacks.", (clear ? "No longer h" : "H")); - } - else { - c->Message(Chat::White, "%i of your bots are %sholding their attacks.", sbl.size(), (clear ? "no longer " : "")); + Bot::BotGroupSay( + sbl.front(), + fmt::format( + "{}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(); my_bot->InterruptSpell(); - Bot::BotGroupSay(my_bot, "Attempting to pick the lock.."); + Bot::BotGroupSay(my_bot, "Attempting to pick the lock."); std::list door_list; entity_list.GetDoorsList(door_list); @@ -4251,7 +4316,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep) ++open_count; } 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") : (""))); @@ -4791,38 +4856,78 @@ void bot_command_taunt(Client *c, const Seperator *sep) int taunting_count = 0; for (auto bot_iter : sbl) { - if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) + if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) { continue; + } - if (toggle_taunt) + if (toggle_taunt) { bot_iter->SetTaunting(!bot_iter->IsTaunting()); - else + } else { bot_iter->SetTaunting(taunt_state); + } - if (sbl.size() == 1) - Bot::BotGroupSay(bot_iter, "I am %s taunting", bot_iter->IsTaunting() ? "now" : "no longer"); + if (sbl.size() == 1) { + Bot::BotGroupSay( + bot_iter, + fmt::format( + "I am {} taunting.", + bot_iter->IsTaunting() ? "now" : "no longer" + ).c_str() + ); + } ++taunting_count; } + for (auto bot_iter : sbl) { - if (!bot_iter->HasPet()) + if (!bot_iter->HasPet()) { continue; - if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) + } + + if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { continue; - if (toggle_taunt) + } + + if (toggle_taunt) { bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting()); - else + } else { 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; } if (taunting_count) { - 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"))); - else - 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"))); + if (toggle_taunt) { + c->Message( + Chat::White, + 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 { 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)) { Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); } else if (c->GetBotOption(Client::booSpawnMessageTell)) { - c->Message( - Chat::Tell, - fmt::format( - "{} tells you, \"{}\"", - my_bot->GetCleanName(), - bot_spawn_message[message_index] - ).c_str() - ); + my_bot->OwnerMessage(bot_spawn_message[message_index]); } } @@ -6663,9 +6761,11 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep) Bot::BotGroupSay( bot_iter, - "My current stance is '%s' (%i)", - EQ::constants::GetStanceName(bot_iter->GetBotStance()), - bot_iter->GetBotStance() + fmt::format( + "My current stance is {} ({}).", + 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) { - 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; + } + 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; } const int ab_mask = ActionableBots::ABM_NoFilter; std::list 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; + } for (auto bot_iter : sbl) { - if (!bot_iter) + if (!bot_iter) { continue; - - //Bot::BotGroupSay(bot_iter, "Whee!"); + } bot_iter->WipeHateList(); bot_iter->SetTarget(nullptr); bot_iter->Teleport(c->GetPosition()); bot_iter->DoAnim(0); - if (!bot_iter->HasPet()) + if (!bot_iter->HasPet()) { continue; + } bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->SetTarget(nullptr); bot_iter->GetPet()->Teleport(c->GetPosition()); } - if (sbl.size() == 1) - c->Message(Chat::White, "Summoned %s to you", ((sbl.front()) ? (sbl.front()->GetCleanName()) : ("'nullptr'"))); - else - c->Message(Chat::White, "Summoned %i bots to you", sbl.size()); + if (sbl.size() == 1) { + c->Message( + Chat::White, + 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) @@ -9182,15 +9304,13 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) break; } - c->Message( - Chat::Tell, + my_bot->OwnerMessage( fmt::format( - "{} tells you, 'My {} (Slot {}) {} already unequipped.'", - my_bot->GetCleanName(), + "My {} (Slot {}) {} already unequipped.", EQ::invslot::GetInvPossessionsSlotName(slot_id), slot_id, slot_message - ).c_str() + ) ); return; } @@ -9247,15 +9367,13 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) my_bot->BotRemoveEquipItem(slot_id); my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); - c->Message( - Chat::Tell, + my_bot->OwnerMessage( fmt::format( - "{} tells you, 'I have unequipped {} from my {} (Slot {}).'", - my_bot->GetCleanName(), + "I have unequipped {} from my {} (Slot {}).", linker.GenerateLink(), EQ::invslot::GetInvPossessionsSlotName(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; casting_bot->InterruptSpell(); - if (annouce_cast) - Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName()); + if (annouce_cast) { + 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); } @@ -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) { - if (!bot_owner) + if (!bot_owner) { return; + } - if (!MyBots::IsMyBot(bot_owner, druid_bot)) + if (!MyBots::IsMyBot(bot_owner, druid_bot)) { druid_bot = nullptr; - if (!MyBots::IsMyBot(bot_owner, wizard_bot)) + } + + if (!MyBots::IsMyBot(bot_owner, wizard_bot)) { wizard_bot = nullptr; + } + if (!druid_bot && !wizard_bot) { bot_owner->Message(Chat::White, "No bots are capable of performing this action"); return; } - bot_owner->Message(Chat::White, "The following destinations are available:"); if (!local_list) { - bot_owner->Message(Chat::White, "None"); + bot_owner->Message(Chat::White, "There are no destinations you can be taken to."); return; } std::string msg; std::string text_link; - int destinations = 0; + auto destination_count = 0; + auto destination_number = 1; for (auto list_iter : *local_list) { auto local_entry = list_iter->SafeCastToDepart(); - 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; + if (!local_entry) { 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; - 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()); - ++destinations; + } + + msg = fmt::format( + "{}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; } } - 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) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index abca626b9..c076c5372 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -98,8 +98,16 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { } castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); - if(castedSpell) - BotGroupSay(this, "Attempting to mez %s.", addMob->GetCleanName()); + if (castedSpell) { + BotGroupSay( + this, + fmt::format( + "Attempting to mesmerize {} with {}.", + addMob->GetCleanName(), + spells[botSpell.SpellId].name + ).c_str() + ); + } } break; } @@ -272,7 +280,13 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { Group *g = GetGroup(); 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; imembers[i] && !g->members[i]->qglobal) { @@ -281,10 +295,17 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { } } } - } - else { - if(tar != this) //we don't need spam of bots healing themselves - BotGroupSay(this, "Casting %s on %s", spells[botSpell.SpellId].name, tar->GetCleanName()); + } else { + if (tar != this) { //we don't need spam of bots healing themselves + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + spells[botSpell.SpellId].name, + tar->GetCleanName() + ).c_str() + ); + } 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); - if(castedSpell && GetClass() != BARD) - BotGroupSay(this, "Attempting to slow %s.", tar->GetCleanName()); + if (castedSpell && GetClass() != BARD) { + BotGroupSay( + this, + fmt::format( + "Attempting to slow {} with {}.", + tar->GetCleanName(), + spells[botSpell.SpellId].name + ).c_str() + ); + } } break; } @@ -982,7 +1011,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); 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; } } @@ -1596,8 +1632,16 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); - if(castedSpell) - Say("Casting %s on %s, please stay in range!", spells[botSpell.SpellId].name, tar->GetCleanName()); + if (castedSpell) { + BotGroupSay( + this, + fmt::format( + "Casting {} on {}, please stay in range!", + spells[botSpell.SpellId].name, + tar->GetCleanName() + ).c_str() + ); + } return castedSpell; } diff --git a/zone/client.cpp b/zone/client.cpp index 9d886f19b..1e602ac97 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1170,8 +1170,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } Mob* sender = this; - if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) + if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) { sender = GetPet(); + } if (!is_silent) { 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); - if (sender != this) + if (sender != this) { break; + } - if(quest_manager.ProximitySayInUse()) + if (quest_manager.ProximitySayInUse()) { entity_list.ProcessProximitySay(message, this, language); + } - if (GetTarget() != 0 && GetTarget()->IsNPC() && - !IsInvisible(GetTarget())) { - if(!GetTarget()->CastToNPC()->IsEngaged()) { - CheckLDoNHail(GetTarget()); - CheckEmoteHail(GetTarget(), message); + if ( + GetTarget() && + GetTarget()->IsNPC() && + !IsInvisible(GetTarget()) + ) { + auto* t = GetTarget()->CastToNPC(); + if (!t->IsEngaged()) { + CheckLDoNHail(t); + CheckEmoteHail(t, message); - if(DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { - NPC *tar = GetTarget()->CastToNPC(); - parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); + if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { + + parse->EventNPC(EVENT_SAY, t, this, message, language); - if(RuleB(TaskSystem, EnableTaskSystem)) { - if (UpdateTasksOnSpeakWith(tar)) { - tar->DoQuestPause(this); + if (RuleB(TaskSystem, EnableTaskSystem)) { + if (UpdateTasksOnSpeakWith(t)) { + t->DoQuestPause(this); } } } - } - else { - if (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { - parse->EventNPC(EVENT_AGGRO_SAY, GetTarget()->CastToNPC(), this, message, language); + } else { + if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { + parse->EventNPC(EVENT_AGGRO_SAY, t, 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; } 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); } diff --git a/zone/client.h b/zone/client.h index a2106e33f..235f0424a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1408,7 +1408,7 @@ public: void SuspendMinion(int value); void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); void NotifyNewTitlesAvailable(); - void Signal(uint32 data); + void Signal(int signal_id); Mob *GetBindSightTarget() { return bind_sight_target; } void SetBindSightTarget(Mob *n) { bind_sight_target = n; } const uint16 GetBoatID() const { return controlling_boat_id; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 78b445c64..0b5714cd7 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11200,13 +11200,19 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) 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); - Mob *Target = GetTarget(); - if (Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, export_string, 0); + auto t = GetTarget(); + if (t) { + 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 + } } } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 349bde7c4..6927f86b8 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -163,6 +163,11 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_TASK_BEFORE_UPDATE", "EVENT_AA_BUY", "EVENT_AA_GAIN" + #ifdef BOTS + , + "EVENT_SPELL_EFFECT_BOT", + "EVENT_SPELL_EFFECT_BUFF_TIC_BOT" +#endif }; PerlembParser::PerlembParser() : perl(nullptr) @@ -170,6 +175,11 @@ PerlembParser::PerlembParser() : perl(nullptr) global_npc_quest_status_ = questUnloaded; player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded; + +#ifdef BOTS + bot_quest_status_ = questUnloaded; + global_bot_quest_status_ = questUnloaded; +#endif } PerlembParser::~PerlembParser() @@ -203,15 +213,28 @@ void PerlembParser::ReloadQuests() global_npc_quest_status_ = questUnloaded; 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(); spell_quest_status_.clear(); } int PerlembParser::EventCommon( - QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct* spell, Mob *mob, - uint32 extradata, bool global, std::vector *extra_pointers -) -{ + QuestEventID event, + uint32 objid, + const char *data, + Mob *npcmob, + EQ::ItemInstance *item_inst, + const SPDat_Spell_Struct* spell, + Mob *mob, + uint32 extradata, + bool global, + std::vector *extra_pointers +) { if (!perl) { return 0; } @@ -220,20 +243,46 @@ int PerlembParser::EventCommon( return 0; } - bool isPlayerQuest = false; - bool isGlobalPlayerQuest = false; - bool isGlobalNPC = false; - bool isItemQuest = false; - bool isSpellQuest = false; + bool isPlayerQuest = false; + bool isGlobalPlayerQuest = false; + bool isGlobalNPC = false; + bool isBotQuest = false; + bool isGlobalBotQuest = false; + bool isItemQuest = false; + bool isSpellQuest = false; + std::string package_name; GetQuestTypes( - isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, - event, npcmob, item_inst, mob, global + isPlayerQuest, + isGlobalPlayerQuest, + isBotQuest, + isGlobalBotQuest, + isGlobalNPC, + isItemQuest, + isSpellQuest, + event, + npcmob, + item_inst, + mob, + global ); + GetQuestPackageName( - isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, - package_name, event, objid, data, npcmob, item_inst, global + isPlayerQuest, + isGlobalPlayerQuest, + isBotQuest, + isGlobalBotQuest, + isGlobalNPC, + isItemQuest, + isSpellQuest, + package_name, + event, + objid, + data, + npcmob, + item_inst, + global ); const char *sub_name = QuestEventSubroutines[event]; @@ -249,6 +298,8 @@ int PerlembParser::EventCommon( ExportQGlobals( isPlayerQuest, isGlobalPlayerQuest, + isBotQuest, + isGlobalBotQuest, isGlobalNPC, isItemQuest, isSpellQuest, @@ -264,6 +315,8 @@ int PerlembParser::EventCommon( ExportMobVariables( isPlayerQuest, isGlobalPlayerQuest, + isBotQuest, + isGlobalBotQuest, isGlobalNPC, isItemQuest, isSpellQuest, @@ -290,6 +343,8 @@ int PerlembParser::EventCommon( if (isPlayerQuest || isGlobalPlayerQuest) { 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) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst, nullptr); } else if (isSpellQuest) { @@ -304,19 +359,19 @@ int PerlembParser::EventCommon( } 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 *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( - 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 *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( @@ -345,11 +400,11 @@ int PerlembParser::EventItem( } 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 *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) @@ -860,77 +915,93 @@ int PerlembParser::SendCommands( std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; 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()) { - std::string eval_str = cl; - eval_str += " = undef;"; - perl->eval(eval_str.c_str()); + auto e = fmt::format("{} = undef;", cl); + perl->eval(e.c_str()); } if (clear_vars_.find(np) != clear_vars_.end()) { - std::string eval_str = np; - eval_str += " = undef;"; - perl->eval(eval_str.c_str()); + auto e = fmt::format("{} = undef;", np); + perl->eval(e.c_str()); } if (clear_vars_.find(qi) != clear_vars_.end()) { - std::string eval_str = qi; - eval_str += " = undef;"; - perl->eval(eval_str.c_str()); + auto e = fmt::format("{} = undef;", qi); + perl->eval(e.c_str()); } if (clear_vars_.find(sp) != clear_vars_.end()) { - std::string eval_str = sp; - eval_str += " = undef;"; - perl->eval(eval_str.c_str()); + auto e = fmt::format("{} = undef;", sp); + perl->eval(e.c_str()); } if (clear_vars_.find(enl) != clear_vars_.end()) { - std::string eval_str = enl; - eval_str += " = undef;"; - perl->eval(eval_str.c_str()); + auto e = fmt::format("{} = undef;", enl); + perl->eval(e.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 Client *curc = quest_manager.GetInitiator(); - snprintf(namebuf, 64, "%s::client", pkgprefix); - SV *client = get_sv(namebuf, true); - if (curc != nullptr) { + buf = fmt::format("{}::client", pkgprefix); + SV *client = get_sv(buf.c_str(), true); + if (curc) { sv_setref_pv(client, "Client", curc); - } - else { + } else { //clear out the value, mainly to get rid of blessedness sv_setsv(client, _empty_sv); } //only export NPC if it's a npc quest - if (!other->IsClient()) { + if (!other->IsClient() && other->IsNPC()) { NPC *curn = quest_manager.GetNPC(); - snprintf(namebuf, 64, "%s::npc", pkgprefix); - SV *npc = get_sv(namebuf, true); + buf = fmt::format("{}::npc", pkgprefix); + SV *npc = get_sv(buf.c_str(), true); 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 if (item_inst) { EQ::ItemInstance *curi = quest_manager.GetQuestItem(); - snprintf(namebuf, 64, "%s::questitem", pkgprefix); - SV *questitem = get_sv(namebuf, true); + buf = fmt::format("{}::questitem", pkgprefix); + SV *questitem = get_sv(buf.c_str(), true); sv_setref_pv(questitem, "QuestItem", curi); } if (spell) { const SPDat_Spell_Struct* current_spell = quest_manager.GetQuestSpell(); SPDat_Spell_Struct* real_spell = const_cast(current_spell); - snprintf(namebuf, 64, "%s::spell", pkgprefix); - SV *spell = get_sv(namebuf, true); + buf = fmt::format("{}::spell", pkgprefix); + SV *spell = get_sv(buf.c_str(), true); 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); #endif @@ -944,11 +1015,20 @@ int PerlembParser::SendCommands( std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; 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_[np] = 1; clear_vars_[qi] = 1; clear_vars_[sp] = 1; clear_vars_[enl] = 1; + +#ifdef BOTS + clear_vars_[bot] = 1; +#endif } #endif @@ -967,19 +1047,16 @@ int PerlembParser::SendCommands( #ifdef EMBPERL_XS_CLASSES if (!quest_manager.QuestsRunning()) { - auto iter = clear_vars_.begin(); std::string eval_str; - while (iter != clear_vars_.end()) { - eval_str += iter->first; - eval_str += " = undef;"; - ++iter; + for (const auto &v : clear_vars_) { + eval_str += fmt::format("{} = undef;", v.first); } + clear_vars_.clear(); try { perl->eval(eval_str.c_str()); - } - catch (std::string e) { + } catch (std::string e) { AddError( fmt::format( "Script Clear Error | Error [{}]", @@ -1023,29 +1100,61 @@ void PerlembParser::MapFunctions() } void PerlembParser::GetQuestTypes( - bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, - bool &isSpellQuest, QuestEventID event, NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob, bool global -) -{ - if (event == EVENT_SPELL_EFFECT_CLIENT || + bool &isPlayerQuest, + bool &isGlobalPlayerQuest, + bool &isBotQuest, + bool &isGlobalBotQuest, + 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 || +#ifdef BOTS + event == EVENT_SPELL_EFFECT_BOT || +#endif event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT || event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC || +#ifdef BOTS + event == EVENT_SPELL_EFFECT_BUFF_TIC_BOT || +#endif event == EVENT_SPELL_FADE || - event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) { + event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE + ) { isSpellQuest = true; - } - else { - if (!npcmob && mob) { + } else { + if (npcmob) { if (!item_inst) { if (global) { - isGlobalPlayerQuest = true; - } - else { - isPlayerQuest = true; + if (npcmob->IsBot()) { + isGlobalBotQuest = 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; } } @@ -1055,6 +1164,8 @@ void PerlembParser::GetQuestTypes( void PerlembParser::GetQuestPackageName( bool &isPlayerQuest, bool &isGlobalPlayerQuest, + bool &isBotQuest, + bool &isGlobalBotQuest, bool &isGlobalNPC, bool &isItemQuest, bool &isSpellQuest, @@ -1062,7 +1173,7 @@ void PerlembParser::GetQuestPackageName( QuestEventID event, uint32 objid, const char *data, - NPC *npcmob, + Mob *npcmob, EQ::ItemInstance *item_inst, bool global ) @@ -1070,6 +1181,8 @@ void PerlembParser::GetQuestPackageName( if ( !isPlayerQuest && !isGlobalPlayerQuest && + !isBotQuest && + !isGlobalBotQuest && !isItemQuest && !isSpellQuest ) { @@ -1077,30 +1190,30 @@ void PerlembParser::GetQuestPackageName( isGlobalNPC = true; package_name = "qst_global_npc"; } else { - package_name = "qst_npc_"; - package_name += std::to_string(npcmob->GetNPCTypeID()); + package_name = fmt::format("qst_npc_{}", npcmob->GetNPCTypeID()); } } else if (isItemQuest) { // need a valid EQ::ItemInstance pointer check here..unsure how to cancel this process const EQ::ItemData *item = item_inst->GetItem(); - package_name = "qst_item_"; - package_name += std::to_string(item->ID); + package_name = fmt::format("qst_item_{}", item->ID); } else if (isPlayerQuest) { package_name = "qst_player"; } else if (isGlobalPlayerQuest) { package_name = "qst_global_player"; + } else if (isBotQuest) { + package_name = "qst_bot"; + } else if (isGlobalBotQuest) { + package_name = "qst_global_bot"; } else { - package_name = "qst_spell_"; - package_name += std::to_string(objid); + package_name = fmt::format("qst_spell_{}", 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 char_id = mob->CastToClient()->CharacterID(); - } - else { + } else { if (npcmob) { char_id = -static_cast(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( - bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id -) -{ + 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 +) { //NPC quest - if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { + if ( + !isPlayerQuest && + !isGlobalPlayerQuest && + !isBotQuest && + !isGlobalBotQuest && + !isItemQuest && + !isSpellQuest + ) { //only export for npcs that are global enabled. if (npcmob && npcmob->GetQglobal()) { std::map globhash; - QGlobalCache *npc_c = nullptr; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; + QGlobalCache *npc_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; //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()) { char_c = mob->CastToClient()->GetQGlobals(); } + zone_c = zone->GetQGlobals(); if (!npc_c) { - npc_c = npcmob->CreateQGlobals(); - npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); + if (npcmob && npcmob->IsNPC()) { + npc_c = npcmob->CastToNPC()->CreateQGlobals(); + npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); + } } if (!char_c) { @@ -1157,7 +1295,8 @@ void PerlembParser::ExportQGlobals( npc_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, - zone->GetZoneID()); + zone->GetZoneID()) + ; } if (char_c) { @@ -1166,7 +1305,8 @@ void PerlembParser::ExportQGlobals( char_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, - zone->GetZoneID()); + zone->GetZoneID() + ); } if (zone_c) { @@ -1175,7 +1315,8 @@ void PerlembParser::ExportQGlobals( zone_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, - zone->GetZoneID()); + zone->GetZoneID() + ); } auto iter = globalMap.begin(); @@ -1184,19 +1325,20 @@ void PerlembParser::ExportQGlobals( ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; } + ExportHash(package_name.c_str(), "qglobals", globhash); } - } - else { + } else { std::map globhash; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; //retrieve our globals if (mob && mob->IsClient()) { char_c = mob->CastToClient()->GetQGlobals(); } - zone_c = zone->GetQGlobals(); + + zone_c = zone->GetQGlobals(); if (!char_c) { if (mob && mob->IsClient()) { @@ -1226,15 +1368,23 @@ void PerlembParser::ExportQGlobals( ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; } + ExportHash(package_name.c_str(), "qglobals", globhash); } } void PerlembParser::ExportMobVariables( - bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob -) -{ + bool isPlayerQuest, + bool isGlobalPlayerQuest, + bool isBotQuest, + bool isGlobalBotQuest, + bool isGlobalNPC, + bool isItemQuest, + bool isSpellQuest, + std::string &package_name, + Mob *mob, + Mob *npcmob +) { uint8 fac = 0; if (mob && mob->IsClient()) { 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()); } - if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest) { - if (mob && npcmob && mob->IsClient()) { +#ifdef BOTS + 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(); fac = client->GetFactionLevel( @@ -1261,8 +1424,15 @@ void PerlembParser::ExportMobVariables( ExportVar(package_name.c_str(), "userid", mob->GetID()); } - if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { - if (npcmob) { + if ( + !isPlayerQuest && + !isGlobalPlayerQuest && + !isBotQuest && + !isGlobalBotQuest && + !isItemQuest && + !isSpellQuest + ) { + if (npcmob->IsNPC()) { ExportVar(package_name.c_str(), "mname", npcmob->GetName()); ExportVar(package_name.c_str(), "mobid", npcmob->GetID()); ExportVar(package_name.c_str(), "mlevel", npcmob->GetLevel()); @@ -1331,14 +1501,20 @@ void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) } void PerlembParser::ExportEventVariables( - std::string &package_name, QuestEventID event, uint32 objid, const char *data, - NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob, uint32 extradata, std::vector *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 *extra_pointers +) { switch (event) { case EVENT_SAY: { - if (npcmob && mob) { - npcmob->DoQuestPause(mob); + if (npcmob && npcmob->IsNPC() && mob) { + npcmob->CastToNPC()->DoQuestPause(mob); } ExportVar(package_name.c_str(), "data", objid); @@ -1568,8 +1744,15 @@ void PerlembParser::ExportEventVariables( break; } + +#ifdef BOTS + case EVENT_SPELL_EFFECT_BUFF_TIC_BOT: +#endif case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: +#ifdef BOTS + case EVENT_SPELL_EFFECT_BOT: +#endif case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_NPC: 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 *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 *extra_pointers +) { + return EventCommon(evt, 0, data.c_str(), bot, nullptr, nullptr, mob, extra_data, true, extra_pointers); +} +#endif + #endif diff --git a/zone/embparser.h b/zone/embparser.h index f1b18aafd..24cb27756 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -48,18 +48,73 @@ public: PerlembParser(); ~PerlembParser(); - virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *extra_pointers); + virtual int EventNPC( + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalNPC( + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventPlayer( + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalPlayer( + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventItem( + QuestEventID evt, + Client *client, + EQ::ItemInstance *item, + Mob *mob, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventSpell( + QuestEventID evt, + Mob* mob, + Client *client, + uint32 spell_id, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + +#ifdef BOTS + virtual int EventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); +#endif virtual bool HasQuestSub(uint32 npcid, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -68,6 +123,11 @@ public: virtual bool SpellHasQuestSub(uint32 spell_id, 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 LoadGlobalNPCScript(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 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 std::string GetVar(std::string name); virtual void ReloadQuests(); @@ -91,25 +156,98 @@ private: void ExportVar(const char* pkgprefix, const char* varname, const char* classname, void* 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, - uint32 extradata, bool global, std::vector *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); + int EventCommon( + QuestEventID event, + uint32 objid, + const char* data, + Mob* npcmob, + EQ::ItemInstance* item_inst, + const SPDat_Spell_Struct* spell, + Mob* mob, + uint32 extradata, + bool global, + std::vector *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 GetQuestTypes(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, - bool &isSpellQuest, QuestEventID event, NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob, bool global); - void GetQuestPackageName(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, - bool &isSpellQuest, std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, EQ::ItemInstance* item_inst, bool global); - void ExportCharID(const std::string &package_name, int &char_id, NPC *npcmob, Mob *mob); - void ExportQGlobals(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id); - void ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob); + void GetQuestTypes( + bool &isPlayerQuest, + bool &isGlobalPlayerQuest, + bool &isBotQuest, + bool &isGlobalBotQuest, + bool &isGlobalNPC, + bool &isItemQuest, + bool &isSpellQuest, + QuestEventID event, + 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 ExportItemVariables(std::string &package_name, Mob *mob); - void ExportEventVariables(std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob, uint32 extradata, std::vector *extra_pointers); + void ExportEventVariables( + std::string &package_name, + QuestEventID event, + uint32 objid, + const char* data, + Mob* npcmob, + EQ::ItemInstance* item_inst, + Mob* mob, + uint32 extradata, + std::vector *extra_pointers + ); std::map npc_quest_status_; PerlQuestStatus global_npc_quest_status_; @@ -118,6 +256,11 @@ private: std::map item_quest_status_; std::map spell_quest_status_; +#ifdef BOTS + PerlQuestStatus bot_quest_status_; + PerlQuestStatus global_bot_quest_status_; +#endif + std::map vars_; SV *_empty_sv; std::map clear_vars_; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 87e9a2a22..3a7b714ed 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3134,38 +3134,38 @@ void Perl__crosszonesetentityvariablebynpctypeid(int npc_id, const char* variabl 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); } -void Perl__crosszonesignalclientbygroupid(int group_id, uint32 signal) +void Perl__crosszonesignalclientbygroupid(int group_id, int 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); } -void Perl__crosszonesignalclientbyguildid(int guild_id, uint32 signal) +void Perl__crosszonesignalclientbyguildid(int guild_id, int 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); } -void Perl__crosszonesignalclientbyname(const char* client_name, uint32 signal) +void Perl__crosszonesignalclientbyname(const char* client_name, int signal) { int update_identifier = 0; 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); } @@ -3599,22 +3599,22 @@ void Perl__worldwidesetentityvariablenpc(const char* variable_name, const char* 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); } -void Perl__worldwidesignalclient(uint32 signal) +void Perl__worldwidesignalclient(int 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); } -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); } @@ -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, uint8))&Perl__worldwidesetentityvariableclient); package.add("worldwidesetentityvariablenpc", &Perl__worldwidesetentityvariablenpc); - package.add("worldwidesignalclient", (void(*)(uint32))&Perl__worldwidesignalclient); - package.add("worldwidesignalclient", (void(*)(uint32, uint8))&Perl__worldwidesignalclient); - package.add("worldwidesignalclient", (void(*)(uint32, uint8, uint8))&Perl__worldwidesignalclient); + package.add("worldwidesignalclient", (void(*)(int))&Perl__worldwidesignalclient); + package.add("worldwidesignalclient", (void(*)(int, uint8))&Perl__worldwidesignalclient); + package.add("worldwidesignalclient", (void(*)(int, uint8, uint8))&Perl__worldwidesignalclient); package.add("worldwidesignalnpc", &Perl__worldwidesignalnpc); package.add("worldwideupdateactivity", (void(*)(uint32, int))&Perl__worldwideupdateactivity); package.add("worldwideupdateactivity", (void(*)(uint32, int, int))&Perl__worldwideupdateactivity); diff --git a/zone/entity.cpp b/zone/entity.cpp index 671d7660b..0b822f1fd 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5100,14 +5100,12 @@ void EntityList::GateAllClients() } } -void EntityList::SignalAllClients(uint32 data) +void EntityList::SignalAllClients(int signal_id) { - auto it = client_list.begin(); - while (it != client_list.end()) { - Client *ent = it->second; - if (ent) - ent->Signal(data); - ++it; + for (const auto& c : client_list) { + if (c.second) { + c.second->Signal(signal_id); + } } } @@ -5159,8 +5157,9 @@ void EntityList::GetClientList(std::list &c_list) void EntityList::GetBotList(std::list &b_list) { 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 EntityList::GetBotListByCharacterID(uint32 character_id, uint return client_bot_list; } - for (auto bot : bot_list) { + for (const auto& b : bot_list) { if ( - bot->GetOwner() && - bot->GetBotOwnerCharacterID() == character_id && - ( - !class_id || - bot->GetClass() == class_id - ) + b->GetOwner() && + b->GetBotOwnerCharacterID() == character_id ) { - client_bot_list.push_back(bot); + client_bot_list.push_back(b); } } @@ -5196,14 +5191,45 @@ std::vector EntityList::GetBotListByClientName(std::string client_name) return client_bot_list; } - for (auto bot : bot_list) { - if (bot->GetOwner() && Strings::ToLower(bot->GetOwner()->GetCleanName()) == Strings::ToLower(client_name)) { - client_bot_list.push_back(bot); + for (const auto& b : bot_list) { + if ( + b->GetOwner() && + Strings::ToLower(b->GetOwner()->GetCleanName()) == Strings::ToLower(client_name) + ) { + client_bot_list.push_back(b); } } 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 void EntityList::GetCorpseList(std::list &c_list) diff --git a/zone/entity.h b/zone/entity.h index 05551d634..a9e6907f7 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -508,7 +508,7 @@ public: void UnMarkNPC(uint16 ID); void GateAllClients(); - void SignalAllClients(uint32 data); + void SignalAllClients(int signal_id); void UpdateQGlobal(uint32 qid, QGlobal newGlobal); void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); void SendFindableNPCList(Client *c); @@ -537,6 +537,9 @@ public: inline const std::list &GetBotList() { return bot_list; } std::vector GetBotListByCharacterID(uint32 character_id, uint8 class_id = 0); std::vector 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 inline const std::unordered_map &GetCorpseList() { return corpse_list; } inline const std::unordered_map &GetObjectList() { return object_list; } @@ -605,7 +608,7 @@ private: // Please Do Not Declare Any EntityList Class Members After This Comment #ifdef BOTS 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); Mob* GetMobByBotID(uint32 botID); Bot* GetBotByBotID(uint32 botID); diff --git a/zone/event_codes.h b/zone/event_codes.h index 312fd7d72..922dcd505 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -106,6 +106,10 @@ typedef enum { EVENT_TASK_BEFORE_UPDATE, EVENT_AA_BUY, EVENT_AA_GAIN, +#ifdef BOTS + EVENT_SPELL_EFFECT_BOT, + EVENT_SPELL_EFFECT_BUFF_TIC_BOT, +#endif _LargestEventID } QuestEventID; diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 5f2542fec..a52e0a4fd 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -84,6 +84,16 @@ uint32 Lua_Bot::GetBotItemIDBySlot(uint16 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() { Lua_Safe_Call_Int(); return self->GetExpansionBitmask(); @@ -117,9 +127,11 @@ luabind::scope lua_register_bot() { .def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask) .def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner) .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("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 diff --git a/zone/lua_bot.h b/zone/lua_bot.h index 52cbc1dd2..227932ffb 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -42,9 +42,11 @@ public: int GetExpansionBitmask(); Lua_Mob GetOwner(); bool HasBotItem(uint32 item_id); + void OwnerMessage(std::string message); void RemoveBotItem(uint32 item_id); void SetExpansionBitmask(int expansion_bitmask); void SetExpansionBitmask(int expansion_bitmask, bool save); + void SignalBot(int signal_id); }; #endif diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 7278408c1..fbcf2e727 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1515,9 +1515,9 @@ void Lua_Client::NotifyNewTitlesAvailable() { self->NotifyNewTitlesAvailable(); } -void Lua_Client::Signal(uint32 id) { +void Lua_Client::Signal(int signal_id) { Lua_Safe_Call_Void(); - self->Signal(id); + self->Signal(signal_id); } 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("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix) .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("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) diff --git a/zone/lua_client.h b/zone/lua_client.h index 364d8bd7b..c9f112aa1 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -363,7 +363,7 @@ public: uint32 GetMoney(uint8 type, uint8 subtype); void OpenLFGuildWindow(); void NotifyNewTitlesAvailable(); - void Signal(uint32 id); + void Signal(int signal_id); void AddAlternateCurrencyValue(uint32 currency, int amount); void SetAlternateCurrencyValue(uint32 currency, int amount); int GetAlternateCurrencyValue(uint32 currency); diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 5c6f7a2f3..6eb017ee9 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -440,6 +440,21 @@ Lua_Bot_List Lua_EntityList::GetBotListByClientName(std::string client_name) { 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 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("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers) .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) +#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); } diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index e8c4ac53b..58eeb7ae7 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -140,6 +140,9 @@ public: 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 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 }; diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 7c0489108..cf1ba2292 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -6,6 +6,7 @@ #include "client.h" #include "npc.h" #ifdef BOTS +#include "bot.h" #include "lua_bot.h" #endif #include "lua_item.h" @@ -1371,13 +1372,17 @@ bool Lua_Mob::EntityVariableExists(const char *name) { return self->EntityVariableExists(name); } -void Lua_Mob::Signal(uint32 id) { +void Lua_Mob::Signal(int signal_id) { Lua_Safe_Call_Void(); - if(self->IsClient()) { - self->CastToClient()->Signal(id); - } else if(self->IsNPC()) { - self->CastToNPC()->SignalNPC(id); + if (self->IsClient()) { + self->CastToClient()->Signal(signal_id); + } else if (self->IsNPC()) { + 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("Shout", (void(Lua_Mob::*)(const char*))& 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("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int))&Lua_Mob::SpellFinished) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 4447860b9..1120b7790 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -304,7 +304,7 @@ public: const char* GetEntityVariable(const char *name); void SetEntityVariable(const char *name, const char *value); bool EntityVariableExists(const char *name); - void Signal(uint32 id); + void Signal(int signal_id); 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, int min_damage); diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 6dbf92bff..456b60556 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -12,9 +12,9 @@ struct Lua_NPC_Loot_List { std::vector entries; }; -void Lua_NPC::Signal(int id) { +void Lua_NPC::Signal(int signal_id) { Lua_Safe_Call_Void(); - self->SignalNPC(id); + self->SignalNPC(signal_id); } int Lua_NPC::CheckNPCFactionAlly(int faction) { diff --git a/zone/lua_npc.h b/zone/lua_npc.h index e97362a9c..5eec64d3b 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -29,7 +29,7 @@ public: return reinterpret_cast(GetLuaPtrData()); } - void Signal(int id); + void Signal(int signal_id); int CheckNPCFactionAlly(int faction); void AddItem(int item_id, int charges); void AddItem(int item_id, int charges, bool equip); diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index f33483e7c..7a1d0c84f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -171,6 +171,10 @@ LuaParser::LuaParser() { ItemArgumentDispatch[i] = handle_item_null; SpellArgumentDispatch[i] = handle_spell_null; EncounterArgumentDispatch[i] = handle_encounter_null; + +#ifdef BOTS + BotArgumentDispatch[i] = handle_bot_null; +#endif } NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say; @@ -277,6 +281,22 @@ LuaParser::LuaParser() { EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load; 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; } @@ -566,7 +586,7 @@ int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *cl 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 *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { @@ -579,10 +599,10 @@ int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spe 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 *extra_pointers, luabind::adl::object *l_func) { 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"); 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(&spells[spell_id])); + quest_manager.StartQuest(mob, client, nullptr, const_cast(&spells[spell_id])); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -1318,7 +1338,7 @@ int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInsta 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 *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { @@ -1334,7 +1354,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui while(riter != iter->second.end()) { if(riter->event_id == evt) { 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) { ret = i; } @@ -1352,7 +1372,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui while(riter != iter->second.end()) { if(riter->event_id == evt) { 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) ret = i; } @@ -1367,10 +1387,16 @@ QuestEventID LuaParser::ConvertLuaEvent(QuestEventID evt) { case EVENT_NPC_SLAY: return EVENT_SLAY; break; +#ifdef BOTS + case EVENT_SPELL_EFFECT_BOT: +#endif case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_NPC: return EVENT_SPELL_EFFECT_CLIENT; break; +#ifdef BOTS + case EVENT_SPELL_EFFECT_BUFF_TIC_BOT: +#endif case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: return EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT; @@ -1457,4 +1483,186 @@ uint32 LuaParser::GetExperienceForKill(Client *self, Mob *against, bool &ignoreD return retval; } +#ifdef BOTS +int LuaParser::EventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *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 *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 *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(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 *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 diff --git a/zone/lua_parser.h b/zone/lua_parser.h index cd3d06646..f3141f97a 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -36,20 +36,80 @@ class LuaParser : public QuestInterface { public: ~LuaParser(); - virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, - std::vector *extra_pointers); + virtual int EventNPC( + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalNPC( + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventPlayer( + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalPlayer( + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventItem( + QuestEventID evt, + Client *client, + EQ::ItemInstance *item, + Mob *mob, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventSpell( + QuestEventID evt, + Mob* mob, + Client *client, + uint32 spell_id, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventEncounter( + QuestEventID evt, + std::string encounter_name, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + +#ifdef BOTS + virtual int EventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int EventGlobalBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); +#endif virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -60,6 +120,11 @@ public: virtual bool EncounterHasQuestSub(std::string encounter_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 LoadGlobalNPCScript(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 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 std::string GetVar(std::string name); virtual void Init(); @@ -75,14 +145,50 @@ public: virtual void RemoveEncounter(const std::string &name); virtual uint32 GetIdentifier() { return 0xb0712acc; } - virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); - virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *extra_pointers); + virtual int DispatchEventNPC( + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int DispatchEventPlayer( + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int DispatchEventItem( + QuestEventID evt, + Client *client, + EQ::ItemInstance *item, + Mob *mob, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + virtual int DispatchEventSpell( + QuestEventID evt, + Mob* mob, + Client *client, + uint32 spell_id, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + +#ifdef BOTS + virtual int DispatchEventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); +#endif static LuaParser* Instance() { static LuaParser inst; @@ -107,16 +213,68 @@ private: LuaParser(const LuaParser&); LuaParser& operator=(const LuaParser&); - int _EventNPC(std::string package_name, QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); - int _EventPlayer(std::string package_name, QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *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 *extra_pointers, luabind::adl::object *l_func = nullptr); - int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *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 *extra_pointers); + int _EventNPC( + std::string package_name, + QuestEventID evt, + NPC* npc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers, + luabind::adl::object *l_func = nullptr + ); + int _EventPlayer( + std::string package_name, + QuestEventID evt, + Client *client, + std::string data, + uint32 extra_data, + std::vector *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 *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 *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 *extra_pointers + ); + +#ifdef BOTS + int _EventBot( + std::string package_name, + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers, + luabind::adl::object *l_func = nullptr + ); +#endif void LoadScript(std::string filename, std::string package_name); void MapFunctions(lua_State *L); @@ -133,6 +291,10 @@ private: SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID]; +#ifdef BOTS + BotArgumentHandler BotArgumentDispatch[_LargestEventID]; +#endif + }; #endif diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 364cb13a0..9a7ed9c04 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -668,6 +668,26 @@ void handle_player_inspect(QuestInterface* parse, lua_State* L, Client* client, 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* 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* extra_pointers) { + lua_pushinteger(L, std::stoi(data)); + lua_setfield(L, -2, "aa_gained"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { @@ -764,11 +784,11 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I } //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 *extra_pointers) { - if(npc) { - Lua_Mob l_npc(npc); - luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); - l_npc_o.push(L); +void handle_spell_event(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { + if (mob) { + Lua_Mob l_mob(mob); + luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); + l_mob_o.push(L); } else if(client) { Lua_Mob l_client(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"); } -void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { - if(npc) { - Lua_Mob l_npc(npc); - luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); - l_npc_o.push(L); +void handle_translocate_finish(QuestInterface *parse, lua_State* L, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { + if (mob) { + Lua_Mob l_mob(mob); + luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); + l_mob_o.push(L); } else if(client) { Lua_Mob l_client(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"); } -void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *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 *extra_pointers) { } void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { @@ -940,24 +960,216 @@ void handle_player_merchant(QuestInterface* parse, lua_State* L, Client* client, 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* 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"); +#ifdef BOTS +void handle_bot_null( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { } -void handle_player_aa_gain(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { +void handle_bot_cast( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *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 *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 *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 *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_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 *extra_pointers +) { + Lua_Client l_client(reinterpret_cast(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 *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 *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 *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 *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 *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 diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 53010058a..67d50e2b3 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -5,9 +5,13 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector*); typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::ItemInstance*, Mob*, std::string, uint32, std::vector*); -typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, std::string, uint32, std::vector*); +typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector*); +#ifdef BOTS +typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector*); +#endif + //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); @@ -153,11 +157,11 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I std::vector *extra_pointers); //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 *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 *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 *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, std::vector *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 *extra_pointers +); + +void handle_bot_cast( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_combat( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_death( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_popup_response( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_say( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_signal( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_slay( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_target_change( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_timer( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_bot_use_skill( + QuestInterface *parse, + lua_State* L, + Bot* bot, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +#endif + #endif #endif diff --git a/zone/mob.cpp b/zone/mob.cpp index f65f2af18..25629aa30 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4300,6 +4300,8 @@ void Mob::SetTarget(Mob *mob) #ifdef BOTS 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 } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 8b35ba010..af9a97204 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -32,6 +32,10 @@ #include "fastmath.h" #include "../common/data_verification.h" +#ifdef BOTS +#include "bot.h" +#endif + #include #include #include @@ -1923,10 +1927,11 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) if (attacker->GetHP() > 0) { if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) { parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); - uint32 emoteid = GetEmoteID(); - if (emoteid != 0) { + auto emote_id = GetEmoteID(); + if (emote_id) { CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid); } + std::string mob_name = GetCleanName(); combat_record.Start(mob_name); 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 void Mob::AI_Event_NoLongerEngaged() { - if (!IsAIControlled()) + if (!IsAIControlled()) { return; + } + AI_walking_timer->Start(RandomTimer(3000,20000)); time_until_can_move = Timer::GetCurrentTime(); - if (minLastFightingDelayMoving == maxLastFightingDelayMoving) + + if (minLastFightingDelayMoving == maxLastFightingDelayMoving) { time_until_can_move += minLastFightingDelayMoving; - else + } else { time_until_can_move += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving); + } StopNavigation(); ClearRampage(); @@ -1954,16 +1969,21 @@ void Mob::AI_Event_NoLongerEngaged() { SetPrimaryAggro(false); SetAssistAggro(false); if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { - if (entity_list.GetNPCByID(this->GetID())) { - uint32 emoteid = CastToNPC()->GetEmoteID(); + if (entity_list.GetNPCByID(GetID())) { + auto emote_id = CastToNPC()->GetEmoteID(); parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); - if (emoteid != 0) { + if (emote_id) { CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emoteid); } + combat_record.Stop(); CastToNPC()->SetCombatEvent(false); } } +#ifdef BOTS + } else if (IsBot()) { + parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0); +#endif } } diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 2baabf6a8..271e50c43 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -81,6 +81,16 @@ uint32 Perl_Bot_GetBotItemIDBySlot(Bot* self, uint16 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) { return self->GetExpansionBitmask(); @@ -117,9 +127,11 @@ void perl_register_bot() package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask); package.add("GetOwner", &Perl_Bot_GetOwner); package.add("HasBotItem", &Perl_Bot_HasBotItem); + package.add("OwnerMessage", &Perl_Bot_OwnerMessage); package.add("RemoveBotItem", &Perl_Bot_RemoveBotItem); package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask); package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask); + package.add("SignalBot", &Perl_Bot_SignalBot); } #endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 8db8b7075..9428e4372 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -449,6 +449,21 @@ perl::array Perl_EntityList_GetBotListByClientName(EntityList* self, std::string } 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 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("RemoveTrap", &Perl_EntityList_RemoveTrap); package.add("ReplaceWithTarget", &Perl_EntityList_ReplaceWithTarget); +#ifdef BOTS + package.add("SignalAllBotsByOwnerCharacterID", &Perl_EntityList_SignalAllBotsByOwnerCharacterID); +#endif 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); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 827b9ad79..42fdbd554 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -1492,9 +1492,9 @@ perl::array Perl_Mob_GetHateList(Mob* self) 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 diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 0bab655f2..b4203f834 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -43,10 +43,34 @@ public: std::vector *extra_pointers) { return 0; } virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *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 *extra_pointers) { return 0; } virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } + +#ifdef BOTS + virtual int EventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointer + ) { + return 0; + } + + virtual int EventGlobalBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ) { + return 0; + } +#endif virtual bool HasQuestSub(uint32 npcid, 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 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 LoadGlobalNPCScript(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 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, std::vector *extra_pointers) { return 0; } virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *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 *extra_pointers) { return 0; } + +#ifdef BOTS + virtual int DispatchEventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ) { + return 0; + } +#endif virtual void AddVar(std::string name, std::string val) { } virtual std::string GetVar(std::string name) { return std::string(); } diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index a6b3f775c..6099bb4ba 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -36,6 +36,11 @@ QuestParserCollection::QuestParserCollection() { _player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded; + +#ifdef BOTS + _bot_quest_status = QuestUnloaded; + _global_bot_quest_status = QuestUnloaded; +#endif } QuestParserCollection::~QuestParserCollection() { @@ -70,7 +75,7 @@ void QuestParserCollection::Init() { } void QuestParserCollection::ReloadQuests(bool reset_timers) { - if(reset_timers) { + if (reset_timers) { quest_manager.ClearAllTimers(); } @@ -79,11 +84,17 @@ void QuestParserCollection::ReloadQuests(bool reset_timers) { _player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded; + +#ifdef BOTS + _bot_quest_status = QuestUnloaded; + _global_bot_quest_status = QuestUnloaded; +#endif + _spell_quest_status.clear(); _item_quest_status.clear(); _encounter_quest_status.clear(); auto iter = _load_precedence.begin(); - while(iter != _load_precedence.end()) { + while (iter != _load_precedence.end()) { (*iter)->ReloadQuests(); ++iter; } @@ -452,38 +463,46 @@ int QuestParserCollection::EventItem(QuestEventID evt, Client *client, EQ::ItemI return 0; } -int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int QuestParserCollection::EventSpell( + QuestEventID evt, + Mob* mob, + Client *client, + uint32 spell_id, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { 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 - if(iter->second != QuestFailedToLoad) { - auto qiter = _interfaces.find(iter->second); - int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); - int i = qiter->second->EventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); - if(i != 0) { - ret = i; - } + if (iter->second != QuestFailedToLoad) { + auto qi = _interfaces.find(iter->second); + int ret = DispatchEventSpell(evt, mob, 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) { + ret = i; + } + return ret; } - return DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); - } - else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { + + return DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers); + } else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { std::string filename; QuestInterface *qi = GetQIBySpellQuest(spell_id, filename); if (qi) { _spell_quest_status[spell_id] = qi->GetIdentifier(); qi->LoadSpellScript(filename, spell_id); - int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); - int i = qi->EventSpell(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, mob, client, spell_id, data, extra_data, extra_pointers); if (i != 0) { ret = i; } + return ret; - } - else { + } else { _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; @@ -1022,18 +1041,25 @@ int QuestParserCollection::DispatchEventItem(QuestEventID evt, Client *client, E return ret; } -int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - int ret = 0; +int QuestParserCollection::DispatchEventSpell( + QuestEventID evt, + Mob* mob, + Client *client, + uint32 spell_id, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { - int i = (*iter)->DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); - if(i != 0) { - ret = i; - } + int i = (*iter)->DispatchEventSpell(evt, mob, client, spell_id, data, extra_data, extra_pointers); + if(i != 0) { + ret = i; + } ++iter; } - return ret; + return ret; } 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 *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 *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 *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 *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 diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index 8e1fc0768..919dad618 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -72,16 +72,31 @@ public: bool SpellHasQuestSub(uint32 spell_id, 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, std::vector *extra_pointers = nullptr); int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *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 *extra_pointers = nullptr); int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); + +#ifdef BOTS + int EventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers = nullptr + ); +#endif void GetErrors(std::list &quest_errors); @@ -117,11 +132,35 @@ private: bool ItemHasEncounterSub(EQ::ItemInstance* item, QuestEventID evt); 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 *extra_pointers); int EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); int EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); int EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); +#ifdef BOTS + int EventBotLocal( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); + int EventBotGlobal( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); +#endif + QuestInterface *GetQIByNPCQuest(uint32 npcid, std::string &filename); QuestInterface *GetQIByGlobalNPCQuest(std::string &filename); QuestInterface *GetQIByPlayerQuest(std::string &filename); @@ -129,6 +168,11 @@ private: QuestInterface *GetQIBySpellQuest(uint32 spell_id, std::string &filename); QuestInterface *GetQIByItemQuest(std::string item_script, 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, std::vector *extra_pointers); @@ -136,9 +180,20 @@ private: std::vector *extra_pointers); int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *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 *extra_pointers); +#ifdef BOTS + int DispatchEventBot( + QuestEventID evt, + Bot *bot, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers + ); +#endif + std::map _interfaces; std::map _extensions; std::list _load_precedence; @@ -149,6 +204,13 @@ private: uint32 _global_npc_quest_status; uint32 _player_quest_status; uint32 _global_player_quest_status; + +#ifdef BOTS + uint32 _bot_quest_status; + uint32 _global_bot_quest_status; +#endif + + std::map _spell_quest_status; std::map _item_quest_status; std::map _encounter_quest_status; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 605a32d5b..de872a1ba 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -94,10 +94,15 @@ void QuestManager::Process() { parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); } else if (cur->mob->IsEncounter()) { 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 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 //number of timers... worst case we have to check a bunch of timers twice @@ -3395,6 +3400,17 @@ NPC *QuestManager::GetNPC() const { 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 { if(!quests_running_.empty()) { running_quest e = quests_running_.top(); @@ -3653,7 +3669,7 @@ void QuestManager::CrossZoneSetEntityVariable(uint8 update_type, int update_iden 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)); CZSignal_Struct* CZS = (CZSignal_Struct*)pack->pBuffer; CZS->update_type = update_type; @@ -3763,7 +3779,7 @@ void QuestManager::WorldWideSetEntityVariable(uint8 update_type, const char* var 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)); WWSignal_Struct* WWS = (WWSignal_Struct*)pack->pBuffer; WWS->update_type = update_type; diff --git a/zone/questmgr.h b/zone/questmgr.h index 845781546..25da9de8b 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -311,7 +311,7 @@ public: 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 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 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); @@ -320,7 +320,7 @@ public: 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 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 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); @@ -345,6 +345,10 @@ public: std::string GetRecipeName(uint32 recipe_id); bool HasRecipeLearned(uint32 recipe_id); +#ifdef BOTS + Bot *GetBot() const; +#endif + Client *GetInitiator() const; NPC *GetNPC() const; Mob *GetOwner() const; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bbed45323..06a2aedac 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -175,11 +175,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove return true; } } 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(); 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)) { @@ -3782,9 +3789,15 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) return; } } 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; } +#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++) { @@ -4129,9 +4142,15 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) return; } } 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; } +#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++) diff --git a/zone/spells.cpp b/zone/spells.cpp index 3b3c6d090..03b4b7d95 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -245,17 +245,21 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, GetID(), GetCasterLevel(spell_id) ); - if(IsClient()) { + if (IsClient()) { if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0) != 0) { if (IsDiscipline(spell_id)) { CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); } else { CastToClient()->SendSpellBarEnable(spell_id); } - return(false); + return false; } - } else if(IsNPC()) { + } else if (IsNPC()) { 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 @@ -1666,10 +1670,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo GetID(), GetCasterLevel(spell_id) ); - if(IsClient()) { + if (IsClient()) { parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0); - } else if(IsNPC()) { + } else if (IsNPC()) { 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) @@ -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); } else if (spelltar->IsClient()) { 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); if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index c02953b46..db885c178 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2614,7 +2614,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer; uint8 update_type = CZS->update_type; int update_identifier = CZS->update_identifier; - uint32 signal = CZS->signal; + int signal = CZS->signal; const char* client_name = CZS->client_name; if (update_type == CZUpdateType_Character) { auto client = entity_list.GetClientByCharID(update_identifier);