[Tasks] Only update loot tasks for NPC corpses (#2513)

This fixes a bug that allowed looting items from a player's corpse to
increment a task if it didn't have an npc target defined. It looks
like this bug existed before the changes in 7482cfc0.

This also now passes count for task loot updates to handle item stacks.

Also fixes incorrectly casting Corpse to NPC on loot update
This commit is contained in:
hg 2022-11-05 11:13:02 -04:00 committed by GitHub
parent f6dbdf5db8
commit 070bf64d6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 27 deletions

View File

@ -1138,15 +1138,16 @@ public:
); );
} }
} }
inline void UpdateTasksForItem( inline void UpdateTasksForItem(TaskActivityType type, int item_id, int count = 1)
TaskActivityType activity_type,
NPC* npc,
int item_id,
int count = 1
)
{ {
if (task_state) { if (task_state) {
task_state->UpdateTasksForItem(this, activity_type, npc, item_id, count); task_state->UpdateTasksForItem(this, type, item_id, count);
}
}
inline void UpdateTasksOnLoot(Corpse* corpse, int item_id, int count = 1)
{
if (task_state) {
task_state->UpdateTasksOnLoot(this, corpse, item_id, count);
} }
} }
inline void UpdateTasksOnExplore(const glm::vec4& pos) inline void UpdateTasksOnExplore(const glm::vec4& pos)

View File

@ -1370,6 +1370,9 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
} }
} }
// get count for task update before it's mutated by AutoPutLootInInventory
int count = inst->IsStackable() ? inst->GetCharges() : 1;
/* First add it to the looter - this will do the bag contents too */ /* First add it to the looter - this will do the bag contents too */
if (lootitem->auto_loot > 0) { if (lootitem->auto_loot > 0) {
if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data))
@ -1380,8 +1383,9 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
} }
/* Update any tasks that have an activity to loot this item */ /* Update any tasks that have an activity to loot this item */
if (RuleB(TaskSystem, EnableTaskSystem)) if (RuleB(TaskSystem, EnableTaskSystem) && IsNPCCorpse()) {
client->UpdateTasksForItem(TaskActivityType::Loot, IsNPCCorpse() ? CastToNPC() : nullptr, item->ID); client->UpdateTasksOnLoot(this, item->ID, count);
}
/* Remove it from Corpse */ /* Remove it from Corpse */
if (item_data) { if (item_data) {

View File

@ -367,7 +367,7 @@ void Client::GoFish()
PushItemOnCursor(*inst); PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if (RuleB(TaskSystem, EnableTaskSystem)) if (RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(TaskActivityType::Fish, nullptr, food_id); UpdateTasksForItem(TaskActivityType::Fish, food_id);
safe_delete(inst); safe_delete(inst);
inst = m_inv.GetItem(EQ::invslot::slotCursor); inst = m_inv.GetItem(EQ::invslot::slotCursor);
@ -486,7 +486,7 @@ void Client::ForageItem(bool guarantee) {
PushItemOnCursor(*inst); PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if(RuleB(TaskSystem, EnableTaskSystem)) { if(RuleB(TaskSystem, EnableTaskSystem)) {
UpdateTasksForItem(TaskActivityType::Forage, nullptr, foragedfood); UpdateTasksForItem(TaskActivityType::Forage, foragedfood);
} }
safe_delete(inst); safe_delete(inst);

View File

@ -522,10 +522,10 @@ bool ClientTaskState::CanUpdate(Client* client, const TaskUpdateFilter& filter,
} }
// npc filter supports both npc names and ids in match lists // npc filter supports both npc names and ids in match lists
if (!activity.npc_match_list.empty() && (!filter.npc || if (!activity.npc_match_list.empty() && (!filter.mob ||
(!Tasks::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetName()) && (!Tasks::IsInMatchListPartial(activity.npc_match_list, filter.mob->GetName()) &&
!Tasks::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetCleanName()) && !Tasks::IsInMatchListPartial(activity.npc_match_list, filter.mob->GetCleanName()) &&
!Tasks::IsInMatchList(activity.npc_match_list, std::to_string(filter.npc->GetNPCTypeID()))))) !Tasks::IsInMatchList(activity.npc_match_list, std::to_string(filter.mob->GetNPCTypeID())))))
{ {
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc match filter", client->GetName(), task_id, client_activity.activity_id); LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc match filter", client->GetName(), task_id, client_activity.activity_id);
return false; return false;
@ -640,7 +640,7 @@ bool ClientTaskState::UpdateTasksByNPC(Client* client, TaskActivityType type, NP
{ {
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.type = type; filter.type = type;
filter.npc = npc; filter.mob = npc;
return UpdateTasks(client, filter) > 0; return UpdateTasks(client, filter) > 0;
} }
@ -651,7 +651,7 @@ int ClientTaskState::ActiveSpeakTask(Client* client, NPC* npc)
// active task found which has an active SpeakWith activity_information for this NPC. // active task found which has an active SpeakWith activity_information for this NPC.
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.type = TaskActivityType::SpeakWith; filter.type = TaskActivityType::SpeakWith;
filter.npc = npc; filter.mob = npc;
filter.method = METHODQUEST; filter.method = METHODQUEST;
auto result = FindTask(client, filter); auto result = FindTask(client, filter);
@ -670,7 +670,7 @@ int ClientTaskState::ActiveSpeakActivity(Client* client, NPC* npc, int task_id)
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.type = TaskActivityType::SpeakWith; filter.type = TaskActivityType::SpeakWith;
filter.npc = npc; filter.mob = npc;
filter.method = METHODQUEST; filter.method = METHODQUEST;
filter.task_id = task_id; filter.task_id = task_id;
@ -678,19 +678,30 @@ int ClientTaskState::ActiveSpeakActivity(Client* client, NPC* npc, int task_id)
return result.first != 0 ? result.second : -1; // activity id return result.first != 0 ? result.second : -1; // activity id
} }
void ClientTaskState::UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count) void ClientTaskState::UpdateTasksForItem(Client* client, TaskActivityType type, int item_id, int count)
{ {
// This method updates the client's task activities of the specified type which relate // This method updates the client's task activities of the specified type which relate
// to the specified item. // to the specified item.
// //
// Type should be one of ActivityLoot, ActivityTradeSkill, ActivityFish or ActivityForage // Type should be one of ActivityTradeSkill, ActivityFish or ActivityForage
LogTasks("[UpdateTasksForItem] activity_type [{}] item_id [{}]", static_cast<int>(type), item_id); LogTasks("[UpdateTasksForItem] activity_type [{}] item_id [{}] count [{}]", static_cast<int>(type), item_id, count);
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.type = type; filter.type = type;
filter.npc = npc; // looting may filter on npc id or name filter.item_id = item_id;
UpdateTasks(client, filter, count);
}
void ClientTaskState::UpdateTasksOnLoot(Client* client, Corpse* corpse, int item_id, int count)
{
LogTasks("[UpdateTasksOnLoot] corpse [{}] item_id [{}] count [{}]", corpse->GetName(), item_id, count);
TaskUpdateFilter filter{};
filter.type = TaskActivityType::Loot;
filter.mob = corpse;
filter.item_id = item_id; filter.item_id = item_id;
UpdateTasks(client, filter, count); UpdateTasks(client, filter, count);
@ -715,7 +726,7 @@ bool ClientTaskState::UpdateTasksOnDeliver(Client* client, std::vector<EQ::ItemI
bool is_updated = false; bool is_updated = false;
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.npc = npc; filter.mob = npc;
int cash = trade.cp + (trade.sp * 10) + (trade.gp * 100) + (trade.pp * 1000); int cash = trade.cp + (trade.sp * 10) + (trade.gp * 100) + (trade.pp * 1000);
if (cash != 0) if (cash != 0)
@ -772,7 +783,7 @@ void ClientTaskState::UpdateTasksOnKill(Client* client, Client* exp_client, NPC*
{ {
TaskUpdateFilter filter{}; TaskUpdateFilter filter{};
filter.type = TaskActivityType::Kill; filter.type = TaskActivityType::Kill;
filter.npc = npc; filter.mob = npc;
filter.pos = npc->GetPosition(); // or should areas be filtered by client position? filter.pos = npc->GetPosition(); // or should areas be filtered by client position?
filter.use_pos = true; filter.use_pos = true;
filter.exp_client = exp_client; filter.exp_client = exp_client;

View File

@ -24,7 +24,7 @@ struct TaskUpdateFilter
glm::vec4 pos; glm::vec4 pos;
bool use_pos = false; // if true uses pos instead of client position for area filters bool use_pos = false; // if true uses pos instead of client position for area filters
bool ignore_area = false; // if true, area check is disabled bool ignore_area = false; // if true, area check is disabled
NPC* npc = nullptr; Mob* mob = nullptr;
Client* exp_client = nullptr; // used by Kill tasks to filter shared task updates Client* exp_client = nullptr; // used by Kill tasks to filter shared task updates
TaskActivityType type = TaskActivityType::None; TaskActivityType type = TaskActivityType::None;
TaskMethodType method = TaskMethodType::METHODSINGLEID; TaskMethodType method = TaskMethodType::METHODSINGLEID;
@ -56,7 +56,8 @@ public:
void RemoveTask(Client *client, int sequence_number, TaskType task_type); void RemoveTask(Client *client, int sequence_number, TaskType task_type);
void RemoveTaskByTaskID(Client *client, uint32 task_id); void RemoveTaskByTaskID(Client *client, uint32 task_id);
bool UpdateTasksByNPC(Client* client, TaskActivityType type, NPC* npc); bool UpdateTasksByNPC(Client* client, TaskActivityType type, NPC* npc);
void UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count = 1); void UpdateTasksForItem(Client* client, TaskActivityType type, int item_id, int count = 1);
void UpdateTasksOnLoot(Client* client, Corpse* corpse, int item_id, int count = 1);
void UpdateTasksOnExplore(Client* client, const glm::vec4& loc); void UpdateTasksOnExplore(Client* client, const glm::vec4& loc);
bool UpdateTasksOnSpeakWith(Client* client, NPC* npc); bool UpdateTasksOnSpeakWith(Client* client, NPC* npc);
bool UpdateTasksOnDeliver(Client* client, std::vector<EQ::ItemInstance*>& items, Trade& trade, NPC* npc); bool UpdateTasksOnDeliver(Client* client, std::vector<EQ::ItemInstance*>& items, Trade& trade, NPC* npc);

View File

@ -1096,7 +1096,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
} }
if (RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
UpdateTasksForItem(TaskActivityType::TradeSkill, nullptr, itr->first, itr->second); UpdateTasksForItem(TaskActivityType::TradeSkill, itr->first, itr->second);
} }
++itr; ++itr;