diff --git a/zone/client.cpp b/zone/client.cpp index bc42c3db0..5b88e3e43 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -11753,3 +11753,21 @@ void Client::Undye() database.DeleteCharacterDye(CharacterID()); } + +void Client::SetTrackingID(uint32 entity_id) +{ + if (!entity_id) { + TrackingID = 0; + return; + } + + auto *m = entity_list.GetMob(entity_id); + if (!m) { + TrackingID = 0; + return; + } + + TrackingID = entity_id; + + MessageString(Chat::Skills, TRACKING_BEGIN, m->GetCleanName()); +} diff --git a/zone/client.h b/zone/client.h index 180e30464..b73ae9a02 100644 --- a/zone/client.h +++ b/zone/client.h @@ -409,6 +409,8 @@ public: inline void SetBaseRace(uint32 i) { m_pp.race=i; } inline void SetBaseGender(uint32 i) { m_pp.gender=i; } inline void SetDeity(uint32 i) {m_pp.deity=i;deity=i;} + + void SetTrackingID(uint32 entity_id); inline uint8 GetLevel2() const { return m_pp.level2; } inline uint16 GetBaseRace() const { return m_pp.race; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 85ccd9b81..87bcd8da8 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14165,16 +14165,14 @@ void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) return; } - if (app->size != sizeof(TrackTarget_Struct)) - { - LogError("Invalid size for OP_TrackTarget: Expected: [{}], Got: [{}]", - sizeof(TrackTarget_Struct), app->size); + if (app->size != sizeof(TrackTarget_Struct)) { + LogError("Invalid size for OP_TrackTarget: Expected: [{}], Got: [{}]", sizeof(TrackTarget_Struct), app->size); return; } - TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; + auto *t = (TrackTarget_Struct*) app->pBuffer; - TrackingID = tts->EntityID; + SetTrackingID(t->EntityID); } void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e0aac03e4..6dedfc0ca 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1946,10 +1946,11 @@ void Client::CalcRestState() void Client::DoTracking() { - if (TrackingID == 0) + if (!TrackingID) { return; + } - Mob *m = entity_list.GetMob(TrackingID); + auto *m = entity_list.GetMob(TrackingID); if (!m || m->IsCorpse()) { MessageString(Chat::Skills, TRACK_LOST_TARGET); @@ -1957,29 +1958,43 @@ void Client::DoTracking() return; } - float RelativeHeading = GetHeading() - CalculateHeadingToTarget(m->GetX(), m->GetY()); + if (DistanceNoZ(m->GetPosition(), GetPosition()) < 10) { + Message( + Chat::Skills, + fmt::format( + "You have found {}.", + m->GetCleanName() + ).c_str() + ); + TrackingID = 0; + return; + } - if (RelativeHeading < 0) - RelativeHeading += 512; + float relative_heading = GetHeading() - CalculateHeadingToTarget(m->GetX(), m->GetY()); - if (RelativeHeading > 480) + if (relative_heading < 0) { + relative_heading += 512; + } + + if (relative_heading > 480) { MessageString(Chat::Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName()); - else if (RelativeHeading > 416) + } else if (relative_heading > 416) { MessageString(Chat::Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "left"); - else if (RelativeHeading > 352) + } else if (relative_heading > 352) { MessageString(Chat::Skills, TRACK_TO_THE, m->GetCleanName(), "left"); - else if (RelativeHeading > 288) + } else if (relative_heading > 288) { MessageString(Chat::Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "left"); - else if (RelativeHeading > 224) + } else if (relative_heading > 224) { MessageString(Chat::Skills, TRACK_BEHIND_YOU, m->GetCleanName()); - else if (RelativeHeading > 160) + } else if (relative_heading > 160) { MessageString(Chat::Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "right"); - else if (RelativeHeading > 96) + } else if (relative_heading > 96) { MessageString(Chat::Skills, TRACK_TO_THE, m->GetCleanName(), "right"); - else if (RelativeHeading > 32) + } else if (relative_heading > 32) { MessageString(Chat::Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "right"); - else if (RelativeHeading >= 0) + } else if (relative_heading >= 0) { MessageString(Chat::Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName()); + } } void Client::HandleRespawnFromHover(uint32 Option) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index e4f2ee15f..9ad63ae53 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8454,6 +8454,18 @@ XS(XS__discordsend) { XSRETURN_EMPTY; } +XS(XS__tracknpc); +XS(XS__tracknpc) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::tracknpc(uint32 entity_id)"); + { + uint32 entity_id = (uint32) SvUV(ST(0)); + quest_manager.TrackNPC(entity_id); + } + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8893,6 +8905,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "tasktimeleft"), XS__tasktimeleft, file); newXS(strcpy(buf, "toggle_spawn_event"), XS__toggle_spawn_event, file); newXS(strcpy(buf, "toggledoorstate"), XS__toggledoorstate, file); + newXS(strcpy(buf, "tracknpc"), XS__tracknpc, file); newXS(strcpy(buf, "traindisc"), XS__traindisc, file); newXS(strcpy(buf, "traindiscs"), XS__traindiscs, file); newXS(strcpy(buf, "unique_spawn"), XS__unique_spawn, file); diff --git a/zone/entity.cpp b/zone/entity.cpp index 918b1ec2a..16b3e6dc8 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3668,46 +3668,53 @@ void EntityList::SignalMobsByNPCID(uint32 snpc, int signal_id) bool EntityList::MakeTrackPacket(Client *client) { std::list > tracking_list; - uint32 distance = 0; - float MobDistance; + auto distance = static_cast(client->GetSkill(EQ::skills::SkillTracking) * client->GetClassTrackingDistanceMultiplier(client->GetClass())); - distance = (client->GetSkill(EQ::skills::SkillTracking) * client->GetClassTrackingDistanceMultiplier(client->GetClass())); - - if (distance <= 0) + if (distance <= 0.0f) { return false; - if (distance < 300) - distance = 300; + } + + if (distance < 300.0f) { + distance = 300.0f; + } for (auto it = mob_list.cbegin(); it != mob_list.cend(); ++it) { - if (!it->second || it->second == client || !it->second->IsTrackable() || - it->second->IsInvisible(client)) + if ( + !it->second || + it->second == client || + !it->second->IsTrackable() || + it->second->IsInvisible(client) + ) { continue; + } - MobDistance = DistanceNoZ(it->second->GetPosition(), client->GetPosition()); - if (MobDistance > distance) + const auto mob_distance = DistanceNoZ(it->second->GetPosition(), client->GetPosition()); + if (mob_distance > distance) { continue; + } - tracking_list.push_back(std::make_pair(it->second, MobDistance)); + tracking_list.push_back(std::make_pair(it->second, mob_distance)); } tracking_list.sort( [](const std::pair &a, const std::pair &b) { return a.first->GetSpawnTimeStamp() > b.first->GetSpawnTimeStamp(); - }); + } + ); + auto outapp = new EQApplicationPacket(OP_Track, sizeof(Track_Struct) * tracking_list.size()); - Tracking_Struct *outtrack = (Tracking_Struct *)outapp->pBuffer; + auto pack = (Tracking_Struct *) outapp->pBuffer; outapp->priority = 6; int index = 0; for (auto it = tracking_list.cbegin(); it != tracking_list.cend(); ++it, ++index) { - Mob *cur_entity = it->first; - outtrack->Entrys[index].entityid = (uint32)cur_entity->GetID(); - outtrack->Entrys[index].distance = it->second; - outtrack->Entrys[index].level = cur_entity->GetLevel(); - outtrack->Entrys[index].is_npc = !cur_entity->IsClient(); - strn0cpy(outtrack->Entrys[index].name, cur_entity->GetName(), sizeof(outtrack->Entrys[index].name)); - outtrack->Entrys[index].is_pet = cur_entity->IsPet(); - outtrack->Entrys[index].is_merc = cur_entity->IsMerc(); + pack->Entrys[index].entityid = static_cast(it->first->GetID()); + pack->Entrys[index].distance = it->second; + pack->Entrys[index].level = it->first->GetLevel(); + pack->Entrys[index].is_npc = !it->first->IsClient(); + strn0cpy(pack->Entrys[index].name, it->first->GetName(), sizeof(pack->Entrys[index].name)); + pack->Entrys[index].is_pet = it->first->IsPet(); + pack->Entrys[index].is_merc = it->first->IsMerc(); } client->QueuePacket(outapp); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a13ed4c77..21c8306d8 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3392,16 +3392,18 @@ std::string lua_commify(std::string number) { return commify(number); } -bool lua_check_name_filter(std::string name) -{ +bool lua_check_name_filter(std::string name) { return database.CheckNameFilter(name); } -void lua_discord_send(std::string webhook_name, std::string message) -{ +void lua_discord_send(std::string webhook_name, std::string message) { zone->SendDiscordMessage(webhook_name, message); } +void lua_track_npc(uint32 entity_id) { + quest_manager.TrackNPC(entity_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3857,6 +3859,7 @@ luabind::scope lua_register_general() { luabind::def("commify", &lua_commify), luabind::def("check_name_filter", &lua_check_name_filter), luabind::def("discord_send", &lua_discord_send), + luabind::def("track_npc", &lua_track_npc), /* Cross Zone diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 53e23533b..7ce6aec2d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3688,3 +3688,12 @@ std::string QuestManager::getenvironmentaldamagename(uint8 damage_type) { std::string environmental_damage_name = EQ::constants::GetEnvironmentalDamageName(damage_type); return environmental_damage_name; } + +void QuestManager::TrackNPC(uint32 entity_id) { + QuestManagerCurrentQuestVars(); + if (!initiator) { + return; + } + + initiator->SetTrackingID(entity_id); +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 1e8ab19a7..0183a4098 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -336,6 +336,7 @@ public: int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0); const SPDat_Spell_Struct *getspell(uint32 spell_id); std::string getenvironmentaldamagename(uint8 damage_type); + void TrackNPC(uint32 entity_id); Client *GetInitiator() const; NPC *GetNPC() const; diff --git a/zone/string_ids.h b/zone/string_ids.h index 24fd2d22e..7c94bd8b6 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -454,6 +454,7 @@ #define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat. #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. +#define TRACKING_BEGIN 12040 //You begin tracking %1. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. #define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split. #define ATTACKFAILED 12158 //%1 try to %2 %3, but %4!