diff --git a/zone/client.h b/zone/client.h index 333202ad7..aee1a70a4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1158,13 +1158,9 @@ public: { return task_state && task_state->UpdateTasksOnSpeakWith(this, npc); } - inline bool UpdateTasksOnDeliver( - std::list &items, - int cash, - NPC* npc - ) + inline bool UpdateTasksOnDeliver(std::vector& items, Trade& trade, NPC* npc) { - return task_state && task_state->UpdateTasksOnDeliver(this, items, cash, npc); + return task_state && task_state->UpdateTasksOnDeliver(this, items, trade, npc); } void UpdateTasksOnTouchSwitch(int dz_switch_id) { diff --git a/zone/string_ids.h b/zone/string_ids.h index 78f8f6991..06648c217 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -210,6 +210,7 @@ #define EATING_MESSAGE 1091 //Chomp, chomp, chomp... %1 takes a bite from a %2. #define DRINKING_MESSAGE 1093 //Glug, glug, glug... %1 takes a drink from a %2. #define SUCCESSFUL_TAUNT 1095 //I'll teach you to interfere with me %3. +#define TRADE_BACK 1105 //I have no need for this %3, you can have it back. #define PET_SIT_STRING 1130 //Changing position, Master. #define PET_CALMING 1131 //Sorry, Master..calming down. #define PET_FOLLOWING 1132 //Following you, Master. diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index bc130f97b..2d66456f8 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -558,14 +558,14 @@ bool ClientTaskState::CanUpdate(Client* client, const TaskUpdateFilter& filter, return true; } -bool ClientTaskState::UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count) +int ClientTaskState::UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count) { if (!task_manager) { - return false; + return 0; } - bool any_updated = false; + int max_updated = 0; for (const auto& client_task : m_active_tasks) { @@ -591,14 +591,14 @@ bool ClientTaskState::UpdateTasks(Client* client, const TaskUpdateFilter& filter LogTasks("[UpdateTasks] client [{}] task [{}] activity [{}] increment [{}]", client->GetName(), client_task.task_id, client_activity.activity_id, count); - IncrementDoneCount(client, task, client_task.slot, client_activity.activity_id, count); - any_updated = true; + int updated = IncrementDoneCount(client, task, client_task.slot, client_activity.activity_id, count); + max_updated = std::max(max_updated, updated); break; // only one element updated per task, move to next task } } } - return any_updated; + return max_updated; } std::pair ClientTaskState::FindTask(Client* client, const TaskUpdateFilter& filter) const @@ -653,7 +653,7 @@ bool ClientTaskState::UpdateTasksByNPC(Client* client, TaskActivityType type, NP filter.type = type; filter.npc = npc; - return UpdateTasks(client, filter); + return UpdateTasks(client, filter) > 0; } int ClientTaskState::ActiveSpeakTask(Client* client, NPC* npc) @@ -719,12 +719,7 @@ void ClientTaskState::UpdateTasksOnExplore(Client* client, const glm::vec4& pos) UpdateTasks(client, filter); } -bool ClientTaskState::UpdateTasksOnDeliver( - Client *client, - std::list &items, - int cash, - NPC* npc -) +bool ClientTaskState::UpdateTasksOnDeliver(Client* client, std::vector& items, Trade& trade, NPC* npc) { LogTasks("[UpdateTasksOnDeliver] npc [{}]", npc->GetName()); @@ -733,10 +728,12 @@ bool ClientTaskState::UpdateTasksOnDeliver( TaskUpdateFilter filter{}; filter.npc = npc; + int cash = trade.cp + (trade.sp * 10) + (trade.gp * 100) + (trade.pp * 1000); if (cash != 0) { filter.type = TaskActivityType::GiveCash; - if (UpdateTasks(client, filter, cash)) + int updated_count = UpdateTasks(client, filter, cash); + if (updated_count > 0) { // todo: remove used coin and use Deliver with explicit coin fields instead of custom type is_updated = true; @@ -744,14 +741,26 @@ bool ClientTaskState::UpdateTasksOnDeliver( } filter.type = TaskActivityType::Deliver; - for (EQ::ItemInstance* item : items) + for (EQ::ItemInstance*& item : items) { + // items may have gaps for unused trade slots + if (!item) + { + continue; + } + filter.item_id = item->GetID(); int count = item->IsStackable() ? item->GetCharges() : 1; - if (UpdateTasks(client, filter, count)) + int updated_count = UpdateTasks(client, filter, count); + if (updated_count > 0) { - // todo: remove items used in update (highest in case multiple tasks consume same item) + // remove items used in updates + item->SetCharges(count - updated_count); + if (count == updated_count) + { + item = nullptr; // all items in trade slot consumed + } is_updated = true; } } @@ -782,7 +791,7 @@ void ClientTaskState::UpdateTasksOnKill(Client* client, Client* exp_client, NPC* UpdateTasks(client, filter); } -void ClientTaskState::IncrementDoneCount( +int ClientTaskState::IncrementDoneCount( Client *client, const TaskInformation* task_information, int task_index, @@ -793,7 +802,7 @@ void ClientTaskState::IncrementDoneCount( { auto info = GetClientTaskInfo(task_information->type, task_index); if (info == nullptr) { - return; + return 0; } LogTasks( @@ -804,6 +813,9 @@ void ClientTaskState::IncrementDoneCount( count ); + int remaining = task_information->activity_information[activity_id].goal_count - info->activity[activity_id].done_count; + count = std::min(count, remaining); + // shared task shim // intercept and pass to world first before processing normally if (!client->m_shared_task_update && task_information->type == TaskType::Shared) { @@ -840,15 +852,11 @@ void ClientTaskState::IncrementDoneCount( worldserver.SendPacket(pack); safe_delete(pack); - return; + return count; } info->activity[activity_id].done_count += count; - if (info->activity[activity_id].done_count > task_information->activity_information[activity_id].goal_count) { - info->activity[activity_id].done_count = task_information->activity_information[activity_id].goal_count; - } - if (!ignore_quest_update) { std::string export_string = fmt::format( "{} {} {}", @@ -952,6 +960,8 @@ void ClientTaskState::IncrementDoneCount( } task_manager->SaveClientState(client, this); + + return count; } void ClientTaskState::DispatchEventTaskComplete(Client* client, ClientTaskInformation& info, int activity_id) diff --git a/zone/task_client_state.h b/zone/task_client_state.h index 455d473f8..7509de366 100644 --- a/zone/task_client_state.h +++ b/zone/task_client_state.h @@ -59,7 +59,7 @@ public: void UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count = 1); void UpdateTasksOnExplore(Client* client, const glm::vec4& loc); bool UpdateTasksOnSpeakWith(Client* client, NPC* npc); - bool UpdateTasksOnDeliver(Client* client, std::list& items, int cash, NPC* npc); + bool UpdateTasksOnDeliver(Client* client, std::vector& items, Trade& trade, NPC* npc); void UpdateTasksOnTouch(Client *client, int dz_switch_id); void ProcessTaskProximities(Client *client, float x, float y, float z); bool TaskOutOfTime(TaskType task_type, int index); @@ -110,9 +110,9 @@ private: std::pair FindTask(Client* client, const TaskUpdateFilter& filter) const; void RecordCompletedTask(uint32_t character_id, const TaskInformation& task, const ClientTaskInformation& client_task); void UpdateTasksOnKill(Client* client, Client* exp_client, NPC* npc); - bool UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count = 1); + int UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count = 1); - void IncrementDoneCount( + int IncrementDoneCount( Client *client, const TaskInformation* task_information, int task_index, diff --git a/zone/trading.cpp b/zone/trading.cpp index 54cca9786..c051aa6c9 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -880,20 +880,36 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st quest_npc = true; } - std::vector item_list; - std::list items; + // take ownership of all trade slot items + EQ::ItemInstance* insts[4] = { 0 }; for (int i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_NPC_END; ++i) { - EQ::ItemInstance *inst = m_inv.GetItem(i); - if(inst) { - items.push_back(inst); - item_list.push_back(inst); - } else { - item_list.push_back((EQ::ItemInstance*)nullptr); - continue; - } + insts[i - EQ::invslot::TRADE_BEGIN] = m_inv.PopItem(i); + database.SaveInventory(CharacterID(), nullptr, i); + } + + // copy to be filtered by task updates, null trade slots preserved for quest event arg + std::vector items(insts, insts + std::size(insts)); + + if (RuleB(TaskSystem, EnableTaskSystem)) { + if (UpdateTasksOnDeliver(items, *trade, tradingWith->CastToNPC())) { + if (!tradingWith->IsMoving()) + tradingWith->FaceTarget(this); + + EVENT_ITEM_ScriptStopReturn(); + + } + } + + // todo: rule or npc field to auto return normal items also + if (!quest_npc) + { + for (const EQ::ItemInstance* inst : items) { + if (!inst || !inst->GetItem()) { + continue; + } + + const EQ::ItemData* item = inst->GetItem(); - const EQ::ItemData* item = inst->GetItem(); - if(item && quest_npc == false) { bool isPetAndCanHaveNoDrop = (RuleB(Pets, CanTakeNoDrop) && _CLIENTPET(tradingWith) && tradingWith->GetPetType()<=petOther); @@ -920,6 +936,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st ); } else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { + tradingWith->SayString(TRADE_BACK, GetCleanName()); PushItemOnCursor(*baginst, true); } } @@ -939,23 +956,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } // Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { + tradingWith->SayString(TRADE_BACK, GetCleanName()); PushItemOnCursor(*inst, true); - DeleteItemInInventory(i); } } } - if(RuleB(TaskSystem, EnableTaskSystem)) { - int Cash = trade->cp + (trade->sp * 10) + (trade->gp * 100) + (trade->pp * 1000); - if (UpdateTasksOnDeliver(items, Cash, tradingWith->CastToNPC())) { - if(!tradingWith->IsMoving()) - tradingWith->FaceTarget(this); - - EVENT_ITEM_ScriptStopReturn(); - - } - } - char temp1[100] = { 0 }; char temp2[100] = { 0 }; snprintf(temp1, 100, "copper.%d", tradingWith->GetNPCTypeID()); @@ -975,12 +981,8 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st tradingWith->FaceTarget(this); } - EQ::ItemInstance *insts[4] = { 0 }; - for (int i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_NPC_END; ++i) { - insts[i - EQ::invslot::TRADE_BEGIN] = m_inv.PopItem(i); - database.SaveInventory(CharacterID(), nullptr, i); - } - + // quest items are filtered by task deliver updates + std::vector item_list(items.begin(), items.end()); parse->EventNPC(EVENT_TRADE, tradingWith->CastToNPC(), this, "", 0, &item_list); for(int i = 0; i < 4; ++i) {