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

View File

@ -3368,13 +3368,16 @@ namespace RoF
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToRoFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];

View File

@ -3437,13 +3437,16 @@ namespace RoF2
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToRoF2SayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];

View File

@ -2249,13 +2249,16 @@ namespace SoD
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToSoDSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];

View File

@ -1879,13 +1879,16 @@ namespace SoF
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToSoFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];

View File

@ -1459,13 +1459,16 @@ namespace Titanium
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToTitaniumSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) +
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct) +
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];
@ -1479,6 +1482,7 @@ namespace Titanium
InBuffer += strlen(InBuffer) + 1;
memcpy(OutBuffer, InBuffer, sizeof(TaskDescriptionTrailer_Struct));
// we have an extra DWORD in the trailer struct, client should ignore it so w/e
delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req);

View File

@ -2528,13 +2528,16 @@ namespace UF
InBuffer += description_size;
InBuffer += sizeof(TaskDescriptionData2_Struct);
std::string old_message = InBuffer; // start 'Reward' as string
uint32 reward_size = strlen(InBuffer) + 1;
InBuffer += reward_size;
std::string old_message = InBuffer; // start item link string
std::string new_message;
ServerToUFSayLink(new_message, old_message);
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
title_size + description_size + new_message.length() + 1;
title_size + description_size + reward_size + new_message.length() + 1;
in->pBuffer = new unsigned char[in->size];

View File

@ -1,3 +1,5 @@
ALTER TABLE `tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `tasks` ADD `duration_code` TINYINT NOT NULL DEFAULT '0' AFTER `duration`;
UPDATE `tasks` SET `type` = '2'; -- we were treating them all as quests
ALTER TABLE `character_tasks` ADD `type` TINYINT NOT NULL DEFAULT '0' AFTER `slot`;
UPDATE `character_tasks` SET `type` = '2'; -- we were treating them all as quests

View File

@ -1004,8 +1004,9 @@ public:
void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1);
void SendTaskFailed(int TaskID, int TaskIndex);
void SendTaskComplete(int TaskIndex);
inline ClientTaskState *GetTaskState() const { return taskstate; }
inline void CancelTask(int TaskIndex) { if(taskstate) taskstate->CancelTask(this, TaskIndex); }
inline void CancelTask(int TaskIndex, TaskType type) { if(taskstate) taskstate->CancelTask(this, TaskIndex, type); }
inline bool SaveTaskState() { return (taskmanager ? taskmanager->SaveClientState(this, taskstate) : false); }
inline bool IsTaskStateLoaded() { return taskstate != nullptr; }
inline bool IsTaskActive(int TaskID) { return (taskstate ? taskstate->IsTaskActive(TaskID) : false); }

View File

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

View File

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