Added catches for quest and money loot

This commit is contained in:
Flerp 2025-11-18 16:02:37 -08:00
parent 4886019ca0
commit ff4d63ef39

View File

@ -25,14 +25,20 @@ namespace
uint32 sRequiredEquipId = 0; uint32 sRequiredEquipId = 0;
std::vector<uint32> sBobberEntries; std::vector<uint32> sBobberEntries;
struct GuidHash { std::size_t operator()(ObjectGuid const& g) const noexcept { return std::hash<uint64>()(g.GetRawValue()); } }; struct GuidHash
{
std::size_t operator()(ObjectGuid const &g) const noexcept { return std::hash<uint64>()(g.GetRawValue()); }
};
std::unordered_map<ObjectGuid, uint32, GuidHash> sRecastTimers; std::unordered_map<ObjectGuid, uint32, GuidHash> sRecastTimers;
struct PendingLoot { uint32 timer; }; struct PendingLoot
{
uint32 timer;
};
std::unordered_map<ObjectGuid, PendingLoot, GuidHash> sLootTimers; std::unordered_map<ObjectGuid, PendingLoot, GuidHash> sLootTimers;
std::vector<uint32> ParseEntryList(std::string const& csv) std::vector<uint32> ParseEntryList(std::string const &csv)
{ {
std::vector<uint32> out; std::vector<uint32> out;
std::stringstream ss(csv); std::stringstream ss(csv);
@ -47,21 +53,23 @@ namespace
return out; return out;
} }
bool HasEquippedItem(Player* plr, uint32 entry) bool HasEquippedItem(Player *plr, uint32 entry)
{ {
if (!entry) return true; if (!entry)
return true;
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{ {
if (Item* it = plr->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) if (Item *it = plr->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
if (it->GetEntry() == entry) if (it->GetEntry() == entry)
return true; return true;
} }
return false; return false;
} }
bool RequirementMet(Player* plr) bool RequirementMet(Player *plr)
{ {
if (!plr) return false; if (!plr)
return false;
if (sRequiredItemId && !plr->HasItemCount(sRequiredItemId, 1, false)) if (sRequiredItemId && !plr->HasItemCount(sRequiredItemId, 1, false))
return false; return false;
if (sRequiredEquipId && !HasEquippedItem(plr, sRequiredEquipId)) if (sRequiredEquipId && !HasEquippedItem(plr, sRequiredEquipId))
@ -69,18 +77,18 @@ namespace
return true; return true;
} }
void ScheduleRecast(Player* plr) void ScheduleRecast(Player *plr)
{ {
if (!sAutoRecast || !plr || !plr->IsInWorld()) if (!sAutoRecast || !plr || !plr->IsInWorld())
return; return;
sRecastTimers[plr->GetGUID()] = sRecastDelayMs; sRecastTimers[plr->GetGUID()] = sRecastDelayMs;
} }
void ScheduleAutoLoot(Player* plr) void ScheduleAutoLoot(Player *plr)
{ {
if (!sServerAutoLoot || !plr) if (!sServerAutoLoot || !plr)
return; return;
sLootTimers[plr->GetGUID()] = PendingLoot{ sAutoLootDelayMs }; sLootTimers[plr->GetGUID()] = PendingLoot{sAutoLootDelayMs};
} }
void TickRecast(uint32 diff) void TickRecast(uint32 diff)
@ -94,7 +102,7 @@ namespace
} }
else else
{ {
Player* plr = ObjectAccessor::FindPlayer(it->first); Player *plr = ObjectAccessor::FindPlayer(it->first);
if (plr && plr->IsInWorld() && plr->IsAlive() && !plr->IsInCombat() && RequirementMet(plr)) if (plr && plr->IsInWorld() && plr->IsAlive() && !plr->IsInCombat() && RequirementMet(plr))
plr->CastSpell(plr, sRecastSpell, true); plr->CastSpell(plr, sRecastSpell, true);
it = sRecastTimers.erase(it); it = sRecastTimers.erase(it);
@ -113,105 +121,131 @@ namespace
continue; continue;
} }
Player* plr = ObjectAccessor::FindPlayer(it->first); Player *plr = ObjectAccessor::FindPlayer(it->first);
if (plr && plr->IsInWorld() && RequirementMet(plr)) if (plr && plr->IsInWorld() && RequirementMet(plr))
{ {
ObjectGuid lootGuid = plr->GetLootGUID(); ObjectGuid lootGuid = plr->GetLootGUID();
if (lootGuid.IsGameObject()) if (lootGuid.IsGameObject())
{ {
if (GameObject* lootGo = ObjectAccessor::GetGameObject(*plr, lootGuid)) if (GameObject *lootGo = ObjectAccessor::GetGameObject(*plr, lootGuid))
{ {
Loot* loot = &lootGo->loot; Loot *loot = &lootGo->loot;
for (uint32 i = 0; i < loot->items.size(); ++i)
// 1) Normal items
for (uint32 slot = 0; slot < loot->items.size(); ++slot)
{ {
if (loot->items[i].is_looted) LootItem &li = loot->items[slot];
if (li.is_looted)
continue; continue;
InventoryResult msg = EQUIP_ERR_OK; InventoryResult msg = EQUIP_ERR_OK;
plr->StoreLootItem(uint8(i), loot, msg); plr->StoreLootItem(uint8(slot), loot, msg);
if (!loot->quest_items.empty())
{
uint32 baseSlot = loot->items.size();
for (uint32 q = 0; q < loot->quest_items.size(); ++q)
{
QuestItem &qi = loot->quest_items[q];
if (qi.is_looted)
continue;
uint8 slot = uint8(baseSlot + q);
InventoryResult msg = EQUIP_ERR_OK;
plr->StoreLootItem(slot, loot, msg);
}
}
if (loot->gold > 0)
{
plr->ModifyMoney(loot->gold);
loot->gold = 0;
}
plr->SendLootRelease(lootGuid);
if (lootGo->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
lootGo->SetLootState(GO_JUST_DEACTIVATED);
} }
plr->SendLootRelease(lootGuid);
if (lootGo->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
lootGo->SetLootState(GO_JUST_DEACTIVATED);
} }
} }
it = sLootTimers.erase(it);
}
}
void TryAutoFish(Player * plr)
{
if (!plr || !plr->IsInWorld() || !RequirementMet(plr))
return;
std::list<GameObject *> nearList;
for (auto entry : sBobberEntries)
plr->GetGameObjectListWithEntryInGrid(nearList, entry, sScanRange);
for (GameObject *go : nearList)
{
if (!go || go->GetOwnerGUID() != plr->GetGUID())
continue;
if (go->getLootState() == GO_READY && go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
{
go->Use(plr);
if (sServerAutoLoot)
ScheduleAutoLoot(plr);
ScheduleRecast(plr);
return;
}
}
}
}
class AutoFish_WorldScript : public WorldScript
{
public:
AutoFish_WorldScript() : WorldScript("AutoFish_WorldScript") {}
void OnAfterConfigLoad(bool) override
{
sEnabled = sConfigMgr->GetOption<bool>("AutoFish.Enabled", true);
sServerAutoLoot = sConfigMgr->GetOption<bool>("AutoFish.ServerAutoLoot", true);
sAutoRecast = sConfigMgr->GetOption<bool>("AutoFish.AutoRecast", true);
sTickMs = sConfigMgr->GetOption<uint32>("AutoFish.TickMs", 200u);
sScanRange = sConfigMgr->GetOption<float>("AutoFish.ScanRange", 30.0f);
sRecastDelayMs = sConfigMgr->GetOption<uint32>("AutoFish.RecastDelayMs", 500u);
sRecastSpell = sConfigMgr->GetOption<uint32>("AutoFish.RecastSpell", 18248u);
sAutoLootDelayMs = sConfigMgr->GetOption<uint32>("AutoFish.AutoLootDelayMs", 120u);
sRequiredItemId = sConfigMgr->GetOption<uint32>("AutoFish.RequiredItemId", 0u);
sRequiredEquipId = sConfigMgr->GetOption<uint32>("AutoFish.RequiredEquipId", 0u);
sBobberEntries = ParseEntryList(sConfigMgr->GetOption<std::string>("AutoFish.BobberEntries", "35591"));
}
void OnUpdate(uint32 diff) override
{
if (!sEnabled)
return;
static uint32 acc = 0;
acc += diff;
if (acc >= sTickMs)
{
acc = 0;
auto const &players = ObjectAccessor::GetPlayers();
for (auto const &kv : players)
{
Player *plr = kv.second;
if (!plr || !plr->IsInWorld() || plr->IsGameMaster())
continue;
TryAutoFish(plr);
}
} }
it = sLootTimers.erase(it); TickAutoLoot(diff);
TickRecast(diff);
} }
} };
void TryAutoFish(Player* plr) void AddSC_autofish_world()
{ {
if (!plr || !plr->IsInWorld() || !RequirementMet(plr)) new AutoFish_WorldScript();
return;
std::list<GameObject*> nearList;
for (auto entry : sBobberEntries)
plr->GetGameObjectListWithEntryInGrid(nearList, entry, sScanRange);
for (GameObject* go : nearList)
{
if (!go || go->GetOwnerGUID() != plr->GetGUID())
continue;
if (go->getLootState() == GO_READY && go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
{
go->Use(plr);
if (sServerAutoLoot)
ScheduleAutoLoot(plr);
ScheduleRecast(plr);
return;
}
}
} }
}
class AutoFish_WorldScript : public WorldScript
{
public:
AutoFish_WorldScript() : WorldScript("AutoFish_WorldScript") {}
void OnAfterConfigLoad(bool) override
{
sEnabled = sConfigMgr->GetOption<bool>("AutoFish.Enabled", true);
sServerAutoLoot = sConfigMgr->GetOption<bool>("AutoFish.ServerAutoLoot", true);
sAutoRecast = sConfigMgr->GetOption<bool>("AutoFish.AutoRecast", true);
sTickMs = sConfigMgr->GetOption<uint32>("AutoFish.TickMs", 200u);
sScanRange = sConfigMgr->GetOption<float>("AutoFish.ScanRange", 30.0f);
sRecastDelayMs = sConfigMgr->GetOption<uint32>("AutoFish.RecastDelayMs", 500u);
sRecastSpell = sConfigMgr->GetOption<uint32>("AutoFish.RecastSpell", 18248u);
sAutoLootDelayMs = sConfigMgr->GetOption<uint32>("AutoFish.AutoLootDelayMs", 120u);
sRequiredItemId = sConfigMgr->GetOption<uint32>("AutoFish.RequiredItemId", 0u);
sRequiredEquipId = sConfigMgr->GetOption<uint32>("AutoFish.RequiredEquipId", 0u);
sBobberEntries = ParseEntryList(sConfigMgr->GetOption<std::string>("AutoFish.BobberEntries", "35591"));
}
void OnUpdate(uint32 diff) override
{
if (!sEnabled)
return;
static uint32 acc = 0;
acc += diff;
if (acc >= sTickMs)
{
acc = 0;
auto const& players = ObjectAccessor::GetPlayers();
for (auto const& kv : players)
{
Player* plr = kv.second;
if (!plr || !plr->IsInWorld() || plr->IsGameMaster())
continue;
TryAutoFish(plr);
}
}
TickAutoLoot(diff);
TickRecast(diff);
}
};
void AddSC_autofish_world()
{
new AutoFish_WorldScript();
}