diff --git a/zone/command.cpp b/zone/command.cpp index 35b920e53..1df75a9ca 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -225,6 +225,7 @@ int command_init(void) command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", AccountStatus::GMLeadAdmin, command_suspend) || command_add("suspendmulti", "[Character Name One|Character Name Two|etc] [Days] [Reason] - Suspend multiple characters by name for specified number of days", AccountStatus::GMLeadAdmin, command_suspendmulti) || command_add("takeplatinum", "[Platinum] - Takes specified amount of platinum from you or your player target", AccountStatus::GMMgmt, command_takeplatinum) || + command_add("test", "Test Command", AccountStatus::GMLeadAdmin, command_test) || command_add("task", "(subcommand) - Task system commands", AccountStatus::GMLeadAdmin, command_task) || command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_petname) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || @@ -920,6 +921,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/suspend.cpp" #include "gm_commands/suspendmulti.cpp" #include "gm_commands/takeplatinum.cpp" +#include "gm_commands/test.cpp" #include "gm_commands/task.cpp" #include "gm_commands/traindisc.cpp" #include "gm_commands/tune.cpp" diff --git a/zone/command.h b/zone/command.h index 09164e6bf..0b78b555e 100644 --- a/zone/command.h +++ b/zone/command.h @@ -179,6 +179,7 @@ void command_summonitem(Client *c, const Seperator *sep); void command_suspend(Client *c, const Seperator *sep); void command_suspendmulti(Client *c, const Seperator *sep); void command_takeplatinum(Client* c, const Seperator* sep); +void command_test(Client *c, const Seperator *sep); void command_task(Client *c, const Seperator *sep); void command_petname(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); diff --git a/zone/entity.cpp b/zone/entity.cpp index fab1be99f..2d1cdeaba 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2941,7 +2941,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) return; } - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + float scan_range = zone->GetMaxUpdateRange(); // Reserve memory in m_close_mobs to avoid frequent re-allocations if not already reserved. // Assuming mob_list.size() as an upper bound for reservation. @@ -2957,7 +2957,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) continue; } - float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + float distance = Distance(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { // add mob to scanning_mob's close list and vice versa // check if the mob is already in the close mobs list before inserting @@ -2968,6 +2968,8 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) } } + UpdateVisibility(scanning_mob); + LogAIScanClose( "[{}] Scanning close list > list_size [{}] moving [{}]", scanning_mob->GetCleanName(), @@ -2976,6 +2978,97 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) ); } +void EntityList::UpdateVisibility(Mob *scanning_mob) +{ + if (!scanning_mob || !scanning_mob->IsClient()) { + return; + } + + // Ensure sufficient capacity in the visibility map + if (scanning_mob->m_can_see_mob.bucket_count() < scanning_mob->m_close_mobs.size()) { + scanning_mob->m_can_see_mob.reserve(scanning_mob->m_close_mobs.size()); + } + + // Update visibility for all mobs in the close list of `scanning_mob` + for (auto &e : scanning_mob->m_close_mobs) { + auto mob = e.second; + if (!mob) continue; + + int mob_id = mob->GetID(); + + // Update visibility for `scanning_mob` to `mob` + auto [it_scanning_visible, inserted_scanning_visible] = scanning_mob->m_can_see_mob.try_emplace(mob_id, false); + bool scanning_last_known_visible = it_scanning_visible->second; + + if (!scanning_last_known_visible) { + scanning_mob->SendAppearancePacket( + AppearanceType::Invisibility, + 0, + false, + true, + mob->CastToClient() + ); + it_scanning_visible->second = true; + scanning_mob->Shout(fmt::format("Setting scanning_mob visible to mob [{}]", mob->GetCleanName()).c_str()); + } + + // Now update the visibility for `mob` to `scanning_mob` + auto [it_mob_visible, inserted_mob_visible] = mob->m_can_see_mob.try_emplace(scanning_mob->GetID(), false); + bool mob_last_known_visible = it_mob_visible->second; + + if (!mob_last_known_visible) { + mob->SendAppearancePacket( + AppearanceType::Invisibility, + 0, + false, + true, + scanning_mob->CastToClient() + ); + it_mob_visible->second = true; + mob->Shout(fmt::format("Setting mob visible to scanning_mob [{}]", scanning_mob->GetCleanName()).c_str()); + } + } + + // Check all other mobs to make invisible if they are no longer close + for (auto &e : mob_list) { + auto mob = e.second; + if (!mob) continue; + + int mob_id = mob->GetID(); + bool is_on_close_list = mob->m_close_mobs.find(scanning_mob->GetID()) != mob->m_close_mobs.end(); + + // Update scanning_mob's view of mob visibility + auto [it, inserted] = scanning_mob->m_can_see_mob.try_emplace(mob_id, false); + bool last_known_visible = it->second; + + if (!is_on_close_list && last_known_visible) { + scanning_mob->SendAppearancePacket( + AppearanceType::Invisibility, + 3001, + false, + true, + mob->CastToClient() + ); + it->second = false; + scanning_mob->Shout(fmt::format("Setting mob [{}] invisible to scanning_mob", mob->GetCleanName()).c_str()); + } + + // Inverse: Update mob's view of scanning_mob visibility + auto it_mob = mob->m_can_see_mob.find(scanning_mob->GetID()); + if (it_mob != mob->m_can_see_mob.end() && !scanning_mob->m_close_mobs.count(mob_id) && it_mob->second) { + mob->SendAppearancePacket( + AppearanceType::Invisibility, + 3001, + false, + true, + scanning_mob->CastToClient() + ); + it_mob->second = false; + mob->Shout(fmt::format("Setting scanning_mob [{}] invisible to mob", scanning_mob->GetCleanName()).c_str()); + } + } +} + bool EntityList::RemoveMerc(uint16 delete_id) { auto it = merc_list.find(delete_id); diff --git a/zone/entity.h b/zone/entity.h index 33313f14e..f52ae8c3c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -570,6 +570,7 @@ public: void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); void ScanCloseMobs(Mob *scanning_mob); + void UpdateVisibility(Mob *scanning_mob); void GetTrapInfo(Client* c); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/gm_commands/test.cpp b/zone/gm_commands/test.cpp new file mode 100755 index 000000000..c8ebc5e8f --- /dev/null +++ b/zone/gm_commands/test.cpp @@ -0,0 +1,14 @@ + +void command_test(Client *c, const Seperator *sep) +{ + const int arguments = sep->argnum; + + for (auto &e : entity_list.GetMobList()) { + auto mob = e.second; + if (Distance(c->GetPosition(), mob->GetPosition()) > 100) { + mob->SendAppearancePacket(AppearanceType::Invisibility, 3001); + } else { + mob->SendAppearancePacket(AppearanceType::Invisibility, 0); + } + } +} diff --git a/zone/mob.h b/zone/mob.h index 297aa80cb..1bdcac89b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -202,6 +202,7 @@ public: void DisplayInfo(Mob *mob); std::unordered_map m_close_mobs; + std::unordered_map m_can_see_mob; Timer m_scan_close_mobs_timer; Timer m_mob_check_moving_timer; diff --git a/zone/zone.cpp b/zone/zone.cpp index f02be2e2b..cc78a907f 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2780,7 +2780,7 @@ void Zone::CalculateNpcUpdateDistanceSpread() int x_spread = int(std::abs(max_x - min_x)); int y_spread = int(std::abs(max_y - min_y)); int combined_spread = int(std::abs((x_spread + y_spread) / 2)); - int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange())); + int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxUpdateRange())); SetNpcPositionUpdateDistance(update_distance); diff --git a/zone/zone.h b/zone/zone.h index c29a89b29..a86987f1f 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -417,7 +417,7 @@ public: SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message)); }; - double GetMaxMovementUpdateRange() const { return max_movement_update_range; } + double GetMaxUpdateRange() const { return max_movement_update_range; } void SetIsHotzone(bool is_hotzone);