Bunch of work

This commit is contained in:
Michael Cook (mackal) 2018-06-24 23:21:35 -04:00
parent 39d06a4012
commit 3498f7a56f
12 changed files with 312 additions and 240 deletions

View File

@ -3782,7 +3782,7 @@ struct AcceptNewTask_Struct {
//was all 0's from client, server replied with same op, all 0's //was all 0's from client, server replied with same op, all 0's
struct CancelTask_Struct { struct CancelTask_Struct {
uint32 SequenceNumber; uint32 SequenceNumber;
uint32 unknown4; // Only seen 0x00000002 uint32 type; // Only seen 0x00000002
}; };
#if 0 #if 0
@ -3836,28 +3836,28 @@ struct AvailableTaskTrailer_Struct {
struct TaskDescriptionHeader_Struct { struct TaskDescriptionHeader_Struct {
uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc.
uint32 TaskID; uint32 TaskID;
uint32 unknown2; uint8 open_window;
uint32 unknown3; uint32 task_type;
uint8 unknown4; uint32 reward_type; // if this != 4 says Ebon Crystals else Radiant Crystals
}; };
struct TaskDescriptionData1_Struct { struct TaskDescriptionData1_Struct {
uint32 Duration; uint32 Duration;
uint32 unknown2; uint32 dur_code; // if Duration == 0
uint32 StartTime; uint32 StartTime;
}; };
struct TaskDescriptionData2_Struct { struct TaskDescriptionData2_Struct {
uint32 RewardCount; // ?? uint8 has_rewards;
uint32 unknown1; uint32 coin_reward;
uint32 unknown2; uint32 xp_reward;
uint16 unknown3; uint32 unknown3; // don't see it affecting display, faction maybe
//uint8 unknown4;
}; };
struct TaskDescriptionTrailer_Struct { struct TaskDescriptionTrailer_Struct {
//uint16 unknown1; // 0x0012 //uint16 unknown1; // 0x0012
uint32 Points; uint32 Points;
uint8 has_reward_selection; // uses newer reward selection window, not in all clients
}; };
struct TaskActivityHeader_Struct { struct TaskActivityHeader_Struct {
@ -5302,7 +5302,7 @@ struct MercenaryMerchantResponse_Struct {
struct ServerLootItem_Struct { struct ServerLootItem_Struct {
uint32 item_id; // uint32 item_id; uint32 item_id; // uint32 item_id;
int16 equip_slot; // int16 equip_slot; int16 equip_slot; // int16 equip_slot;
uint16 charges; // uint8 charges; uint16 charges; // uint8 charges;
uint16 lootslot; // uint16 lootslot; uint16 lootslot; // uint16 lootslot;
uint32 aug_1; // uint32 aug_1; uint32 aug_1; // uint32 aug_1;
uint32 aug_2; // uint32 aug_2; uint32 aug_2; // uint32 aug_2;
@ -5330,7 +5330,7 @@ struct ClientMarqueeMessage_Struct {
uint32 fade_out_time; //The fade out time, in ms uint32 fade_out_time; //The fade out time, in ms
uint32 duration; //in ms uint32 duration; //in ms
char msg[1]; //message plus null terminator char msg[1]; //message plus null terminator
}; };
typedef std::list<ServerLootItem_Struct*> ItemList; typedef std::list<ServerLootItem_Struct*> ItemList;

View File

@ -3368,13 +3368,16 @@ namespace RoF
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToRoFSayLink(new_message, old_message); ServerToRoFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];

View File

@ -3437,13 +3437,16 @@ namespace RoF2
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToRoF2SayLink(new_message, old_message); ServerToRoF2SayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];

View File

@ -2249,13 +2249,16 @@ namespace SoD
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToSoDSayLink(new_message, old_message); ServerToSoDSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];

View File

@ -1879,13 +1879,16 @@ namespace SoF
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToSoFSayLink(new_message, old_message); ServerToSoFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];

View File

@ -1459,13 +1459,16 @@ namespace Titanium
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToTitaniumSayLink(new_message, old_message); ServerToTitaniumSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) + in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) +
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];
@ -1479,6 +1482,7 @@ namespace Titanium
InBuffer += strlen(InBuffer) + 1; InBuffer += strlen(InBuffer) + 1;
memcpy(OutBuffer, InBuffer, sizeof(TaskDescriptionTrailer_Struct)); 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; delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req); dest->FastQueuePacket(&in, ack_req);

View File

@ -2528,13 +2528,16 @@ namespace UF
InBuffer += description_size; InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct); 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; std::string new_message;
ServerToUFSayLink(new_message, old_message); ServerToUFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_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]; in->pBuffer = new unsigned char[in->size];

View File

@ -1,3 +1,5 @@
ALTER TABLE `tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `id`; 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`; 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 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

View File

@ -1004,8 +1004,9 @@ public:
void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1); void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1);
void SendTaskFailed(int TaskID, int TaskIndex); void SendTaskFailed(int TaskID, int TaskIndex);
void SendTaskComplete(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 SaveTaskState() { return (taskmanager ? taskmanager->SaveClientState(this, taskstate) : false); }
inline bool IsTaskStateLoaded() { return taskstate != nullptr; } inline bool IsTaskStateLoaded() { return taskstate != nullptr; }
inline bool IsTaskActive(int TaskID) { return (taskstate ? taskstate->IsTaskActive(TaskID) : false); } inline bool IsTaskActive(int TaskID) { return (taskstate ? taskstate->IsTaskActive(TaskID) : false); }

View File

@ -4031,7 +4031,7 @@ void Client::Handle_OP_CancelTask(const EQApplicationPacket *app)
CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer;
if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) if (RuleB(TaskSystem, EnableTaskSystem) && taskstate)
taskstate->CancelTask(this, cts->SequenceNumber); taskstate->CancelTask(this, cts->SequenceNumber, static_cast<TaskType>(cts->type));
} }
void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app)

View File

@ -292,7 +292,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) {
Log(Logs::Detail, Logs::Tasks,"TaskManager::SaveClientState for character ID %d", characterID); 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; task<MAXACTIVEQUESTS; task++) { for(int task=0; task<MAXACTIVEQUESTS; task++) {
int taskID = state->ActiveQuests[task].TaskID; int taskID = state->ActiveQuests[task].TaskID;
if(taskID==TASKSLOTEMPTY) 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); 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) " std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, type, acceptedtime) "
"VALUES (%i, %i, %i, %i)", "VALUES (%i, %i, %i, %i, %i)",
characterID, taskID, task, state->ActiveQuests[task].AcceptedTime); characterID, taskID, task, static_cast<int>(Tasks[taskID]->type), state->ActiveQuests[task].AcceptedTime);
auto results = database.QueryDatabase(query); auto results = database.QueryDatabase(query);
if (!results.Success()) { if (!results.Success()) {
Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); 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; i<MAXACTIVEQUESTS; i++) if (state->ActiveTask.TaskID != TASKSLOTEMPTY)
if(state->ActiveQuests[i].TaskID != TASKSLOTEMPTY) state->UnlockActivities(characterID, state->ActiveTask);
state->UnlockActivities(characterID, i); // 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); Log(Logs::General, Logs::Tasks, "[CLIENTLOAD] LoadClientState for Character ID %d DONE!", characterID);
return true; return true;
@ -1313,10 +1316,14 @@ ClientTaskState::ClientTaskState() {
LastCompletedTaskLoaded = 0; LastCompletedTaskLoaded = 0;
CheckedTouchActivities = false; CheckedTouchActivities = false;
for(int i=0; i<MAXACTIVEQUESTS; i++) for (int i = 0; i < MAXACTIVEQUESTS; i++) {
ActiveQuests[i].slot = i;
ActiveQuests[i].TaskID = TASKSLOTEMPTY; ActiveQuests[i].TaskID = TASKSLOTEMPTY;
}
ActiveTask.slot = 0;
ActiveTask.TaskID = TASKSLOTEMPTY; ActiveTask.TaskID = TASKSLOTEMPTY;
// TODO: shared task
} }
ClientTaskState::~ClientTaskState() { ClientTaskState::~ClientTaskState() {
@ -1345,69 +1352,75 @@ static void DeleteCompletedTaskFromDatabase(int charID, int taskID) {
Log(Logs::General, Logs::Tasks, "[UPDATE] Delete query %s", query.c_str()); Log(Logs::General, Logs::Tasks, "[UPDATE] Delete query %s", query.c_str());
} }
bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { bool ClientTaskState::UnlockActivities(int CharID, ClientTaskInformation &task_info)
{
bool AllActivitiesComplete = true; bool AllActivitiesComplete = true;
TaskInformation* Task = taskmanager->Tasks[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 // On loading the client state, all activities that are not completed, are
// marked as hidden. For Sequential (non-stepped) mode, we mark the first // marked as hidden. For Sequential (non-stepped) mode, we mark the first
// activity as active if not complete. // activity as active if not complete.
Log(Logs::General, Logs::Tasks, "[UPDATE] CharID: %i Task: %i Sequence mode is %i", Log(Logs::General, Logs::Tasks, "[UPDATE] CharID: %i Task: %i Sequence mode is %i",
CharID, ActiveQuests[TaskIndex].TaskID, Task->SequenceMode); CharID, task_info.TaskID, Task->SequenceMode);
if(Task->SequenceMode == ActivitiesSequential) {
if(ActiveQuests[TaskIndex].Activity[0].State != ActivityCompleted) if (Task->SequenceMode == ActivitiesSequential) {
ActiveQuests[TaskIndex].Activity[0].State = ActivityActive; if (task_info.Activity[0].State != ActivityCompleted)
task_info.Activity[0].State = ActivityActive;
// Enable the next Hidden task. // Enable the next Hidden task.
for(int i=0; i<Task->ActivityCount; i++) { for (int i = 0; i < Task->ActivityCount; i++) {
if((ActiveQuests[TaskIndex].Activity[i].State == ActivityActive) && if ((task_info.Activity[i].State == ActivityActive) &&
(!Task->Activity[i].Optional)) { (!Task->Activity[i].Optional)) {
AllActivitiesComplete = false; AllActivitiesComplete = false;
break; 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; AllActivitiesComplete = false;
break; 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"); Log(Logs::General, Logs::Tasks, "[UPDATE] KeepOneRecord enabled");
auto Iterator = CompletedTasks.begin(); auto Iterator = CompletedTasks.begin();
int ErasedElements = 0; int ErasedElements = 0;
while(Iterator != CompletedTasks.end()) { while (Iterator != CompletedTasks.end()) {
int TaskID = (*Iterator).TaskID; int TaskID = (*Iterator).TaskID;
if(TaskID == ActiveQuests[TaskIndex].TaskID) { if (TaskID == task_info.TaskID) {
Iterator = CompletedTasks.erase(Iterator); Iterator = CompletedTasks.erase(Iterator);
ErasedElements++; ErasedElements++;
} } else
else
++Iterator; ++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; CompletedTaskInformation cti;
cti.TaskID = ActiveQuests[TaskIndex].TaskID; cti.TaskID = task_info.TaskID;
cti.CompletedTime = time(nullptr); cti.CompletedTime = time(nullptr);
for(int i=0; i<Task->ActivityCount; i++) for (int i = 0; i < Task->ActivityCount; i++)
cti.ActivityDone[i] = (ActiveQuests[TaskIndex].Activity[i].State == ActivityCompleted); cti.ActivityDone[i] = (task_info.Activity[i].State == ActivityCompleted);
CompletedTasks.push_back(cti); 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; return AllActivitiesComplete;
} }
@ -1416,66 +1429,69 @@ bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) {
bool CurrentStepComplete = true; 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 // 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 // client state. Unlock all activities with a step number of 0
if(ActiveQuests[TaskIndex].CurrentStep == -1) {
for(int i=0; i<Task->ActivityCount; i++) {
if((Task->Activity[i].StepNumber == 0) && if (task_info.CurrentStep == -1) {
(ActiveQuests[TaskIndex].Activity[i].State == ActivityHidden)) { for (int i = 0; i < Task->ActivityCount; i++) {
ActiveQuests[TaskIndex].Activity[i].State = ActivityActive; if (Task->Activity[i].StepNumber == 0 && task_info.Activity[i].State == ActivityHidden) {
//ActiveQuests[TaskIndex].Activity[i].Updated=true; 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 Step = task_info.CurrentStep; Step <= Task->LastStep; Step++) {
for(int Activity=0; Activity<Task->ActivityCount; Activity++) { for (int Activity = 0; Activity < Task->ActivityCount; Activity++) {
if(Task->Activity[Activity].StepNumber == (int)ActiveQuests[TaskIndex].CurrentStep) { if (Task->Activity[Activity].StepNumber == (int)task_info.CurrentStep) {
if((ActiveQuests[TaskIndex].Activity[Activity].State != ActivityCompleted) && if ((task_info.Activity[Activity].State != ActivityCompleted) &&
(!Task->Activity[Activity].Optional)) { (!Task->Activity[Activity].Optional)) {
CurrentStepComplete = false; CurrentStepComplete = false;
AllActivitiesComplete = false; AllActivitiesComplete = false;
break; break;
} }
} }
} }
if(!CurrentStepComplete) break; if (!CurrentStepComplete)
ActiveQuests[TaskIndex].CurrentStep++; break;
task_info.CurrentStep++;
} }
if(AllActivitiesComplete) { if (AllActivitiesComplete) {
if(RuleB(TaskSystem, RecordCompletedTasks)) { if (RuleB(TaskSystem, RecordCompletedTasks)) {
// If we are only keeping one completed record per task, and the player has done // 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. // 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"); Log(Logs::General, Logs::Tasks, "[UPDATE] KeepOneRecord enabled");
auto Iterator = CompletedTasks.begin(); auto Iterator = CompletedTasks.begin();
int ErasedElements = 0; int ErasedElements = 0;
while(Iterator != CompletedTasks.end()) {
while (Iterator != CompletedTasks.end()) {
int TaskID = (*Iterator).TaskID; int TaskID = (*Iterator).TaskID;
if(TaskID == ActiveQuests[TaskIndex].TaskID) { if (TaskID == task_info.TaskID) {
Iterator = CompletedTasks.erase(Iterator); Iterator = CompletedTasks.erase(Iterator);
ErasedElements++; ErasedElements++;
} } else
else
++Iterator; ++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; CompletedTaskInformation cti;
cti.TaskID = ActiveQuests[TaskIndex].TaskID; cti.TaskID = task_info.TaskID;
cti.CompletedTime = time(nullptr); cti.CompletedTime = time(nullptr);
for(int i=0; i<Task->ActivityCount; i++) for (int i = 0; i < Task->ActivityCount; i++)
cti.ActivityDone[i] = (ActiveQuests[TaskIndex].Activity[i].State == ActivityCompleted); cti.ActivityDone[i] = (task_info.Activity[i].State == ActivityCompleted);
CompletedTasks.push_back(cti); 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 // Mark all non-completed tasks in the current step as active
// //
for(int Activity=0; Activity<Task->ActivityCount; Activity++) { for (int Activity = 0; Activity < Task->ActivityCount; Activity++) {
if((Task->Activity[Activity].StepNumber == (int)ActiveQuests[TaskIndex].CurrentStep) && if ((Task->Activity[Activity].StepNumber == (int)task_info.CurrentStep) &&
(ActiveQuests[TaskIndex].Activity[Activity].State == ActivityHidden)) { (task_info.Activity[Activity].State == ActivityHidden)) {
ActiveQuests[TaskIndex].Activity[Activity].State = ActivityActive; task_info.Activity[Activity].State = ActivityActive;
ActiveQuests[TaskIndex].Activity[Activity].Updated=true; task_info.Activity[Activity].Updated = true;
} }
} }
return false; return false;
} }
void ClientTaskState::UpdateTasksOnKill(Client *c, int NPCTypeID) { void ClientTaskState::UpdateTasksOnKill(Client *c, int NPCTypeID) {
UpdateTasksByNPC(c, ActivityKill, NPCTypeID); UpdateTasksByNPC(c, ActivityKill, NPCTypeID);
@ -1869,12 +1883,12 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
// Flag the activity as complete // Flag the activity as complete
ActiveQuests[TaskIndex].Activity[ActivityID].State = ActivityCompleted; ActiveQuests[TaskIndex].Activity[ActivityID].State = ActivityCompleted;
// Unlock subsequent activities for this task // 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); Log(Logs::General, Logs::Tasks, "[UPDATE] TaskCompleted is %i", TaskComplete);
// and by the 'Task Stage Completed' message // and by the 'Task Stage Completed' message
c->SendTaskActivityComplete(ActiveQuests[TaskIndex].TaskID, ActivityID, TaskIndex); c->SendTaskActivityComplete(ActiveQuests[TaskIndex].TaskID, ActivityID, TaskIndex);
// Send the updated task/activity list to the client // 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 // 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()); 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); c->SendTaskActivityComplete(ActiveQuests[TaskIndex].TaskID, 0, TaskIndex, false);
taskmanager->SaveClientState(c, this); taskmanager->SaveClientState(c, this);
//c->SendTaskComplete(TaskIndex); //c->SendTaskComplete(TaskIndex);
c->CancelTask(TaskIndex); c->CancelTask(TaskIndex, TaskType::Quest); // TODO: fix
//if(Task->RewardMethod != METHODQUEST) RewardTask(c, Task); //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 // If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST
RewardTask(c, Task); RewardTask(c, Task);
@ -2058,7 +2072,7 @@ void ClientTaskState::FailTask(Client *c, int TaskID) {
if(ActiveQuests[i].TaskID==TaskID) { if(ActiveQuests[i].TaskID==TaskID) {
c->SendTaskFailed(ActiveQuests[i].TaskID, i); c->SendTaskFailed(ActiveQuests[i].TaskID, i);
// Remove the task from the client // Remove the task from the client
c->CancelTask(i); c->CancelTask(i, TaskType::Quest); // TODO: fix
return; return;
} }
@ -2282,7 +2296,7 @@ void ClientTaskState::TaskPeriodicChecks(Client *c) {
// Send Red Task Failed Message // Send Red Task Failed Message
c->SendTaskFailed(ActiveQuests[i].TaskID, i); c->SendTaskFailed(ActiveQuests[i].TaskID, i);
// Remove the task from the client // 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, // 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 // otherwise the player will not see all the failed messages where multiple
// tasks fail at the same time. // 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); auto outapp = new EQApplicationPacket(OP_TaskActivity, 25);
outapp->WriteUInt32(ClientTaskIndex); outapp->WriteUInt32(ClientTaskIndex);
outapp->WriteUInt32(2); outapp->WriteUInt32(static_cast<uint32>(Tasks[TaskID]->type));
outapp->WriteUInt32(TaskID); outapp->WriteUInt32(TaskID);
outapp->WriteUInt32(ActivityID); outapp->WriteUInt32(ActivityID);
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -2552,7 +2566,7 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i
tass = (TaskActivityShort_Struct*)outapp->pBuffer; tass = (TaskActivityShort_Struct*)outapp->pBuffer;
tass->TaskSequenceNumber = ClientTaskIndex; tass->TaskSequenceNumber = ClientTaskIndex;
tass->unknown2 = 0x00000002; tass->unknown2 = static_cast<uint32>(Tasks[TaskID]->type);
tass->TaskID = TaskID; tass->TaskID = TaskID;
tass->ActivityID = ActivityID; tass->ActivityID = ActivityID;
tass->unknown3 = 0x000000; tass->unknown3 = 0x000000;
@ -2669,7 +2683,7 @@ void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int
auto outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength); auto outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength);
outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber
outapp->WriteUInt32(2); // unknown2 outapp->WriteUInt32(static_cast<uint32>(Tasks[TaskID]->type)); // task type
outapp->WriteUInt32(TaskID); outapp->WriteUInt32(TaskID);
outapp->WriteUInt32(ActivityID); outapp->WriteUInt32(ActivityID);
outapp->WriteUInt32(0); // unknown3 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 // One of these unknown fields maybe related to the 'Use On' activity types
outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text1.c_str()); 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()); outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text2.c_str());
// Goal Count // 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; TaskIndex<c->GetActiveTaskCount(); TaskIndex++) { for (int TaskIndex=0; TaskIndex<MAXACTIVEQUESTS; TaskIndex++) {
for(int TaskIndex=0; TaskIndex<MAXACTIVEQUESTS; TaskIndex++) {
int TaskID = c->GetActiveTaskID(TaskIndex); int TaskID = c->GetActiveTaskID(TaskIndex);
if((TaskID==0) || (Tasks[TaskID] ==0)) continue; if((TaskID==0) || (Tasks[TaskID] ==0)) continue;
int StartTime = c->GetTaskStartTime(TaskIndex); 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)); Log(Logs::General, Logs::Tasks, "[UPDATE] SendActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID));
int Sequence = 0; 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 StartTime = task_info.AcceptedTime;
SendActiveTaskDescription(c, TaskID, task_info, StartTime, Tasks[TaskID]->Duration, BringUpTaskJournal);
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);
Log(Logs::General, Logs::Tasks, "[UPDATE] SendSingleActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID)); Log(Logs::General, Logs::Tasks, "[UPDATE] SendSingleActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID));
int Sequence = 0; for (int Activity = 0; Activity < GetActivityCount(TaskID); Activity++) {
if(task_info.Activity[Activity].State != ActivityHidden) {
for(int Activity=0; Activity<GetActivityCount(TaskID); Activity++) { Log(Logs::General, Logs::Tasks, "[UPDATE] Long: %i, %i Complete=%i", TaskID, Activity, TaskComplete);
if(c->GetTaskActivityState(TaskIndex, Activity) != ActivityHidden) { if (Activity == GetActivityCount(TaskID) - 1)
Log(Logs::General, Logs::Tasks, "[UPDATE] Long: %i, %i, %i Complete=%i", TaskID, Activity, TaskIndex, TaskComplete); SendTaskActivityLong(c, TaskID, Activity, task_info.slot,
if(Activity==GetActivityCount(TaskID)-1)
SendTaskActivityLong(c, TaskID, Activity, TaskIndex,
Tasks[TaskID]->Activity[Activity].Optional, TaskComplete); Tasks[TaskID]->Activity[Activity].Optional, TaskComplete);
else else
SendTaskActivityLong(c, TaskID, Activity, TaskIndex, SendTaskActivityLong(c, TaskID, Activity, task_info.slot,
Tasks[TaskID]->Activity[Activity].Optional, 0); 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]) if ((TaskID < 1) || (TaskID >= MAXTASKS) || !Tasks[TaskID])
return; return;
int PacketLength = sizeof(TaskDescriptionHeader_Struct) + Tasks[TaskID]->Title.size() + 1 int PacketLength = sizeof(TaskDescriptionHeader_Struct) + Tasks[TaskID]->Title.length() + 1
+ sizeof(TaskDescriptionData1_Struct) + Tasks[TaskID]->Description.size() + 1 + sizeof(TaskDescriptionData1_Struct) + Tasks[TaskID]->Description.length() + 1
+ sizeof(TaskDescriptionData2_Struct) + 1 + sizeof(TaskDescriptionTrailer_Struct); + 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 // 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. // 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 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; ItemID = Tasks[TaskID]->RewardID;
} } else if (Tasks[TaskID]->RewardMethod == METHODLIST) {
else if(Tasks[TaskID]->RewardMethod==METHODLIST) {
ItemID = GoalListManager.GetFirstEntry(Tasks[TaskID]->RewardID); ItemID = GoalListManager.GetFirstEntry(Tasks[TaskID]->RewardID);
if(ItemID < 0) if (ItemID < 0)
ItemID = 0; ItemID = 0;
} }
@ -2830,20 +2839,11 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN
linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetLinkType(EQEmu::saylink::SayLinkItemData);
linker.SetItemData(reward_item); linker.SetItemData(reward_item);
linker.SetTaskUse(); linker.SetTaskUse();
if (!Tasks[TaskID]->Reward.empty()) Tasks[TaskID]->item_link = linker.GenerateLink();
linker.SetProxyText(Tasks[TaskID]->Reward.c_str());
reward_text.append(linker.GenerateLink());
}
else {
reward_text.append(Tasks[TaskID]->Reward);
} }
}
} PacketLength += Tasks[TaskID]->Reward.length() + 1 + Tasks[TaskID]->item_link.length() + 1;
else {
reward_text.append(Tasks[TaskID]->Reward);
}
PacketLength += reward_text.length() + 1;
char *Ptr; char *Ptr;
TaskDescriptionHeader_Struct* tdh; TaskDescriptionHeader_Struct* tdh;
@ -2855,60 +2855,57 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN
tdh = (TaskDescriptionHeader_Struct*)outapp->pBuffer; tdh = (TaskDescriptionHeader_Struct*)outapp->pBuffer;
tdh->SequenceNumber = SequenceNumber; tdh->SequenceNumber = task_info.slot;
tdh->TaskID = TaskID; tdh->TaskID = TaskID;
tdh->open_window = BringUpTaskJournal;
if(BringUpTaskJournal) tdh->task_type = static_cast<uint32>(Tasks[TaskID]->type);
tdh->unknown2 = 0x00000201; tdh->reward_type = 0; // TODO: 4 says Radiant Crystals else Ebon Crystals when shared task
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;
Ptr = (char *) tdh + sizeof(TaskDescriptionHeader_Struct); Ptr = (char *) tdh + sizeof(TaskDescriptionHeader_Struct);
sprintf(Ptr, "%s", Tasks[TaskID]->Title.c_str()); sprintf(Ptr, "%s", Tasks[TaskID]->Title.c_str());
Ptr = Ptr + strlen(Ptr) + 1; Ptr += Tasks[TaskID]->Title.length() + 1;
tdd1 = (TaskDescriptionData1_Struct*)Ptr; tdd1 = (TaskDescriptionData1_Struct*)Ptr;
tdd1->Duration = Duration; tdd1->Duration = Duration;
tdd1->unknown2 = 0x00000000; tdd1->dur_code = static_cast<uint32>(Tasks[TaskID]->dur_code);
tdd1->StartTime = StartTime; tdd1->StartTime = StartTime;
Ptr = (char *) tdd1 + sizeof(TaskDescriptionData1_Struct); Ptr = (char *) tdd1 + sizeof(TaskDescriptionData1_Struct);
sprintf(Ptr, "%s", Tasks[TaskID]->Description.c_str()); sprintf(Ptr, "%s", Tasks[TaskID]->Description.c_str());
Ptr = Ptr + strlen(Ptr) + 1; Ptr += Tasks[TaskID]->Description.length() + 1;
tdd2 = (TaskDescriptionData2_Struct*)Ptr; 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 // we have this reward stuff!
// to include multiple item links has so far proven futile. Setting it to 0 ends the packet after the next byte. // if we ever don't hardcode this, TaskDescriptionTrailer_Struct will need to be fixed since
tdd2->RewardCount = 1; // "has_reward_selection" is after this bool! Smaller packet when this is 0
tdd2->has_rewards = 1;
if(Tasks[TaskID]->XPReward) tdd2->coin_reward = Tasks[TaskID]->CashReward;
tdd2->unknown1 = 0x00000100; tdd2->xp_reward = Tasks[TaskID]->XPReward ? 1 : 0; // just booled
else tdd2->unknown3 = 0; // STRONGLY suspect this is faction reward, also just booled to hide details?
tdd2->unknown1 = 0x00000000;
tdd2->unknown2 = 0x00000000;
tdd2->unknown3 = 0x0000;
Ptr = (char *) tdd2 + sizeof(TaskDescriptionData2_Struct); Ptr = (char *) tdd2 + sizeof(TaskDescriptionData2_Struct);
sprintf(Ptr, "%s", reward_text.c_str()); // we actually have 2 strings here. One is max length 96 and not parsed for item links
Ptr = Ptr + strlen(Ptr) + 1; // 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 = (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); c->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }
bool ClientTaskState::IsTaskActivityCompleted(int index, int ActivityID) { 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 // 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 // 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; i<MAXACTIVEQUESTS; i++) for(int i=0; i<MAXACTIVEQUESTS; i++)
if(ActiveQuests[i].TaskID != TASKSLOTEMPTY) { if(ActiveQuests[i].TaskID != TASKSLOTEMPTY) {
CancelTask(c, i, false); CancelTask(c, i, TaskType::Quest, false);
ActiveQuests[i].TaskID = TASKSLOTEMPTY; ActiveQuests[i].TaskID = TASKSLOTEMPTY;
} }
// TODO: shared
} }
void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromDB) {
void ClientTaskState::CancelTask(Client *c, int SequenceNumber, TaskType type, bool RemoveFromDB)
{
auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct));
CancelTask_Struct* cts = (CancelTask_Struct*)outapp->pBuffer; CancelTask_Struct* cts = (CancelTask_Struct*)outapp->pBuffer;
cts->SequenceNumber = SequenceNumber; cts->SequenceNumber = SequenceNumber;
cts->unknown4 = 0x00000002; cts->type = static_cast<uint32>(type);
Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask"); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask");
@ -2982,11 +2983,11 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromD
safe_delete(outapp); safe_delete(outapp);
if(RemoveFromDB) 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(); int characterID = c->CharacterID();
Log(Logs::General, Logs::Tasks, "[UPDATE] ClientTaskState Cancel Task %i ", sequenceNumber); 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()); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask: %s", query.c_str());
query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i", query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i AND type=%i",
characterID, ActiveQuests[sequenceNumber].TaskID); characterID, ActiveQuests[sequenceNumber].TaskID, static_cast<int>(type));
results = database.QueryDatabase(query); results = database.QueryDatabase(query);
if(!results.Success()) if(!results.Success())
Log(Logs::General, Logs::Error, "[TASKS] Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); 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()); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask: %s", query.c_str());
ActiveQuests[sequenceNumber].TaskID = TASKSLOTEMPTY; switch (type) {
ActiveTaskCount--; 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) { void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement)
{
if(!taskmanager || TaskID<0 || TaskID>=MAXTASKS) { if (!taskmanager || TaskID < 0 || TaskID >= MAXTASKS) {
c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID); c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID);
return; return;
} }
if(taskmanager->Tasks[TaskID] == nullptr) { auto task = taskmanager->Tasks[TaskID];
if (task == nullptr) {
c->Message(13, "Invalid TaskID %i", TaskID); c->Message(13, "Invalid TaskID %i", TaskID);
return; 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); c->Message(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVEQUESTS);
return; return;
} }
for(int i=0; i<MAXACTIVEQUESTS; i++) { // only Quests can have more than one, so don't need to check others
if(ActiveQuests[i].TaskID==TaskID) { if (task->type == TaskType::Quest) {
c->Message(13, "You have already been assigned this task."); for (int i = 0; i < MAXACTIVEQUESTS; i++) {
return; 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."); c->Message(13, "You are outside the level range of this task.");
return; 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 // 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 // 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 // resend all the active task data to the client when it cancels a task, but that could be construed as a
// waste of bandwidth. // waste of bandwidth.
// //
int FreeSlot = -1; ClientTaskInformation *active_slot = nullptr;
for(int i=0; i<MAXACTIVEQUESTS; i++) { switch (task->type) {
Log(Logs::General, Logs::Tasks, "[UPDATE] ClientTaskState Looking for free slot in slot %i, found TaskID of %i", case TaskType::Task:
i, ActiveQuests[i].TaskID); active_slot = &ActiveTask;
if(ActiveQuests[i].TaskID == 0) { break;
FreeSlot = i; case TaskType::Shared: // TODO: shared
break; 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 // 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); c->Message(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVEQUESTS);
return; return;
} }
active_slot->TaskID = TaskID;
active_slot->AcceptedTime = time(nullptr);
active_slot->Updated = true;
active_slot->CurrentStep = -1;
ActiveQuests[FreeSlot].TaskID = TaskID; for (int i = 0; i < taskmanager->Tasks[TaskID]->ActivityCount; i++) {
ActiveQuests[FreeSlot].AcceptedTime = time(nullptr); active_slot->Activity[i].ActivityID = i;
ActiveQuests[FreeSlot].Updated = true; active_slot->Activity[i].DoneCount = 0;
ActiveQuests[FreeSlot].CurrentStep = -1; active_slot->Activity[i].State = ActivityHidden;
active_slot->Activity[i].Updated = true;
for(int i=0; i<taskmanager->Tasks[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;
} }
UnlockActivities(c->CharacterID(), FreeSlot);
ActiveTaskCount++; UnlockActivities(c->CharacterID(), *active_slot);
taskmanager->SendSingleActiveTaskToClient(c, FreeSlot, false, true);
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()); c->Message(0, "You have been assigned the task '%s'.", taskmanager->Tasks[TaskID]->Title.c_str());
char *buf = 0; std::string buf = std::to_string(TaskID);
MakeAnyLenString(&buf, "%d", TaskID);
NPC *npc = entity_list.GetID(NPCID)->CastToNPC(); NPC *npc = entity_list.GetID(NPCID)->CastToNPC();
if(!npc) { if(!npc) {
c->Message(clientMessageYellow, "Task Giver ID is %i", NPCID); c->Message(clientMessageYellow, "Task Giver ID is %i", NPCID);
c->Message(clientMessageError, "Unable to find NPC to send EVENT_TASKACCEPTED to. Report this bug."); c->Message(clientMessageError, "Unable to find NPC to send EVENT_TASKACCEPTED to. Report this bug.");
safe_delete_array(buf);
return; 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) { void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) {

View File

@ -136,6 +136,7 @@ struct TaskInformation {
std::string Title; // max length 64 std::string Title; // max length 64
std::string Description; // max length 4000, 2048 on Tit std::string Description; // max length 4000, 2048 on Tit
std::string Reward; std::string Reward;
std::string item_link; // max length 128 older clients, item link gets own string
int RewardID; int RewardID;
int CashReward; // Expressed in copper int CashReward; // Expressed in copper
int XPReward; int XPReward;
@ -165,6 +166,7 @@ struct ClientActivityInformation {
}; };
struct ClientTaskInformation { struct ClientTaskInformation {
int slot; // intrusive, but makes things easier :P
int TaskID; int TaskID;
int CurrentStep; int CurrentStep;
int AcceptedTime; int AcceptedTime;
@ -199,9 +201,9 @@ public:
ActivityState GetTaskActivityState(int index, int ActivityID); ActivityState GetTaskActivityState(int index, int ActivityID);
void UpdateTaskActivity(Client *c, int TaskID, int ActivityID, int Count, bool ignore_quest_update = false); void UpdateTaskActivity(Client *c, int TaskID, int ActivityID, int Count, bool ignore_quest_update = false);
void ResetTaskActivity(Client *c, int TaskID, int ActivityID); 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 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); bool UpdateTasksByNPC(Client *c, int ActivityType, int NPCTypeID);
void UpdateTasksOnKill(Client *c, int NPCTypeID); void UpdateTasksOnKill(Client *c, int NPCTypeID);
void UpdateTasksForItem(Client *c, ActivityType Type, int ItemID, int Count=1); void UpdateTasksForItem(Client *c, ActivityType Type, int ItemID, int Count=1);
@ -229,7 +231,7 @@ public:
friend class TaskManager; friend class TaskManager;
private: 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); void IncrementDoneCount(Client *c, TaskInformation *Task, int TaskIndex, int ActivityID, int Count = 1, bool ignore_quest_update = false);
int ActiveTaskCount; int ActiveTaskCount;
ClientTaskInformation ActiveTask; // only one ClientTaskInformation ActiveTask; // only one
@ -263,7 +265,7 @@ public:
void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID); 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 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 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 SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex);
void SendTaskActivityLong(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, void SendTaskActivityLong(Client *c, int TaskID, int ActivityID, int ClientTaskIndex,
bool Optional, bool TaskComplete=false); bool Optional, bool TaskComplete=false);
@ -283,7 +285,7 @@ private:
TaskProximityManager ProximityManager; TaskProximityManager ProximityManager;
TaskInformation* Tasks[MAXTASKS]; TaskInformation* Tasks[MAXTASKS];
std::vector<int> TaskSets[MAXTASKSETS]; std::vector<int> 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);
}; };