diff --git a/conf/mod_autofish.conf.dist b/conf/mod_autofish.conf.dist new file mode 100644 index 0000000..7d3fcce --- /dev/null +++ b/conf/mod_autofish.conf.dist @@ -0,0 +1,8 @@ +# AutoFish module +AutoFish.Enabled = 1 +# How often to scan (ms). 150–250ms feels instant without being spammy. +AutoFish.TickMs = 200 +# Max distance to look for your own bobber +AutoFish.ScanRange = 30.0 +# Bobber entries to watch (comma-separated). Default covers classic/WotLK. +AutoFish.BobberEntries = 35591 diff --git a/src/loader.cpp b/src/loader.cpp new file mode 100644 index 0000000..4e7f78f --- /dev/null +++ b/src/loader.cpp @@ -0,0 +1,6 @@ +void AddSC_autofish_world(); + +void Addmod_autofishScripts() +{ + AddSC_autofish_world(); +} diff --git a/src/mod_autofish.cpp b/src/mod_autofish.cpp new file mode 100644 index 0000000..9dad9a2 --- /dev/null +++ b/src/mod_autofish.cpp @@ -0,0 +1,98 @@ +#include "ScriptMgr.h" +#include "Player.h" +#include "GameObject.h" +#include "ObjectAccessor.h" +#include "GridNotifiers.h" +#include "CellImpl.h" +#include "Config.h" + +namespace +{ + std::vector sBobberEntries; + bool sEnabled = true; + uint32 sTickMs = 200; + float sScanRange = 30.0f; + + std::vector ParseEntryList(std::string const& csv) + { + std::vector out; + std::stringstream ss(csv); + std::string item; + while (std::getline(ss, item, ',')) + { + auto trim = [](std::string& s) + { + s.erase(0, s.find_first_not_of(" \t\r\n")); + s.erase(s.find_last_not_of(" \t\r\n") + 1); + }; + trim(item); + if (!item.empty()) + out.push_back(static_cast(std::stoul(item))); + } + return out; + } + + void TryAutoLootNearbyBobber(Player* plr) + { + if (!plr || !plr->IsInWorld()) + return; + + std::list goList; + Acore::AllGameObjectsMatchingOneEntryInRange check(plr, sBobberEntries, sScanRange); + Acore::GameObjectListSearcher searcher(plr, goList, check); + Cell::VisitGridObjects(plr, searcher, sScanRange); + + for (GameObject* go : goList) + { + if (!go) + continue; + if (go->GetOwnerGUID() != plr->GetGUID()) + continue; + if (go->getLootState() == GO_READY) + { + plr->SendLoot(go->GetGUID(), LOOT_FISHING); + return; + } + } + } +} + +class AutoFish_WorldUpdate : public WorldScript +{ +public: + AutoFish_WorldUpdate() : WorldScript("AutoFish_WorldUpdate") {} + + void OnAfterConfigLoad(bool) override + { + sEnabled = sConfigMgr->GetOption("AutoFish.Enabled", true); + sTickMs = sConfigMgr->GetOption("AutoFish.TickMs", 200u); + sScanRange = sConfigMgr->GetOption("AutoFish.ScanRange", 30.0f); + sBobberEntries = ParseEntryList(sConfigMgr->GetOption("AutoFish.BobberEntries", "35591")); + } + + void OnUpdate(uint32 diff) override + { + if (!sEnabled) + return; + + static uint32 acc = 0; + acc += diff; + if (acc < sTickMs) + return; + acc = 0; + + auto const& players = ObjectAccessor::GetPlayers(); + for (auto const& kv : players) + { + Player* plr = kv.second; + if (!plr || !plr->IsInWorld() || plr->IsGameMaster()) + continue; + TryAutoLootNearbyBobber(plr); + } + } +}; + +void AddSC_autofish_world() +{ + new AutoFish_WorldUpdate(); +}