diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index fd09f4d56..6240222d4 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3782,7 +3782,7 @@ struct AcceptNewTask_Struct { //was all 0's from client, server replied with same op, all 0's struct CancelTask_Struct { uint32 SequenceNumber; - uint32 unknown4; // Only seen 0x00000002 + uint32 type; // Only seen 0x00000002 }; #if 0 @@ -3836,28 +3836,28 @@ struct AvailableTaskTrailer_Struct { struct TaskDescriptionHeader_Struct { uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. uint32 TaskID; - uint32 unknown2; - uint32 unknown3; - uint8 unknown4; + uint8 open_window; + uint32 task_type; + uint32 reward_type; // if this != 4 says Ebon Crystals else Radiant Crystals }; struct TaskDescriptionData1_Struct { uint32 Duration; - uint32 unknown2; + uint32 dur_code; // if Duration == 0 uint32 StartTime; }; struct TaskDescriptionData2_Struct { - uint32 RewardCount; // ?? - uint32 unknown1; - uint32 unknown2; - uint16 unknown3; - //uint8 unknown4; + uint8 has_rewards; + uint32 coin_reward; + uint32 xp_reward; + uint32 unknown3; // don't see it affecting display, faction maybe }; struct TaskDescriptionTrailer_Struct { //uint16 unknown1; // 0x0012 uint32 Points; + uint8 has_reward_selection; // uses newer reward selection window, not in all clients }; struct TaskActivityHeader_Struct { @@ -5302,7 +5302,7 @@ struct MercenaryMerchantResponse_Struct { struct ServerLootItem_Struct { uint32 item_id; // uint32 item_id; int16 equip_slot; // int16 equip_slot; - uint16 charges; // uint8 charges; + uint16 charges; // uint8 charges; uint16 lootslot; // uint16 lootslot; uint32 aug_1; // uint32 aug_1; uint32 aug_2; // uint32 aug_2; @@ -5330,7 +5330,7 @@ struct ClientMarqueeMessage_Struct { uint32 fade_out_time; //The fade out time, in ms uint32 duration; //in ms char msg[1]; //message plus null terminator - + }; typedef std::list ItemList; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 62ef6751a..499ca31ec 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -3368,13 +3368,16 @@ namespace RoF InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToRoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 5a30c1695..28bfb569e 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -3437,13 +3437,16 @@ namespace RoF2 InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToRoF2SayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 560151bfa..0ac9d1b84 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -2249,13 +2249,16 @@ namespace SoD InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToSoDSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index c16bed086..05bb95a6a 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1879,13 +1879,16 @@ namespace SoF InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToSoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index bf3c3d359..41e3e9206 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1459,13 +1459,16 @@ namespace Titanium InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToTitaniumSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) + sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct) + - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; @@ -1479,6 +1482,7 @@ namespace Titanium InBuffer += strlen(InBuffer) + 1; memcpy(OutBuffer, InBuffer, sizeof(TaskDescriptionTrailer_Struct)); + // we have an extra DWORD in the trailer struct, client should ignore it so w/e delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 79fdca333..608c6622f 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2528,13 +2528,16 @@ namespace UF InBuffer += description_size; InBuffer += sizeof(TaskDescriptionData2_Struct); - std::string old_message = InBuffer; // start 'Reward' as string + uint32 reward_size = strlen(InBuffer) + 1; + InBuffer += reward_size; + + std::string old_message = InBuffer; // start item link string std::string new_message; ServerToUFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ - title_size + description_size + new_message.length() + 1; + title_size + description_size + reward_size + new_message.length() + 1; in->pBuffer = new unsigned char[in->size]; diff --git a/utils/sql/git/required/tasks_revamp.sql b/utils/sql/git/required/tasks_revamp.sql index 160a7e29a..4e1452fcc 100644 --- a/utils/sql/git/required/tasks_revamp.sql +++ b/utils/sql/git/required/tasks_revamp.sql @@ -1,3 +1,5 @@ ALTER TABLE `tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `id`; ALTER TABLE `tasks` ADD `duration_code` TINYINT NOT NULL DEFAULT '0' AFTER `duration`; UPDATE `tasks` SET `type` = '2'; -- we were treating them all as quests +ALTER TABLE `character_tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `slot`; +UPDATE `character_tasks` SET `type` = '2'; -- we were treating them all as quests diff --git a/zone/client.h b/zone/client.h index f958b20d5..989edf7dd 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1004,8 +1004,9 @@ public: void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1); void SendTaskFailed(int TaskID, int TaskIndex); void SendTaskComplete(int TaskIndex); + inline ClientTaskState *GetTaskState() const { return taskstate; } - inline void CancelTask(int TaskIndex) { if(taskstate) taskstate->CancelTask(this, TaskIndex); } + inline void CancelTask(int TaskIndex, TaskType type) { if(taskstate) taskstate->CancelTask(this, TaskIndex, type); } inline bool SaveTaskState() { return (taskmanager ? taskmanager->SaveClientState(this, taskstate) : false); } inline bool IsTaskStateLoaded() { return taskstate != nullptr; } inline bool IsTaskActive(int TaskID) { return (taskstate ? taskstate->IsTaskActive(TaskID) : false); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 5fe4291bd..362658e97 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4031,7 +4031,7 @@ void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->CancelTask(this, cts->SequenceNumber); + taskstate->CancelTask(this, cts->SequenceNumber, static_cast(cts->type)); } void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 8c7cd3aa2..15458c22e 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -292,7 +292,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { Log(Logs::Detail, Logs::Tasks,"TaskManager::SaveClientState for character ID %d", characterID); - if(state->ActiveTaskCount > 0) { + if(state->ActiveTaskCount > 0) { // TODO: tasks for(int task=0; taskActiveQuests[task].TaskID; if(taskID==TASKSLOTEMPTY) @@ -302,9 +302,9 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { Log(Logs::General, Logs::Tasks, "[CLIENTSAVE] TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", characterID, task, taskID); - std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " - "VALUES (%i, %i, %i, %i)", - characterID, taskID, task, state->ActiveQuests[task].AcceptedTime); + std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, type, acceptedtime) " + "VALUES (%i, %i, %i, %i, %i)", + characterID, taskID, task, static_cast(Tasks[taskID]->type), state->ActiveQuests[task].AcceptedTime); auto results = database.QueryDatabase(query); if (!results.Success()) { Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); @@ -660,9 +660,12 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { } } - for(int i=0; iActiveQuests[i].TaskID != TASKSLOTEMPTY) - state->UnlockActivities(characterID, i); + if (state->ActiveTask.TaskID != TASKSLOTEMPTY) + state->UnlockActivities(characterID, state->ActiveTask); + // TODO: shared + for (int i = 0; i < MAXACTIVEQUESTS; i++) + if (state->ActiveQuests[i].TaskID != TASKSLOTEMPTY) + state->UnlockActivities(characterID, state->ActiveQuests[i]); Log(Logs::General, Logs::Tasks, "[CLIENTLOAD] LoadClientState for Character ID %d DONE!", characterID); return true; @@ -1313,10 +1316,14 @@ ClientTaskState::ClientTaskState() { LastCompletedTaskLoaded = 0; CheckedTouchActivities = false; - for(int i=0; iTasks[ActiveQuests[TaskIndex].TaskID]; + TaskInformation* Task = taskmanager->Tasks[task_info.TaskID]; - if(Task==nullptr) return true; + if (Task == nullptr) + return true; // On loading the client state, all activities that are not completed, are // marked as hidden. For Sequential (non-stepped) mode, we mark the first // activity as active if not complete. Log(Logs::General, Logs::Tasks, "[UPDATE] CharID: %i Task: %i Sequence mode is %i", - CharID, ActiveQuests[TaskIndex].TaskID, Task->SequenceMode); - if(Task->SequenceMode == ActivitiesSequential) { + CharID, task_info.TaskID, Task->SequenceMode); - if(ActiveQuests[TaskIndex].Activity[0].State != ActivityCompleted) - ActiveQuests[TaskIndex].Activity[0].State = ActivityActive; + if (Task->SequenceMode == ActivitiesSequential) { + if (task_info.Activity[0].State != ActivityCompleted) + task_info.Activity[0].State = ActivityActive; // Enable the next Hidden task. - for(int i=0; iActivityCount; i++) { - if((ActiveQuests[TaskIndex].Activity[i].State == ActivityActive) && - (!Task->Activity[i].Optional)) { + for (int i = 0; i < Task->ActivityCount; i++) { + if ((task_info.Activity[i].State == ActivityActive) && + (!Task->Activity[i].Optional)) { AllActivitiesComplete = false; break; } - if(ActiveQuests[TaskIndex].Activity[i].State == ActivityHidden) { - ActiveQuests[TaskIndex].Activity[i].State = ActivityActive; + + if (task_info.Activity[i].State == ActivityHidden) { + task_info.Activity[i].State = ActivityActive; AllActivitiesComplete = false; break; } } - if(AllActivitiesComplete && RuleB(TaskSystem, RecordCompletedTasks)) { - if(RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { + + if (AllActivitiesComplete && RuleB(TaskSystem, RecordCompletedTasks)) { + if (RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { Log(Logs::General, Logs::Tasks, "[UPDATE] KeepOneRecord enabled"); auto Iterator = CompletedTasks.begin(); int ErasedElements = 0; - while(Iterator != CompletedTasks.end()) { + while (Iterator != CompletedTasks.end()) { int TaskID = (*Iterator).TaskID; - if(TaskID == ActiveQuests[TaskIndex].TaskID) { + if (TaskID == task_info.TaskID) { Iterator = CompletedTasks.erase(Iterator); ErasedElements++; - } - else + } else ++Iterator; } - Log(Logs::General, Logs::Tasks, "[UPDATE] Erased Element count is %i", ErasedElements); - if(ErasedElements) { - LastCompletedTaskLoaded -= ErasedElements; - DeleteCompletedTaskFromDatabase(CharID, ActiveQuests[TaskIndex].TaskID); - } + Log(Logs::General, Logs::Tasks, "[UPDATE] Erased Element count is %i", ErasedElements); + + if (ErasedElements) { + LastCompletedTaskLoaded -= ErasedElements; + DeleteCompletedTaskFromDatabase(CharID, task_info.TaskID); + } } CompletedTaskInformation cti; - cti.TaskID = ActiveQuests[TaskIndex].TaskID; + cti.TaskID = task_info.TaskID; cti.CompletedTime = time(nullptr); - for(int i=0; iActivityCount; i++) - cti.ActivityDone[i] = (ActiveQuests[TaskIndex].Activity[i].State == ActivityCompleted); + for (int i = 0; i < Task->ActivityCount; i++) + cti.ActivityDone[i] = (task_info.Activity[i].State == ActivityCompleted); CompletedTasks.push_back(cti); } - Log(Logs::General, Logs::Tasks, "[UPDATE] Returning sequential task, AllActivitiesComplete is %i", AllActivitiesComplete); + + Log(Logs::General, Logs::Tasks, "[UPDATE] Returning sequential task, AllActivitiesComplete is %i", + AllActivitiesComplete); + return AllActivitiesComplete; } @@ -1416,66 +1429,69 @@ bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { bool CurrentStepComplete = true; - Log(Logs::General, Logs::Tasks, "[UPDATE] Current Step is %i, Last Step is %i", ActiveQuests[TaskIndex].CurrentStep, Task->LastStep); + Log(Logs::General, Logs::Tasks, "[UPDATE] Current Step is %i, Last Step is %i", task_info.CurrentStep, + Task->LastStep); // If CurrentStep is -1, this is the first call to this method since loading the // client state. Unlock all activities with a step number of 0 - if(ActiveQuests[TaskIndex].CurrentStep == -1) { - for(int i=0; iActivityCount; i++) { - if((Task->Activity[i].StepNumber == 0) && - (ActiveQuests[TaskIndex].Activity[i].State == ActivityHidden)) { - ActiveQuests[TaskIndex].Activity[i].State = ActivityActive; - //ActiveQuests[TaskIndex].Activity[i].Updated=true; + if (task_info.CurrentStep == -1) { + for (int i = 0; i < Task->ActivityCount; i++) { + if (Task->Activity[i].StepNumber == 0 && task_info.Activity[i].State == ActivityHidden) { + task_info.Activity[i].State = ActivityActive; + // task_info.Activity[i].Updated=true; } } - ActiveQuests[TaskIndex].CurrentStep = 0; + task_info.CurrentStep = 0; } - for(int Step=ActiveQuests[TaskIndex].CurrentStep; Step<=Task->LastStep; Step++) { - for(int Activity=0; ActivityActivityCount; Activity++) { - if(Task->Activity[Activity].StepNumber == (int)ActiveQuests[TaskIndex].CurrentStep) { - if((ActiveQuests[TaskIndex].Activity[Activity].State != ActivityCompleted) && - (!Task->Activity[Activity].Optional)) { + for (int Step = task_info.CurrentStep; Step <= Task->LastStep; Step++) { + for (int Activity = 0; Activity < Task->ActivityCount; Activity++) { + if (Task->Activity[Activity].StepNumber == (int)task_info.CurrentStep) { + if ((task_info.Activity[Activity].State != ActivityCompleted) && + (!Task->Activity[Activity].Optional)) { CurrentStepComplete = false; AllActivitiesComplete = false; break; } } } - if(!CurrentStepComplete) break; - ActiveQuests[TaskIndex].CurrentStep++; + if (!CurrentStepComplete) + break; + task_info.CurrentStep++; } - if(AllActivitiesComplete) { - if(RuleB(TaskSystem, RecordCompletedTasks)) { + if (AllActivitiesComplete) { + if (RuleB(TaskSystem, RecordCompletedTasks)) { // If we are only keeping one completed record per task, and the player has done // the same task again, erase the previous completed entry for this task. - if(RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { + if (RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { Log(Logs::General, Logs::Tasks, "[UPDATE] KeepOneRecord enabled"); auto Iterator = CompletedTasks.begin(); int ErasedElements = 0; - while(Iterator != CompletedTasks.end()) { + + while (Iterator != CompletedTasks.end()) { int TaskID = (*Iterator).TaskID; - if(TaskID == ActiveQuests[TaskIndex].TaskID) { + if (TaskID == task_info.TaskID) { Iterator = CompletedTasks.erase(Iterator); ErasedElements++; - } - else + } else ++Iterator; } - Log(Logs::General, Logs::Tasks, "[UPDATE] Erased Element count is %i", ErasedElements); - if(ErasedElements) { - LastCompletedTaskLoaded -= ErasedElements; - DeleteCompletedTaskFromDatabase(CharID, ActiveQuests[TaskIndex].TaskID); - } + Log(Logs::General, Logs::Tasks, "[UPDATE] Erased Element count is %i", ErasedElements); + + if (ErasedElements) { + LastCompletedTaskLoaded -= ErasedElements; + DeleteCompletedTaskFromDatabase(CharID, task_info.TaskID); + } } + CompletedTaskInformation cti; - cti.TaskID = ActiveQuests[TaskIndex].TaskID; + cti.TaskID = task_info.TaskID; cti.CompletedTime = time(nullptr); - for(int i=0; iActivityCount; i++) - cti.ActivityDone[i] = (ActiveQuests[TaskIndex].Activity[i].State == ActivityCompleted); + for (int i = 0; i < Task->ActivityCount; i++) + cti.ActivityDone[i] = (task_info.Activity[i].State == ActivityCompleted); CompletedTasks.push_back(cti); } @@ -1484,19 +1500,17 @@ bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { // Mark all non-completed tasks in the current step as active // - for(int Activity=0; ActivityActivityCount; Activity++) { - if((Task->Activity[Activity].StepNumber == (int)ActiveQuests[TaskIndex].CurrentStep) && - (ActiveQuests[TaskIndex].Activity[Activity].State == ActivityHidden)) { - ActiveQuests[TaskIndex].Activity[Activity].State = ActivityActive; - ActiveQuests[TaskIndex].Activity[Activity].Updated=true; + for (int Activity = 0; Activity < Task->ActivityCount; Activity++) { + if ((Task->Activity[Activity].StepNumber == (int)task_info.CurrentStep) && + (task_info.Activity[Activity].State == ActivityHidden)) { + task_info.Activity[Activity].State = ActivityActive; + task_info.Activity[Activity].Updated = true; } } return false; - } - void ClientTaskState::UpdateTasksOnKill(Client *c, int NPCTypeID) { UpdateTasksByNPC(c, ActivityKill, NPCTypeID); @@ -1869,12 +1883,12 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T // Flag the activity as complete ActiveQuests[TaskIndex].Activity[ActivityID].State = ActivityCompleted; // Unlock subsequent activities for this task - bool TaskComplete = UnlockActivities(c->CharacterID(), TaskIndex); + bool TaskComplete = UnlockActivities(c->CharacterID(), ActiveQuests[TaskIndex]); // TODO: fix this function Log(Logs::General, Logs::Tasks, "[UPDATE] TaskCompleted is %i", TaskComplete); // and by the 'Task Stage Completed' message c->SendTaskActivityComplete(ActiveQuests[TaskIndex].TaskID, ActivityID, TaskIndex); // Send the updated task/activity list to the client - taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false); + taskmanager->SendSingleActiveTaskToClient(c, ActiveQuests[TaskIndex], TaskComplete, false); // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title.c_str()); @@ -1911,7 +1925,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T c->SendTaskActivityComplete(ActiveQuests[TaskIndex].TaskID, 0, TaskIndex, false); taskmanager->SaveClientState(c, this); //c->SendTaskComplete(TaskIndex); - c->CancelTask(TaskIndex); + c->CancelTask(TaskIndex, TaskType::Quest); // TODO: fix //if(Task->RewardMethod != METHODQUEST) RewardTask(c, Task); // If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST RewardTask(c, Task); @@ -2058,7 +2072,7 @@ void ClientTaskState::FailTask(Client *c, int TaskID) { if(ActiveQuests[i].TaskID==TaskID) { c->SendTaskFailed(ActiveQuests[i].TaskID, i); // Remove the task from the client - c->CancelTask(i); + c->CancelTask(i, TaskType::Quest); // TODO: fix return; } @@ -2282,7 +2296,7 @@ void ClientTaskState::TaskPeriodicChecks(Client *c) { // Send Red Task Failed Message c->SendTaskFailed(ActiveQuests[i].TaskID, i); // Remove the task from the client - c->CancelTask(i); + c->CancelTask(i, TaskType::Quest); // TODO: Fix // It is a conscious decision to only fail one task per call to this method, // otherwise the player will not see all the failed messages where multiple // tasks fail at the same time. @@ -2536,7 +2550,7 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i { auto outapp = new EQApplicationPacket(OP_TaskActivity, 25); outapp->WriteUInt32(ClientTaskIndex); - outapp->WriteUInt32(2); + outapp->WriteUInt32(static_cast(Tasks[TaskID]->type)); outapp->WriteUInt32(TaskID); outapp->WriteUInt32(ActivityID); outapp->WriteUInt32(0); @@ -2552,7 +2566,7 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i tass = (TaskActivityShort_Struct*)outapp->pBuffer; tass->TaskSequenceNumber = ClientTaskIndex; - tass->unknown2 = 0x00000002; + tass->unknown2 = static_cast(Tasks[TaskID]->type); tass->TaskID = TaskID; tass->ActivityID = ActivityID; tass->unknown3 = 0x000000; @@ -2669,7 +2683,7 @@ void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int auto outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength); outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber - outapp->WriteUInt32(2); // unknown2 + outapp->WriteUInt32(static_cast(Tasks[TaskID]->type)); // task type outapp->WriteUInt32(TaskID); outapp->WriteUInt32(ActivityID); outapp->WriteUInt32(0); // unknown3 @@ -2687,7 +2701,7 @@ void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int // One of these unknown fields maybe related to the 'Use On' activity types outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text1.c_str()); - outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Text2.size() + 1); // String Length - Add in null terminator + outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Text2.length() + 1); // String Length - Add in null terminator outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text2.c_str()); // Goal Count @@ -2730,15 +2744,18 @@ void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int } -void TaskManager::SendActiveTasksToClient(Client *c, bool TaskComplete) { +void TaskManager::SendActiveTasksToClient(Client *c, bool TaskComplete) +{ + auto state = c->GetTaskState(); + if (!state) + return; - //for(int TaskIndex=0; TaskIndexGetActiveTaskCount(); TaskIndex++) { - for(int TaskIndex=0; TaskIndexGetActiveTaskID(TaskIndex); if((TaskID==0) || (Tasks[TaskID] ==0)) continue; int StartTime = c->GetTaskStartTime(TaskIndex); - SendActiveTaskDescription(c, TaskID, TaskIndex, StartTime, Tasks[TaskID]->Duration, false); + SendActiveTaskDescription(c, TaskID, state->ActiveQuests[TaskIndex], StartTime, Tasks[TaskID]->Duration, false); Log(Logs::General, Logs::Tasks, "[UPDATE] SendActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID)); int Sequence = 0; @@ -2764,62 +2781,54 @@ void TaskManager::SendActiveTasksToClient(Client *c, bool TaskComplete) { } } +void TaskManager::SendSingleActiveTaskToClient(Client *c, ClientTaskInformation &task_info, bool TaskComplete, + bool BringUpTaskJournal) +{ + int TaskID = task_info.TaskID; -void TaskManager::SendSingleActiveTaskToClient(Client *c, int TaskIndex, bool TaskComplete, bool BringUpTaskJournal) { + if (TaskID == 0 || Tasks[TaskID] == nullptr) + return; - if((TaskIndex < 0) || (TaskIndex >= MAXACTIVEQUESTS)) return; - - int TaskID = c->GetActiveTaskID(TaskIndex); - - if((TaskID==0) || (Tasks[TaskID] ==0)) return; - - int StartTime = c->GetTaskStartTime(TaskIndex); - SendActiveTaskDescription(c, TaskID, TaskIndex, StartTime, Tasks[TaskID]->Duration, BringUpTaskJournal); + int StartTime = task_info.AcceptedTime; + SendActiveTaskDescription(c, TaskID, task_info, StartTime, Tasks[TaskID]->Duration, BringUpTaskJournal); Log(Logs::General, Logs::Tasks, "[UPDATE] SendSingleActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID)); - int Sequence = 0; - - for(int Activity=0; ActivityGetTaskActivityState(TaskIndex, Activity) != ActivityHidden) { - Log(Logs::General, Logs::Tasks, "[UPDATE] Long: %i, %i, %i Complete=%i", TaskID, Activity, TaskIndex, TaskComplete); - if(Activity==GetActivityCount(TaskID)-1) - SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + for (int Activity = 0; Activity < GetActivityCount(TaskID); Activity++) { + if(task_info.Activity[Activity].State != ActivityHidden) { + Log(Logs::General, Logs::Tasks, "[UPDATE] Long: %i, %i Complete=%i", TaskID, Activity, TaskComplete); + if (Activity == GetActivityCount(TaskID) - 1) + SendTaskActivityLong(c, TaskID, Activity, task_info.slot, Tasks[TaskID]->Activity[Activity].Optional, TaskComplete); else - SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + SendTaskActivityLong(c, TaskID, Activity, task_info.slot, Tasks[TaskID]->Activity[Activity].Optional, 0); + } else { + Log(Logs::General, Logs::Tasks, "[UPDATE] Short: %i, %i", TaskID, Activity); + SendTaskActivityShort(c, TaskID, Activity, task_info.slot); } - else { - Log(Logs::General, Logs::Tasks, "[UPDATE] Short: %i, %i, %i", TaskID, Activity, TaskIndex); - SendTaskActivityShort(c, TaskID, Activity, TaskIndex); - } - Sequence++; } } -void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceNumber, int StartTime, int Duration, bool BringUpTaskJournal) +void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInformation &task_info, int StartTime, int Duration, bool BringUpTaskJournal) { if ((TaskID < 1) || (TaskID >= MAXTASKS) || !Tasks[TaskID]) return; - int PacketLength = sizeof(TaskDescriptionHeader_Struct) + Tasks[TaskID]->Title.size() + 1 - + sizeof(TaskDescriptionData1_Struct) + Tasks[TaskID]->Description.size() + 1 + int PacketLength = sizeof(TaskDescriptionHeader_Struct) + Tasks[TaskID]->Title.length() + 1 + + sizeof(TaskDescriptionData1_Struct) + Tasks[TaskID]->Description.length() + 1 + sizeof(TaskDescriptionData2_Struct) + 1 + sizeof(TaskDescriptionTrailer_Struct); - std::string reward_text; - int ItemID = 0; - // If there is an item make the Reward text into a link to the item (only the first item if a list // is specified). I have been unable to get multiple item links to work. // - if(Tasks[TaskID]->RewardID) { + if(Tasks[TaskID]->RewardID && Tasks[TaskID]->item_link.empty()) { + int ItemID = 0; // If the reward is a list of items, and the first entry on the list is valid - if(Tasks[TaskID]->RewardMethod==METHODSINGLEID) { + if (Tasks[TaskID]->RewardMethod == METHODSINGLEID) { ItemID = Tasks[TaskID]->RewardID; - } - else if(Tasks[TaskID]->RewardMethod==METHODLIST) { + } else if (Tasks[TaskID]->RewardMethod == METHODLIST) { ItemID = GoalListManager.GetFirstEntry(Tasks[TaskID]->RewardID); - if(ItemID < 0) + if (ItemID < 0) ItemID = 0; } @@ -2830,20 +2839,11 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetItemData(reward_item); linker.SetTaskUse(); - if (!Tasks[TaskID]->Reward.empty()) - linker.SetProxyText(Tasks[TaskID]->Reward.c_str()); - - reward_text.append(linker.GenerateLink()); - } - else { - reward_text.append(Tasks[TaskID]->Reward); + Tasks[TaskID]->item_link = linker.GenerateLink(); } + } - } - else { - reward_text.append(Tasks[TaskID]->Reward); - } - PacketLength += reward_text.length() + 1; + PacketLength += Tasks[TaskID]->Reward.length() + 1 + Tasks[TaskID]->item_link.length() + 1; char *Ptr; TaskDescriptionHeader_Struct* tdh; @@ -2855,60 +2855,57 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN tdh = (TaskDescriptionHeader_Struct*)outapp->pBuffer; - tdh->SequenceNumber = SequenceNumber; + tdh->SequenceNumber = task_info.slot; tdh->TaskID = TaskID; - - if(BringUpTaskJournal) - tdh->unknown2 = 0x00000201; - else - tdh->unknown2 = 0x00000200; - //tdh->unknown2 = 0x00000100; // This makes the Task Description have an S instead of Q, but the description doesn't show - - tdh->unknown3 = 0x00000000; - tdh->unknown4 = 0x00; + tdh->open_window = BringUpTaskJournal; + tdh->task_type = static_cast(Tasks[TaskID]->type); + tdh->reward_type = 0; // TODO: 4 says Radiant Crystals else Ebon Crystals when shared task Ptr = (char *) tdh + sizeof(TaskDescriptionHeader_Struct); sprintf(Ptr, "%s", Tasks[TaskID]->Title.c_str()); - Ptr = Ptr + strlen(Ptr) + 1; + Ptr += Tasks[TaskID]->Title.length() + 1; tdd1 = (TaskDescriptionData1_Struct*)Ptr; tdd1->Duration = Duration; - tdd1->unknown2 = 0x00000000; + tdd1->dur_code = static_cast(Tasks[TaskID]->dur_code); tdd1->StartTime = StartTime; Ptr = (char *) tdd1 + sizeof(TaskDescriptionData1_Struct); sprintf(Ptr, "%s", Tasks[TaskID]->Description.c_str()); - Ptr = Ptr + strlen(Ptr) + 1; + Ptr += Tasks[TaskID]->Description.length() + 1; tdd2 = (TaskDescriptionData2_Struct*)Ptr; - // This next field may not be a reward count. It is always 1 in the packets I have seen. Setting it to 2 and trying - // to include multiple item links has so far proven futile. Setting it to 0 ends the packet after the next byte. - tdd2->RewardCount = 1; + // we have this reward stuff! + // if we ever don't hardcode this, TaskDescriptionTrailer_Struct will need to be fixed since + // "has_reward_selection" is after this bool! Smaller packet when this is 0 + tdd2->has_rewards = 1; - if(Tasks[TaskID]->XPReward) - tdd2->unknown1 = 0x00000100; - else - tdd2->unknown1 = 0x00000000; + tdd2->coin_reward = Tasks[TaskID]->CashReward; + tdd2->xp_reward = Tasks[TaskID]->XPReward ? 1 : 0; // just booled + tdd2->unknown3 = 0; // STRONGLY suspect this is faction reward, also just booled to hide details? - tdd2->unknown2 = 0x00000000; - tdd2->unknown3 = 0x0000; Ptr = (char *) tdd2 + sizeof(TaskDescriptionData2_Struct); - sprintf(Ptr, "%s", reward_text.c_str()); - Ptr = Ptr + strlen(Ptr) + 1; + // we actually have 2 strings here. One is max length 96 and not parsed for item links + // We actually skipped past that string incorrectly before, so TODO: fix item link string + sprintf(Ptr, "%s", Tasks[TaskID]->Reward.c_str()); + Ptr += Tasks[TaskID]->Reward.length() + 1; + + // second string is parsed for item links + sprintf(Ptr, "%s", Tasks[TaskID]->item_link.c_str()); + Ptr += Tasks[TaskID]->item_link.length() + 1; tdt = (TaskDescriptionTrailer_Struct*)Ptr; - tdt->Points = 0x00000000; // Points Count - + tdt->Points = 0x00000000; // Points Count TODO: this does have a visible affect on the client ... + tdt->has_reward_selection = 0; // TODO: new rewards window c->QueuePacket(outapp); safe_delete(outapp); - } bool ClientTaskState::IsTaskActivityCompleted(int index, int ActivityID) { @@ -2960,21 +2957,25 @@ void ClientTaskState::CancelAllTasks(Client *c) { // It removes tasks from the in-game client state ready for them to be // resent to the client, in case an updated task fails to load + CancelTask(c, 0, TaskType::Task, false); + ActiveTask.TaskID = TASKSLOTEMPTY; + for(int i=0; ipBuffer; cts->SequenceNumber = SequenceNumber; - cts->unknown4 = 0x00000002; + cts->type = static_cast(type); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask"); @@ -2982,11 +2983,11 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromD safe_delete(outapp); if(RemoveFromDB) - RemoveTask(c, SequenceNumber); + RemoveTask(c, SequenceNumber, type); } -void ClientTaskState::RemoveTask(Client *c, int sequenceNumber) { - +void ClientTaskState::RemoveTask(Client *c, int sequenceNumber, TaskType type) +{ int characterID = c->CharacterID(); Log(Logs::General, Logs::Tasks, "[UPDATE] ClientTaskState Cancel Task %i ", sequenceNumber); @@ -2999,104 +3000,151 @@ void ClientTaskState::RemoveTask(Client *c, int sequenceNumber) { } Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask: %s", query.c_str()); - query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i", - characterID, ActiveQuests[sequenceNumber].TaskID); + query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i AND type=%i", + characterID, ActiveQuests[sequenceNumber].TaskID, static_cast(type)); results = database.QueryDatabase(query); if(!results.Success()) Log(Logs::General, Logs::Error, "[TASKS] Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask: %s", query.c_str()); - ActiveQuests[sequenceNumber].TaskID = TASKSLOTEMPTY; - ActiveTaskCount--; + switch (type) { + case TaskType::Task: + ActiveTask.TaskID = TASKSLOTEMPTY; + break; + case TaskType::Shared: + break; // TODO: shared tasks + case TaskType::Quest: + ActiveQuests[sequenceNumber].TaskID = TASKSLOTEMPTY; + ActiveTaskCount--; + break; + default: + break; + } } -void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement) { - - if(!taskmanager || TaskID<0 || TaskID>=MAXTASKS) { +void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement) +{ + if (!taskmanager || TaskID < 0 || TaskID >= MAXTASKS) { c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID); return; - } - if(taskmanager->Tasks[TaskID] == nullptr) { + auto task = taskmanager->Tasks[TaskID]; + + if (task == nullptr) { c->Message(13, "Invalid TaskID %i", TaskID); return; } - if(ActiveTaskCount==MAXACTIVEQUESTS) { + bool max_tasks = false; + + switch (task->type) { + case TaskType::Task: + if (ActiveTask.TaskID != TASKSLOTEMPTY) + max_tasks = true; + break; + case TaskType::Shared: // TODO: shared tasks + // if (something) + max_tasks = true; + break; + case TaskType::Quest: + if (ActiveTaskCount == MAXACTIVEQUESTS) + max_tasks = true; + break; + default: + break; + } + + if (max_tasks) { c->Message(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVEQUESTS); return; } - for(int i=0; iMessage(13, "You have already been assigned this task."); - return; + // only Quests can have more than one, so don't need to check others + if (task->type == TaskType::Quest) { + for (int i = 0; i < MAXACTIVEQUESTS; i++) { + if (ActiveQuests[i].TaskID == TaskID) { + c->Message(13, "You have already been assigned this task."); + return; + } } } - if (enforce_level_requirement && !taskmanager->AppropriateLevel(TaskID, c->GetLevel())) - { + if (enforce_level_requirement && !taskmanager->AppropriateLevel(TaskID, c->GetLevel())) { c->Message(13, "You are outside the level range of this task."); return; } - if(!taskmanager->IsTaskRepeatable(TaskID) && IsTaskCompleted(TaskID)) return; + if (!taskmanager->IsTaskRepeatable(TaskID) && IsTaskCompleted(TaskID)) + return; // We do it this way, because when the Client cancels a task, it retains the sequence number of the remaining // tasks in it's window, until something causes the TaskDescription packets to be sent again. We could just // resend all the active task data to the client when it cancels a task, but that could be construed as a // waste of bandwidth. // - int FreeSlot = -1; - for(int i=0; itype) { + case TaskType::Task: + active_slot = &ActiveTask; + break; + case TaskType::Shared: // TODO: shared + active_slot = nullptr; + break; + case TaskType::Quest: + for (int i = 0; i < MAXACTIVEQUESTS; i++) { + Log(Logs::General, Logs::Tasks, + "[UPDATE] ClientTaskState Looking for free slot in slot %i, found TaskID of %i", i, + ActiveQuests[i].TaskID); + if (ActiveQuests[i].TaskID == 0) { + active_slot = &ActiveQuests[i]; + break; + } } + break; + default: + break; } // This shouldn't happen unless there is a bug in the handling of ActiveTaskCount somewhere - if(FreeSlot == -1) { + if (active_slot == nullptr) { c->Message(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVEQUESTS); return; } + active_slot->TaskID = TaskID; + active_slot->AcceptedTime = time(nullptr); + active_slot->Updated = true; + active_slot->CurrentStep = -1; - ActiveQuests[FreeSlot].TaskID = TaskID; - ActiveQuests[FreeSlot].AcceptedTime = time(nullptr); - ActiveQuests[FreeSlot].Updated = true; - ActiveQuests[FreeSlot].CurrentStep = -1; - - for(int i=0; iTasks[TaskID]->ActivityCount; i++) { - ActiveQuests[FreeSlot].Activity[i].ActivityID = i; - ActiveQuests[FreeSlot].Activity[i].DoneCount = 0; - ActiveQuests[FreeSlot].Activity[i].State = ActivityHidden; - ActiveQuests[FreeSlot].Activity[i].Updated = true; + for (int i = 0; i < taskmanager->Tasks[TaskID]->ActivityCount; i++) { + active_slot->Activity[i].ActivityID = i; + active_slot->Activity[i].DoneCount = 0; + active_slot->Activity[i].State = ActivityHidden; + active_slot->Activity[i].Updated = true; } - UnlockActivities(c->CharacterID(), FreeSlot); - ActiveTaskCount++; - taskmanager->SendSingleActiveTaskToClient(c, FreeSlot, false, true); + + UnlockActivities(c->CharacterID(), *active_slot); + + if (task->type == TaskType::Quest) + ActiveTaskCount++; + + taskmanager->SendSingleActiveTaskToClient(c, *active_slot, false, true); c->Message(0, "You have been assigned the task '%s'.", taskmanager->Tasks[TaskID]->Title.c_str()); - char *buf = 0; - MakeAnyLenString(&buf, "%d", TaskID); + 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."); - safe_delete_array(buf); return; } - taskmanager->SaveClientState(c, this); - parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf, 0); - safe_delete_array(buf); + taskmanager->SaveClientState(c, this); + parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf.c_str(), 0); } void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) { diff --git a/zone/tasks.h b/zone/tasks.h index 8db0c4ef8..5fd074300 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -136,6 +136,7 @@ struct TaskInformation { std::string Title; // max length 64 std::string Description; // max length 4000, 2048 on Tit std::string Reward; + std::string item_link; // max length 128 older clients, item link gets own string int RewardID; int CashReward; // Expressed in copper int XPReward; @@ -165,6 +166,7 @@ struct ClientActivityInformation { }; struct ClientTaskInformation { + int slot; // intrusive, but makes things easier :P int TaskID; int CurrentStep; int AcceptedTime; @@ -199,9 +201,9 @@ public: ActivityState GetTaskActivityState(int index, int ActivityID); void UpdateTaskActivity(Client *c, int TaskID, int ActivityID, int Count, bool ignore_quest_update = false); void ResetTaskActivity(Client *c, int TaskID, int ActivityID); - void CancelTask(Client *c, int SequenceNumber, bool RemoveFromDB = true); + void CancelTask(Client *c, int SequenceNumber, TaskType type, bool RemoveFromDB = true); void CancelAllTasks(Client *c); - void RemoveTask(Client *c, int SequenceNumber); + void RemoveTask(Client *c, int SequenceNumber, TaskType type); bool UpdateTasksByNPC(Client *c, int ActivityType, int NPCTypeID); void UpdateTasksOnKill(Client *c, int NPCTypeID); void UpdateTasksForItem(Client *c, ActivityType Type, int ItemID, int Count=1); @@ -229,7 +231,7 @@ public: friend class TaskManager; private: - bool UnlockActivities(int CharID, int TaskIndex); + bool UnlockActivities(int CharID, ClientTaskInformation &task_info); void IncrementDoneCount(Client *c, TaskInformation *Task, int TaskIndex, int ActivityID, int Count = 1, bool ignore_quest_update = false); int ActiveTaskCount; ClientTaskInformation ActiveTask; // only one @@ -263,7 +265,7 @@ public: void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID); void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks); // task list provided by QuestManager (perl/lua) void SendActiveTasksToClient(Client *c, bool TaskComplete=false); - void SendSingleActiveTaskToClient(Client *c, int TaskIndex, bool TaskComplete, bool BringUpTaskJournal=false); + void SendSingleActiveTaskToClient(Client *c, ClientTaskInformation &task_info, bool TaskComplete, bool BringUpTaskJournal = false); void SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex); void SendTaskActivityLong(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete=false); @@ -283,7 +285,7 @@ private: TaskProximityManager ProximityManager; TaskInformation* Tasks[MAXTASKS]; std::vector TaskSets[MAXTASKSETS]; - void SendActiveTaskDescription(Client *c, int TaskID, int SequenceNumber, int StartTime, int Duration, bool BringUpTaskJournal=false); + void SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInformation &task_info, int StartTime, int Duration, bool BringUpTaskJournal=false); };