diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 3f4556a07..3666f78a3 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -462,6 +462,7 @@ N(OP_SetServerFilter), N(OP_SetStartCity), N(OP_SetTitle), N(OP_SetTitleReply), +N(OP_SharedTaskMemberList), N(OP_Shielding), N(OP_ShopDelItem), N(OP_ShopEnd), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 3d3481db7..3876f57af 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3698,6 +3698,7 @@ struct TaskMemberList_Struct { /*12*/ char list_pointer[0]; /* list is of the form: char member_name[1] //null terminated string + uint32 monster_mission; // class chosen uint8 task_leader //boolean flag */ }; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 016586ba8..0120b00a5 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -564,6 +564,7 @@ OP_TaskHistoryReply=0x25eb OP_DeclineAllTasks=0x0000 OP_TaskRequestTimer=0x4b76 OP_AcceptNewSharedTask=0x3e5e +OP_SharedTaskMemberList=0x4ddb # Title opcodes OP_NewTitlesAvailable=0x45d1 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index cf6704b55..bb59c2ae9 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -569,6 +569,7 @@ OP_TaskHistoryReply=0x3d05 OP_DeclineAllTasks=0x0000 OP_TaskRequestTimer=0x7a48 OP_AcceptNewSharedTask=0x6646 +OP_SharedTaskMemberList=0x1e7d # Title opcodes OP_NewTitlesAvailable=0x0d32 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index d5c3f7931..ddc10b971 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -535,6 +535,7 @@ OP_CancelTask=0x726b # C OP_DeclineAllTasks=0x0000 # OP_TaskRequestTimer=0x2e70 OP_AcceptNewSharedTask=0x4751 +OP_SharedTaskMemberList=0x55f4 OP_Shroud=0x6d1f diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index ab5bf766a..bf5d96be1 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -500,7 +500,7 @@ OP_TaskHistoryRequest=0x3035 # OP_TaskHistoryReply=0x3A60 # OP_CancelTask=0x4db6 #Xinu or 0x2c8c or 0x4db6 OP_DeclineAllTasks=0x0000 #not sure, 12 bytes -OP_TaskMemberList=0x3713 +#OP_TaskMemberList=0x3713 OP_TaskMemberInvite=0x3cde OP_TaskMemberInviteResponse=0x6cab OP_TaskMemberChange=0x354a @@ -511,6 +511,7 @@ OP_TaskPlayerList=0x0ad6 OP_TaskQuit=0x2c8c OP_TaskRequestTimer=0x0b08 OP_AcceptNewSharedTask=0x5bed +OP_SharedTaskMemberList=0x3713 #Title opcodes OP_NewTitlesAvailable=0x179c # diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 3f6042386..bfc7d4219 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -458,7 +458,7 @@ OP_TaskActivityComplete=0x54eb OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05 OP_TaskDescription=0x5ef7 # ShowEQ 10/27/05 OP_TaskActivity=0x682d # ShowEQ 10/27/05 -OP_TaskMemberList=0x722f #not sure +#OP_TaskMemberList=0x722f #not sure OP_OpenNewTasksWindow=0x5e7c #combined with OP_AvaliableTask I think OP_AvaliableTask=0x0000 OP_AcceptNewTask=0x207f @@ -476,6 +476,7 @@ OP_TaskPlayerList=0x3961 OP_TaskQuit=0x35dd OP_TaskRequestTimer=0x6a1d OP_AcceptNewSharedTask=0x194d +OP_SharedTaskMemberList=0x722f #task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes) diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 9bc3013ff..6f262e1cd 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -558,6 +558,7 @@ OP_CancelTask=0x3bf5 # C OP_DeclineAllTasks=0x0000 # OP_TaskRequestTimer=0x719e OP_AcceptNewSharedTask=0x6ded +OP_SharedTaskMemberList=0x584e # Title opcodes OP_NewTitlesAvailable=0x4b49 # C diff --git a/world/shared_tasks.cpp b/world/shared_tasks.cpp index 2e4b714da..1d2468202 100644 --- a/world/shared_tasks.cpp +++ b/world/shared_tasks.cpp @@ -433,6 +433,7 @@ void SharedTask::MemberLeftGame(ClientListEntry *cle) * Serializes Members into the SerializeBuffer * Starts with count then followed by names null-termed * In the future this will include monster mission shit + * This should probably send the SharedMember struct or something more like it, fine for now */ void SharedTask::SerializeMembers(SerializeBuffer &buf, bool include_leader) const { diff --git a/zone/tasks.cpp b/zone/tasks.cpp index dcdc577c5..480e49e31 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2451,6 +2451,15 @@ bool TaskManager::IsTaskRepeatable(int TaskID) { return Task->Repeatable; } +SharedTaskState *TaskManager::CreateSharedTask(int id, int task_id) +{ + auto ret = SharedTasks.insert({id, {id, task_id}}); + if (!ret.second) // hmm was already created + return nullptr; + + return &(ret.first->second); +} + bool ClientTaskState::TaskOutOfTime(TaskType type, int Index) { // Returns true if the Task in the specified slot has a time limit that has been exceeded. @@ -3267,10 +3276,8 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor if (ActiveTask.TaskID != TASKSLOTEMPTY) max_tasks = true; break; - case TaskType::Shared: // TODO: shared tasks - // if (something) - max_tasks = true; - break; + case TaskType::Shared: // shared tasks shouldn't call this function. should we log? + return; case TaskType::Quest: if (ActiveTaskCount == MAXACTIVEQUESTS) max_tasks = true; @@ -3312,9 +3319,8 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor case TaskType::Task: active_slot = &ActiveTask; break; - case TaskType::Shared: // TODO: shared - active_slot = nullptr; - break; + case TaskType::Shared: // shared aren't done here, should have returned already :P + return; case TaskType::Quest: for (int i = 0; i < MAXACTIVEQUESTS; i++) { Log(Logs::General, Logs::Tasks, @@ -3467,103 +3473,66 @@ void ClientTaskState::PendSharedTask(Client *c, int TaskID, int NPCID, bool enfo delete pack; return; - - /* - std::vector missing_players; // names of players not in this zone so we can put the checks off to world - bool task_failed = false; - if (group) { - for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if (group->members[i] == c) - continue; - - c->PendingTaskAddMember(group->membername[i]); - - if (group->members[i] && group->members[i]->IsClient()) { - auto *client = group->members[i]->CastToClient(); - auto *task_state = client->GetTaskState(); - if (!task_state->HasSlotForTask(task)) { - task_failed = true; - c->Message_StringID(13, TASK_REJECT_GROUP_HAVE_ONE, c->GetName()); - break; - } else { - if (task->replay_group) { - auto expires = client->GetTaskLockoutTimeLeft(task->replay_group); - if (expires > 0) { - task_failed = true; - std::string days = std::to_string(expires / 86400); - expires = expires % 86400; - std::string hours = std::to_string(expires / 3600); - expires = expires % 3600; - std::string minutes = std::to_string(expires / 60); - c->Message_StringID(13, TASK_REJECT_LOCKEDOUT_OTHER, - client->GetName(), days.c_str(), - hours.c_str(), minutes.c_str()); - client->Message_StringID(13, TASK_REJECT_LOCKEDOUT_ME, - days.c_str(), hours.c_str(), - minutes.c_str()); - break; - } - } - } - } else if (group->members[i] == nullptr) { // out of zone - missing_players.push_back(group->membername[i]); - } - } - } else if (raid) { - for (int i = 0; i < MAX_RAID_MEMBERS; ++i) { - if (raid->members[i].member == c) - continue; - - c->PendingTaskAddMember(raid->members[i].membername); - - if (raid->members[i].member) { - auto *client = raid->members[i].member; - auto *task_state = client->GetTaskState(); - if (!task_state->HasSlotForTask(task)) { - task_failed = true; - c->Message_StringID(13, TASK_REJECT_RAID_HAVE_ONE, c->GetName()); - break; - } else { - if (task->replay_group) { - auto expires = client->GetTaskLockoutTimeLeft(task->replay_group); - if (expires > 0) { - task_failed = true; - std::string days = std::to_string(expires / 86400); - expires = expires % 86400; - std::string hours = std::to_string(expires / 3600); - expires = expires % 3600; - std::string minutes = std::to_string(expires / 60); - c->Message_StringID(13, TASK_REJECT_LOCKEDOUT_OTHER, - client->GetName(), days.c_str(), - hours.c_str(), minutes.c_str()); - client->Message_StringID(13, TASK_REJECT_LOCKEDOUT_ME, - days.c_str(), hours.c_str(), - minutes.c_str()); - break; - } - } - } - } else if (raid->members[i].membername[0] != '\0') { // out of zone - missing_players.push_back(raid->members[i].membername); - } - } - } - - if (task_failed) { // we already yelled at them - c->ResetPendingTask(); - return; - } - - // so we've verified all the clients we can and didn't fail, time to pend and yell at world - c->SetPendingTask(TaskID, NPCID); - c->StartPendingTimer(); // in case something goes wrong and takes ages, we time out - */ - } void ClientTaskState::AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id, std::vector &members) { + // all of this data should have been verified already + // first we need to create the new SharedTaskState + auto task_state = taskmanager->CreateSharedTask(id, TaskID); + if (!task_state) { + // TODO: something failed, tell world + return; + } + // we need to init the activity now + auto task_activity = task_state->GetActivity(); + + task_activity->TaskID = TaskID; + task_activity->AcceptedTime = time(nullptr); + task_activity->Updated = true; + task_activity->CurrentStep = -1; + + for (int i = 0; i < taskmanager->Tasks[TaskID]->ActivityCount; i++) { + task_activity->Activity[i].ActivityID = i; + task_activity->Activity[i].DoneCount = 0; + task_activity->Activity[i].State = ActivityHidden; + task_activity->Activity[i].Updated = true; + } + + // TODO: figure out packet order + // unsure if we unlock now packet wise, just copying normal tasks for now + UnlockActivities(c->CharacterID(), *task_activity); + + taskmanager->SendSingleActiveTaskToClient(c, *task_activity, false, true); + c->Message(0, "You have been assigned the task '%s'.", taskmanager->Tasks[TaskID]->Title.c_str()); + + // send member list of just leader + task_state->AddMember(c->GetName(), c, true); + task_state->SendMembersList(c); + + // send compass shit + + // add everyone else and send that + // we could try to find these members so they could know about it ... but ahh not sure :P + for (auto &m : members) + task_state->AddMember(m); + + task_state->SendMembersList(c); + + std::string buf = std::to_string(TaskID); + NPC *npc = entity_list.GetID(NPCID)->CastToNPC(); + if(!npc) { + c->Message(clientMessageYellow, "Task Giver ID is %i", NPCID); + c->Message(clientMessageError, "Unable to find NPC to send EVENT_TASKACCEPTED to. Report this bug."); + // TODO: ahh do we wanna do this? clean up world at least + return; + } + + // TODO: save state + parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf.c_str(), 0); + // TODO: We need to tell world we are successful so we can tell all the other clients + // there are a few issues we need to solve with this } void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) { @@ -3810,3 +3779,25 @@ void SharedTaskState::MemberEnterZone(Mob *player) it->entity = player; } +void SharedTaskState::SendMembersList(Client *to) const +{ + if (!to) + return; + + SerializeBuffer buf(sizeof(TaskMemberList_Struct) + 15 * members.size()); + buf.WriteInt32(0); // unknown ids + buf.WriteInt32(0); + buf.WriteInt32(members.size()); + + for (auto &&m : members) { + buf.WriteString(m.name); + buf.WriteInt32(0); // monster mission shit + buf.WriteInt8(m.leader); + } + + auto outapp = new EQApplicationPacket(OP_SharedTaskMemberList, buf); + + to->QueuePacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/tasks.h b/zone/tasks.h index cba82562a..4e6993a92 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -95,11 +95,13 @@ struct SharedTaskMember { Mob *entity; // needs to be managed bool leader; SharedTaskMember() : entity(nullptr), leader(false) {} + SharedTaskMember(std::string name, Mob *entity, bool leader) : name(name), entity(entity), leader(leader) {} }; class SharedTaskState { public: SharedTaskState() : locked(false) {} + SharedTaskState(int id, int task_id) : id(id), task_id(task_id), locked(false) { } // ~SharedTaskState(); inline const bool IsLocked() const { return locked; } @@ -109,12 +111,24 @@ public: void MemberZoned(Mob *player); // player left zone, update their pointer void MemberEnterZone(Mob *player); // player entered zone, update their pointer + void AddMember(std::string name, Mob *entity = nullptr, bool leader = false) + { + members.push_back({name, entity, leader}); + if (leader) + leader_name = name; + } + + void SendMembersList(Client *to) const; + ClientTaskInformation *GetActivity() { return &activity; } friend class TaskManager; private: + int id; + int task_id; std::vector members; + std::string leader_name; ClientTaskInformation activity; bool locked; }; @@ -253,6 +267,7 @@ public: friend class ClientTaskState; void LoadSharedTask(int id); // loads the shared task state + SharedTaskState *CreateSharedTask(int id, int task_id); private: TaskGoalListManager GoalListManager;