diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8a9d2c673..f3171d185 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -61,6 +61,7 @@ SET(common_sources rdtsc.cpp rulesys.cpp say_link.cpp + serialize_buffer.cpp serverinfo.cpp shareddb.cpp skills.cpp @@ -192,6 +193,7 @@ SET(common_headers ruletypes.h say_link.h seperator.h + serialize_buffer.h serverinfo.h servertalk.h shareddb.h diff --git a/common/base_packet.h b/common/base_packet.h index bdd774aa2..8d64e6d62 100644 --- a/common/base_packet.h +++ b/common/base_packet.h @@ -63,6 +63,8 @@ public: void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); } void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); } void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } + // this is used in task system a lot, it is NOT null-termed + void WriteLengthString(uint32 len, const char *str) { *(uint32 *)(pBuffer + _wpos) = len; _wpos += sizeof(uint32); memcpy(pBuffer + _wpos, str, len); _wpos += len; } void WriteData(const void *ptr, size_t n) { memcpy(pBuffer + _wpos, ptr, n); _wpos += n; } uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } diff --git a/common/eq_packet.h b/common/eq_packet.h index 1fea3b9d1..29588cbc1 100644 --- a/common/eq_packet.h +++ b/common/eq_packet.h @@ -20,6 +20,7 @@ #include "base_packet.h" #include "platform.h" +#include "serialize_buffer.h" #include #ifdef STATIC_OPCODE diff --git a/common/serialize_buffer.cpp b/common/serialize_buffer.cpp new file mode 100644 index 000000000..065bd9f2d --- /dev/null +++ b/common/serialize_buffer.cpp @@ -0,0 +1,23 @@ +#include "serialize_buffer.h" + +void SerializeBuffer::Grow(size_t new_size) +{ + assert(new_size > m_capacity); + auto new_buffer = new unsigned char[new_size * 2]; + memset(new_buffer, 0, new_size * 2); + + if (m_buffer) + memcpy(new_buffer, m_buffer, m_capacity); + m_capacity = new_size * 2; + delete[] m_buffer; + m_buffer = new_buffer; +} + +void SerializeBuffer::Reset() +{ + delete[] m_buffer; + m_buffer = nullptr; + m_capacity = 0; + m_pos = 0; +} + diff --git a/common/serialize_buffer.h b/common/serialize_buffer.h new file mode 100644 index 000000000..665a76d3a --- /dev/null +++ b/common/serialize_buffer.h @@ -0,0 +1,186 @@ +#ifndef SERIALIZE_BUFFER_H +#define SERIALIZE_BUFFER_H + +#include +#include +#include +#include + +class SerializeBuffer +{ +public: + SerializeBuffer() : m_buffer(nullptr), m_capacity(0), m_pos(0) {} + + explicit SerializeBuffer(size_t size) : m_capacity(size), m_pos(0) + { + m_buffer = new unsigned char[size]; + memset(m_buffer, 0, size); + } + + SerializeBuffer(const SerializeBuffer &rhs) + : m_buffer(new unsigned char[rhs.m_capacity]), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos) + { + memcpy(m_buffer, rhs.m_buffer, rhs.m_capacity); + } + + SerializeBuffer &operator=(const SerializeBuffer &rhs) + { + if (this != &rhs) { + delete[] m_buffer; + m_buffer = new unsigned char[rhs.m_capacity]; + m_capacity = rhs.m_capacity; + m_pos = rhs.m_pos; + memcpy(m_buffer, rhs.m_buffer, m_capacity); + } + return *this; + } + + SerializeBuffer(SerializeBuffer &&rhs) : m_buffer(rhs.m_buffer), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos) + { + rhs.m_buffer = nullptr; + rhs.m_capacity = 0; + rhs.m_pos = 0; + } + + SerializeBuffer &operator=(SerializeBuffer &&rhs) + { + if (this != &rhs) { + delete[] m_buffer; + + m_buffer = rhs.m_buffer; + m_capacity = rhs.m_capacity; + m_pos = rhs.m_pos; + + rhs.m_buffer = nullptr; + rhs.m_capacity = 0; + rhs.m_pos = 0; + } + return *this; + } + + ~SerializeBuffer() { delete[] m_buffer; } + + void WriteUInt8(uint8_t v) + { + if (m_pos + sizeof(uint8_t) > m_capacity) + Grow(m_capacity + sizeof(uint8_t)); + *(uint8_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(uint8_t); + } + + void WriteUInt16(uint16_t v) + { + if (m_pos + sizeof(uint16_t) > m_capacity) + Grow(m_capacity + sizeof(uint16_t)); + *(uint16_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(uint16_t); + } + + void WriteUInt32(uint32_t v) + { + if (m_pos + sizeof(uint32_t) > m_capacity) + Grow(m_capacity + sizeof(uint32_t)); + *(uint32_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(uint32_t); + } + + void WriteUInt64(uint64_t v) + { + if (m_pos + sizeof(uint64_t) > m_capacity) + Grow(m_capacity + sizeof(uint64_t)); + *(uint64_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(uint64_t); + } + + void WriteInt8(int8_t v) + { + if (m_pos + sizeof(int8_t) > m_capacity) + Grow(m_capacity + sizeof(int8_t)); + *(int8_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(int8_t); + } + + void WriteInt16(int16_t v) + { + if (m_pos + sizeof(int16_t) > m_capacity) + Grow(m_capacity + sizeof(int16_t)); + *(int16_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(int16_t); + } + + void WriteInt32(int32_t v) + { + if (m_pos + sizeof(int32_t) > m_capacity) + Grow(m_capacity + sizeof(int32_t)); + *(int32_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(int32_t); + } + + void WriteInt64(int64_t v) + { + if (m_pos + sizeof(int64_t) > m_capacity) + Grow(m_capacity + sizeof(int64_t)); + *(int64_t *)(m_buffer + m_pos) = v; + m_pos += sizeof(int64_t); + } + + void WriteFloat(float v) + { + if (m_pos + sizeof(float) > m_capacity) + Grow(m_capacity + sizeof(float)); + *(float *)(m_buffer + m_pos) = v; + m_pos += sizeof(float); + } + + void WriteDouble(double v) + { + if (m_pos + sizeof(double) > m_capacity) + Grow(m_capacity + sizeof(double)); + *(double *)(m_buffer + m_pos) = v; + m_pos += sizeof(double); + } + + void WriteString(const char *str) + { + assert(str != nullptr); + auto len = strlen(str) + 1; + if (m_pos + len > m_capacity) + Grow(m_capacity + len); + memcpy(m_buffer + m_pos, str, len); + m_pos += len; + } + + void WriteString(const std::string &str) + { + auto len = str.length() + 1; + if (m_pos + len > m_capacity) + Grow(m_capacity + len); + memcpy(m_buffer + m_pos, str.c_str(), len); + m_pos += len; + } + + void WriteLengthString(uint32_t len, const char *str) + { + assert(str != nullptr); + if (m_pos + len + sizeof(uint32_t) > m_capacity) + Grow(m_capacity + len + sizeof(uint32_t)); + *(uint32_t *)(m_buffer + m_pos) = len; + m_pos += sizeof(uint32_t); + memcpy(m_buffer + m_pos, str, len); + m_pos += len; + } + + size_t size() const { return m_pos; } + size_t length() const { return size(); } + size_t capacity() const { return m_capacity; } + const unsigned char *buffer() const { return m_buffer; } + +private: + void Grow(size_t new_size); + void Reset(); + unsigned char *m_buffer; + size_t m_capacity; + size_t m_pos; +}; + +#endif /* !SERIALIZE_BUFFER_H */ diff --git a/utils/sql/git/required/tasks_revamp.sql b/utils/sql/git/required/tasks_revamp.sql index 4e1452fcc..bd751e12c 100644 --- a/utils/sql/git/required/tasks_revamp.sql +++ b/utils/sql/git/required/tasks_revamp.sql @@ -3,3 +3,17 @@ ALTER TABLE `tasks` ADD `duration_code` TINYINT NOT NULL DEFAULT '0' AFTER `dura 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 +ALTER TABLE `activities` ADD `target_name` VARCHAR(64) NOT NULL DEFAULT '' AFTER `activitytype`; +ALTER TABLE `activities` ADD `item_list` VARCHAR(128) NOT NULL DEFAULT '' AFTER `target_name`; +ALTER TABLE `activities` ADD `skill_list` VARCHAR(64) NOT NULL DEFAULT '-1' AFTER `item_list`; +ALTER TABLE `activities` ADD `spell_list` VARCHAR(64) NOT NULL DEFAULT '0' AFTER `skill_list`; +ALTER TABLE `activities` ADD `description_override` VARCHAR(128) NOT NULL DEFAULT '' AFTER `spell_list`; +ALTER TABLE `activities` ADD `zones` VARCHAR(64) NOT NULL DEFAULT '' AFTER `zoneid`; +UPDATE `activities` SET `description_override` = `text3`; +UPDATE `activities` SET `target_name` = `text1`; +UPDATE `activities` SET `item_list` = `text2`; +UPDATE `activities` SET `zones` = `zoneid`; -- should be safe for us ... +ALTER TABLE `activities` DROP COLUMN `text1`; +ALTER TABLE `activities` DROP COLUMN `text2`; +ALTER TABLE `activities` DROP COLUMN `text3`; +ALTER TABLE `activities` DROP COLUMN `zoneid`; diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 15458c22e..9e82b1db5 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -172,21 +172,19 @@ bool TaskManager::LoadTasks(int singleTask) } if (singleTask == 0) - query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " - "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " - "`goalcount`, `delivertonpc`, `zoneid`, `optional` " - "FROM `activities` " - "WHERE `taskid` < %i AND `activityid` < %i " - "ORDER BY taskid, activityid ASC", - MAXTASKS, MAXACTIVITIESPERTASK); + query = + StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, " + "`skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zones`, `optional` FROM `activities` WHERE `taskid` < " + "%i AND `activityid` < %i ORDER BY taskid, activityid ASC", + MAXTASKS, MAXACTIVITIESPERTASK); else - query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " - "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " - "`goalcount`, `delivertonpc`, `zoneid`, `optional` " - "FROM `activities` " - "WHERE `taskid` = %i AND `activityid` < %i " - "ORDER BY taskid, activityid ASC", - singleTask, MAXACTIVITIESPERTASK); + query = + StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, `target_name`, `item_list`, " + "`skill_list`, `spell_list`, `description_override`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zones`, `optional` FROM `activities` WHERE `taskid` = " + "%i AND `activityid` < %i ORDER BY taskid, activityid ASC", + singleTask, MAXACTIVITIESPERTASK); results = database.QueryDatabase(query); if (!results.Success()) { Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); @@ -237,38 +235,42 @@ bool TaskManager::LoadTasks(int singleTask) Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type = atoi(row[3]); - if (row[4][0]) - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1 = row[4]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].target_name = row[4]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].item_list = row[5]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_list = row[6]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_id = atoi(row[6]); // for older clients + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_list = row[7]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_id = atoi(row[7]); // for older clients + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].desc_override = row[8]; - if (row[5][0]) - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2 = row[5]; - - if (row[6][0]) - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3 = row[6]; - - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[7]); - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[9]); - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[10]); - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[11]); - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[12]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[9]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[10]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[11]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[12]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].zones = row[13]; + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[13]); // for older clients + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[14]); Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " - "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", + "GoalMethod: %i, GoalCount: %3i, Zones:%s", Tasks[taskID]->ActivityCount, activityID, taskID, Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type, Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID, Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod, Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount, - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].zones.c_str()); - Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text1: %s", - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1.c_str()); - Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text2: %s", - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2.c_str()); - Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Text3: %s", - Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3.c_str()); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] target_name: %s", + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].target_name.c_str()); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] item_list: %s", + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].item_list.c_str()); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] skill_list: %s", + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].skill_list.c_str()); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] spell_list: %s", + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].spell_list.c_str()); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] description_override: %s", + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].desc_override.c_str()); Tasks[taskID]->ActivityCount++; } @@ -1155,110 +1157,88 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task } -void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList) { - +void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList) +{ Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSelector for %i Tasks", TaskCount); int PlayerLevel = c->GetLevel(); // Check if any of the tasks exist - for(int i=0; iIsTaskActive(TaskList[i])) continue; - - if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + for (int i = 0; i < TaskCount; i++) { + if (!AppropriateLevel(TaskList[i], PlayerLevel)) + continue; + if (c->IsTaskActive(TaskList[i])) + continue; + if (!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) + continue; ValidTasks++; - - PacketLength += 21; // Task Data - strings - PacketLength += Tasks[TaskList[i]]->Title.size() + 1 + - Tasks[TaskList[i]]->Description.size() + 1; - - sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone); - /* - PacketLength += strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text1) + 1 + - strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text2) + - strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text3) + 1 + - strlen(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)) + 1 + - 3 + 3 + 5; // Other strings (Hard set for now) - */ - PacketLength += 11 + 11 + 11 + 3 + 3 + (strlen(StartZone) * 2) + 2; // Other strings (Hard set for now) - PacketLength += 28; // Activity Data - strings (Hard set for 1 activity per task for now) } - if(ValidTasks == 0) return; + if (ValidTasks == 0) + return; - auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, PacketLength); + SerializeBuffer buf(50 * ValidTasks); - outapp->WriteUInt32(ValidTasks); // TaskCount - outapp->WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? + buf.WriteUInt32(ValidTasks); // TaskCount + buf.WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? -- should fix maybe some day, but we let more than 1 type through :P // so I guess an NPC can only offer one type of quests or we can only open a selection with one type :P (so quest call can tell us I guess) // this is also sent in OP_TaskDescription - outapp->WriteUInt32(mob->GetID()); // TaskGiver + buf.WriteUInt32(mob->GetID()); // TaskGiver - for(int i=0; iIsTaskActive(TaskList[i])) + continue; + if (!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) + continue; - if(!AppropriateLevel(TaskList[i], PlayerLevel)) continue; + buf.WriteUInt32(TaskList[i]); // TaskID + buf.WriteFloat(1.0f); // affects color, difficulty? + buf.WriteUInt32(Tasks[TaskList[i]]->Duration); + buf.WriteUInt32(static_cast(Tasks[TaskList[i]]->dur_code)); // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited - if(c->IsTaskActive(TaskList[i])) continue; + buf.WriteString(Tasks[TaskList[i]]->Title.c_str()); // max 64 with null + buf.WriteString(Tasks[TaskList[i]]->Description.c_str()); // max 4000 with null - if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + buf.WriteUInt8(0); // Has reward set flag + buf.WriteUInt32(Tasks[TaskList[i]]->ActivityCount); // ActivityCount - outapp->WriteUInt32(TaskList[i]); // TaskID - outapp->WriteFloat(1.0f); // affects color, difficulty? - outapp->WriteUInt32(Tasks[TaskList[i]]->Duration); - outapp->WriteUInt32(static_cast(Tasks[TaskList[i]]->dur_code)); // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited + for (int j = 0; j < Tasks[TaskList[i]]->ActivityCount; ++j) { + buf.WriteUInt32(j); // ActivityNumber + auto &activity = Tasks[TaskList[i]]->Activity[j]; + buf.WriteUInt32(activity.Type); // ActivityType + buf.WriteUInt32(0); // solo, group, raid? + buf.WriteString(activity.target_name); // max length 64, "target name" so like loot x foo from bar (this is bar) - outapp->WriteString(Tasks[TaskList[i]]->Title.c_str()); // max 64 with null - outapp->WriteString(Tasks[TaskList[i]]->Description.c_str()); // max 4000 with null + // this string is item names + buf.WriteLengthString(activity.item_list.length(), activity.item_list.c_str()); - outapp->WriteUInt8(0); // Has reward set flag - outapp->WriteUInt32(1); // ActivityCount - Hard set to 1 for now + buf.WriteUInt32(activity.GoalCount); // GoalCount - // Activity stuff below - Will need to iterate through each task - // Currently hard set for testing + // this string is skill IDs? probably one of the "use on" tasks + buf.WriteLengthString(activity.skill_list.length(), activity.skill_list.c_str()); + // this string is spell IDs? probably one of the "use on" tasks + buf.WriteLengthString(activity.spell_list.length(), activity.spell_list.c_str()); - sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone); - - outapp->WriteUInt32(0); // ActivityNumber - outapp->WriteUInt32(1); // ActivityType - outapp->WriteUInt32(0); // solo, group, raid? - outapp->WriteString("Text1 Test"); // max length 64 -- affects what string ID it will use for the description, should be a name here? - - // this string is item names - outapp->WriteUInt32(11); // Text2Len - outapp->WriteString("Text2 Test"); - - outapp->WriteUInt32(1); // GoalCount - - // this string is skill IDs? probably one of the "use on" tasks - outapp->WriteUInt32(3); // NumString1Len - outapp->WriteString("-1"); - - // this string is spell IDs? probably one of the "use on" tasks - outapp->WriteUInt32(3); // NumString2Len - outapp->WriteString("-1"); - - //outapp->WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)); - outapp->WriteString(StartZone); // Zone number in ascii max length 64, can be multiple with separated by ; - outapp->WriteString("Text3 Test"); // max length 128 -- overrides the automatic descriptions - outapp->WriteString(StartZone); // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used + //buf.WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)); + buf.WriteString(activity.zones); // Zone number in ascii max length 64, can be multiple with separated by ; + buf.WriteString(activity.desc_override); // max length 128 -- overrides the automatic descriptions + // this doesn't appear to be shown to the client at all and isn't the same as zones ... defaults to '0' though + buf.WriteString(activity.zones); // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used + } } + auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf.buffer(), buf.length()); + c->QueuePacket(outapp); safe_delete(outapp); @@ -2609,12 +2589,12 @@ void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, in tah->ActivityID = ActivityID; tah->unknown3 = 0x00000000; - // We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes + // We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes // no difference to the client. All activity updates will be done based on our interal activity types. if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100) tah->ActivityType = Tasks[TaskID]->Activity[ActivityID].Type; else - tah->ActivityType = ActivityUse1; + tah->ActivityType = ActivityCastOn; tah->Optional = Optional; tah->unknown5 = 0x00000000; @@ -2667,77 +2647,59 @@ void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, in } // Used only by RoF+ Clients -void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete) { +void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete) +{ + SerializeBuffer buf(100); - uint32 String2Len = 3; - if(TaskComplete) - String2Len = 4; + buf.WriteUInt32(ClientTaskIndex); // TaskSequenceNumber + buf.WriteUInt32(static_cast(Tasks[TaskID]->type)); // task type + buf.WriteUInt32(TaskID); + buf.WriteUInt32(ActivityID); + buf.WriteUInt32(0); // unknown3 - long PacketLength = 29 + 4 + 8 + 4 + 4 + 5; - PacketLength = PacketLength + Tasks[TaskID]->Activity[ActivityID].Text1.size() + 1 + - Tasks[TaskID]->Activity[ActivityID].Text2.size() + 1 + - Tasks[TaskID]->Activity[ActivityID].Text3.size() + 1 + - ((strlen(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)) + 1) * 2) + - 3 + String2Len; - - auto outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength); - - outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber - outapp->WriteUInt32(static_cast(Tasks[TaskID]->type)); // task type - outapp->WriteUInt32(TaskID); - outapp->WriteUInt32(ActivityID); - outapp->WriteUInt32(0); // unknown3 - - // We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes + // We send our 'internal' types as ActivityCastOn. text3 should be set to the activity description, so it makes // no difference to the client. All activity updates will be done based on our interal activity types. if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100) - outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Type); + buf.WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Type); else - outapp->WriteUInt32(ActivityUse1); + buf.WriteUInt32(ActivityCastOn); // w/e! - outapp->WriteUInt32(Optional); - outapp->WriteUInt8(0); // unknown5 + buf.WriteUInt8(Optional); + buf.WriteUInt32(0); // solo, group, raid // One of these unknown fields maybe related to the 'Use On' activity types - outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text1.c_str()); + buf.WriteString(Tasks[TaskID]->Activity[ActivityID].target_name); // target name string - outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Text2.length() + 1); // String Length - Add in null terminator - outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text2.c_str()); + buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].item_list.length(), Tasks[TaskID]->Activity[ActivityID].item_list.c_str()); // item name list // Goal Count if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) - outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].GoalCount); + buf.WriteUInt32(Tasks[TaskID]->Activity[ActivityID].GoalCount); else - outapp->WriteUInt32(1); // GoalCount + buf.WriteUInt32(1); // GoalCount - outapp->WriteUInt32(3); // String Length - Add in null terminator - outapp->WriteString("-1"); + // skill ID list ; separated + buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].skill_list.length(), Tasks[TaskID]->Activity[ActivityID].skill_list.c_str()); - if(!TaskComplete) { - outapp->WriteUInt32(3); // String Length - Add in null terminator - outapp->WriteString("-1"); - } - else - { - outapp->WriteUInt32(4); // String Length - Add in null terminator - outapp->WriteString("-54"); - } + // spelll ID list ; separated -- unsure wtf we're doing here + buf.WriteLengthString(Tasks[TaskID]->Activity[ActivityID].spell_list.length(), Tasks[TaskID]->Activity[ActivityID].spell_list.c_str()); - outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)); - outapp->WriteUInt32(0); // unknown7 + buf.WriteString(Tasks[TaskID]->Activity[ActivityID].zones); + buf.WriteUInt32(0); // unknown7 - outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text3.c_str()); + buf.WriteString(Tasks[TaskID]->Activity[ActivityID].desc_override); // description override if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) - outapp->WriteUInt32(c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID)); // DoneCount + buf.WriteUInt32(c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID)); // DoneCount else // For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise. - outapp->WriteUInt32((c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount)); + buf.WriteUInt32((c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount)); - outapp->WriteUInt8(1); // unknown9 + buf.WriteUInt8(1); // unknown9 - outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)); + buf.WriteString(Tasks[TaskID]->Activity[ActivityID].zones); + auto outapp = new EQApplicationPacket(OP_TaskActivity, buf.buffer(), buf.length()); c->QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/tasks.h b/zone/tasks.h index 5fd074300..01c08f4a1 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -105,11 +105,19 @@ struct ActivityInformation { std::string Text1; std::string Text2; std::string Text3; + std::string target_name; // name mob, location -- default empty + std::string item_list; // likely defaults to empty + std::string skill_list; // IDs ; separated -- default -1 + std::string spell_list; // IDs ; separated -- default 0 + std::string desc_override; // overrides auto generated description -- default empty + int skill_id; // older clients, first id from above + int spell_id; // older clients, first id from above int GoalID; TaskMethodType GoalMethod; int GoalCount; int DeliverToNPC; int ZoneID; + std::string zones; // IDs ; searated, ZoneID is the first in this list for older clients -- default empty string bool Optional; }; @@ -154,7 +162,7 @@ struct TaskInformation { typedef enum { ActivityHidden = 0, ActivityActive = 1, ActivityCompleted = 2 } ActivityState; typedef enum { ActivityDeliver = 1, ActivityKill = 2, ActivityLoot = 3, ActivitySpeakWith = 4, ActivityExplore = 5, - ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityUse1 = 9, ActivityUse2 = 10, + ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityCastOn = 9, ActivitySkillOn = 10, ActivityTouch = 11, ActivityCollect = 13, ActivityGiveCash = 100 } ActivityType;