|
|
|
|
@ -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; task<MAXACTIVEQUESTS; task++) {
|
|
|
|
|
int taskID = state->ActiveQuests[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<int>(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; i<MAXACTIVEQUESTS; i++)
|
|
|
|
|
if(state->ActiveQuests[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; i<MAXACTIVEQUESTS; i++)
|
|
|
|
|
for (int i = 0; i < MAXACTIVEQUESTS; i++) {
|
|
|
|
|
ActiveQuests[i].slot = i;
|
|
|
|
|
ActiveQuests[i].TaskID = TASKSLOTEMPTY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActiveTask.slot = 0;
|
|
|
|
|
ActiveTask.TaskID = TASKSLOTEMPTY;
|
|
|
|
|
// TODO: shared task
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) {
|
|
|
|
|
|
|
|
|
|
bool ClientTaskState::UnlockActivities(int CharID, ClientTaskInformation &task_info)
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
// 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; i<Task->ActivityCount; 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; i<Task->ActivityCount; 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; i<Task->ActivityCount; 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; Activity<Task->ActivityCount; 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; i<Task->ActivityCount; 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; Activity<Task->ActivityCount; 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<uint32>(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<uint32>(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<uint32>(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; TaskIndex<c->GetActiveTaskCount(); TaskIndex++) {
|
|
|
|
|
for(int TaskIndex=0; TaskIndex<MAXACTIVEQUESTS; TaskIndex++) {
|
|
|
|
|
for (int TaskIndex=0; TaskIndex<MAXACTIVEQUESTS; TaskIndex++) {
|
|
|
|
|
int TaskID = c->GetActiveTaskID(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; Activity<GetActivityCount(TaskID); Activity++) {
|
|
|
|
|
if(c->GetTaskActivityState(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<uint32>(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<uint32>(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; i<MAXACTIVEQUESTS; i++)
|
|
|
|
|
if(ActiveQuests[i].TaskID != TASKSLOTEMPTY) {
|
|
|
|
|
CancelTask(c, i, false);
|
|
|
|
|
CancelTask(c, i, TaskType::Quest, false);
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
CancelTask_Struct* cts = (CancelTask_Struct*)outapp->pBuffer;
|
|
|
|
|
cts->SequenceNumber = SequenceNumber;
|
|
|
|
|
cts->unknown4 = 0x00000002;
|
|
|
|
|
cts->type = static_cast<uint32>(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<int>(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; i<MAXACTIVEQUESTS; i++) {
|
|
|
|
|
if(ActiveQuests[i].TaskID==TaskID) {
|
|
|
|
|
c->Message(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; 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) {
|
|
|
|
|
FreeSlot = i;
|
|
|
|
|
break;
|
|
|
|
|
ClientTaskInformation *active_slot = nullptr;
|
|
|
|
|
switch (task->type) {
|
|
|
|
|
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; 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;
|
|
|
|
|
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) {
|
|
|
|
|
|