diff --git a/common/repositories/base/base_task_activities_repository.h b/common/repositories/base/base_task_activities_repository.h index 3856aae17..c908c25a9 100644 --- a/common/repositories/base/base_task_activities_repository.h +++ b/common/repositories/base/base_task_activities_repository.h @@ -21,6 +21,7 @@ public: struct TaskActivities { int taskid; int activityid; + int req_activity_id; int step; int activitytype; std::string target_name; @@ -48,6 +49,7 @@ public: return { "taskid", "activityid", + "req_activity_id", "step", "activitytype", "target_name", @@ -71,6 +73,7 @@ public: return { "taskid", "activityid", + "req_activity_id", "step", "activitytype", "target_name", @@ -128,6 +131,7 @@ public: e.taskid = 0; e.activityid = 0; + e.req_activity_id = -1; e.step = 0; e.activitytype = 0; e.target_name = ""; @@ -180,21 +184,22 @@ public: e.taskid = atoi(row[0]); e.activityid = atoi(row[1]); - e.step = atoi(row[2]); - e.activitytype = atoi(row[3]); - e.target_name = row[4] ? row[4] : ""; - e.item_list = row[5] ? row[5] : ""; - e.skill_list = row[6] ? row[6] : ""; - e.spell_list = row[7] ? row[7] : ""; - e.description_override = row[8] ? row[8] : ""; - e.goalid = atoi(row[9]); - e.goal_match_list = row[10] ? row[10] : ""; - e.goalmethod = atoi(row[11]); - e.goalcount = atoi(row[12]); - e.delivertonpc = atoi(row[13]); - e.zones = row[14] ? row[14] : ""; - e.zone_version = atoi(row[15]); - e.optional = atoi(row[16]); + e.req_activity_id = atoi(row[2]); + e.step = atoi(row[3]); + e.activitytype = atoi(row[4]); + e.target_name = row[5] ? row[5] : ""; + e.item_list = row[6] ? row[6] : ""; + e.skill_list = row[7] ? row[7] : ""; + e.spell_list = row[8] ? row[8] : ""; + e.description_override = row[9] ? row[9] : ""; + e.goalid = atoi(row[10]); + e.goal_match_list = row[11] ? row[11] : ""; + e.goalmethod = atoi(row[12]); + e.goalcount = atoi(row[13]); + e.delivertonpc = atoi(row[14]); + e.zones = row[15] ? row[15] : ""; + e.zone_version = atoi(row[16]); + e.optional = atoi(row[17]); return e; } @@ -230,21 +235,22 @@ public: v.push_back(columns[0] + " = " + std::to_string(e.taskid)); v.push_back(columns[1] + " = " + std::to_string(e.activityid)); - v.push_back(columns[2] + " = " + std::to_string(e.step)); - v.push_back(columns[3] + " = " + std::to_string(e.activitytype)); - v.push_back(columns[4] + " = '" + Strings::Escape(e.target_name) + "'"); - v.push_back(columns[5] + " = '" + Strings::Escape(e.item_list) + "'"); - v.push_back(columns[6] + " = '" + Strings::Escape(e.skill_list) + "'"); - v.push_back(columns[7] + " = '" + Strings::Escape(e.spell_list) + "'"); - v.push_back(columns[8] + " = '" + Strings::Escape(e.description_override) + "'"); - v.push_back(columns[9] + " = " + std::to_string(e.goalid)); - v.push_back(columns[10] + " = '" + Strings::Escape(e.goal_match_list) + "'"); - v.push_back(columns[11] + " = " + std::to_string(e.goalmethod)); - v.push_back(columns[12] + " = " + std::to_string(e.goalcount)); - v.push_back(columns[13] + " = " + std::to_string(e.delivertonpc)); - v.push_back(columns[14] + " = '" + Strings::Escape(e.zones) + "'"); - v.push_back(columns[15] + " = " + std::to_string(e.zone_version)); - v.push_back(columns[16] + " = " + std::to_string(e.optional)); + v.push_back(columns[2] + " = " + std::to_string(e.req_activity_id)); + v.push_back(columns[3] + " = " + std::to_string(e.step)); + v.push_back(columns[4] + " = " + std::to_string(e.activitytype)); + v.push_back(columns[5] + " = '" + Strings::Escape(e.target_name) + "'"); + v.push_back(columns[6] + " = '" + Strings::Escape(e.item_list) + "'"); + v.push_back(columns[7] + " = '" + Strings::Escape(e.skill_list) + "'"); + v.push_back(columns[8] + " = '" + Strings::Escape(e.spell_list) + "'"); + v.push_back(columns[9] + " = '" + Strings::Escape(e.description_override) + "'"); + v.push_back(columns[10] + " = " + std::to_string(e.goalid)); + v.push_back(columns[11] + " = '" + Strings::Escape(e.goal_match_list) + "'"); + v.push_back(columns[12] + " = " + std::to_string(e.goalmethod)); + v.push_back(columns[13] + " = " + std::to_string(e.goalcount)); + v.push_back(columns[14] + " = " + std::to_string(e.delivertonpc)); + v.push_back(columns[15] + " = '" + Strings::Escape(e.zones) + "'"); + v.push_back(columns[16] + " = " + std::to_string(e.zone_version)); + v.push_back(columns[17] + " = " + std::to_string(e.optional)); auto results = db.QueryDatabase( fmt::format( @@ -268,6 +274,7 @@ public: v.push_back(std::to_string(e.taskid)); v.push_back(std::to_string(e.activityid)); + v.push_back(std::to_string(e.req_activity_id)); v.push_back(std::to_string(e.step)); v.push_back(std::to_string(e.activitytype)); v.push_back("'" + Strings::Escape(e.target_name) + "'"); @@ -314,6 +321,7 @@ public: v.push_back(std::to_string(e.taskid)); v.push_back(std::to_string(e.activityid)); + v.push_back(std::to_string(e.req_activity_id)); v.push_back(std::to_string(e.step)); v.push_back(std::to_string(e.activitytype)); v.push_back("'" + Strings::Escape(e.target_name) + "'"); @@ -364,21 +372,22 @@ public: e.taskid = atoi(row[0]); e.activityid = atoi(row[1]); - e.step = atoi(row[2]); - e.activitytype = atoi(row[3]); - e.target_name = row[4] ? row[4] : ""; - e.item_list = row[5] ? row[5] : ""; - e.skill_list = row[6] ? row[6] : ""; - e.spell_list = row[7] ? row[7] : ""; - e.description_override = row[8] ? row[8] : ""; - e.goalid = atoi(row[9]); - e.goal_match_list = row[10] ? row[10] : ""; - e.goalmethod = atoi(row[11]); - e.goalcount = atoi(row[12]); - e.delivertonpc = atoi(row[13]); - e.zones = row[14] ? row[14] : ""; - e.zone_version = atoi(row[15]); - e.optional = atoi(row[16]); + e.req_activity_id = atoi(row[2]); + e.step = atoi(row[3]); + e.activitytype = atoi(row[4]); + e.target_name = row[5] ? row[5] : ""; + e.item_list = row[6] ? row[6] : ""; + e.skill_list = row[7] ? row[7] : ""; + e.spell_list = row[8] ? row[8] : ""; + e.description_override = row[9] ? row[9] : ""; + e.goalid = atoi(row[10]); + e.goal_match_list = row[11] ? row[11] : ""; + e.goalmethod = atoi(row[12]); + e.goalcount = atoi(row[13]); + e.delivertonpc = atoi(row[14]); + e.zones = row[15] ? row[15] : ""; + e.zone_version = atoi(row[16]); + e.optional = atoi(row[17]); all_entries.push_back(e); } @@ -405,21 +414,22 @@ public: e.taskid = atoi(row[0]); e.activityid = atoi(row[1]); - e.step = atoi(row[2]); - e.activitytype = atoi(row[3]); - e.target_name = row[4] ? row[4] : ""; - e.item_list = row[5] ? row[5] : ""; - e.skill_list = row[6] ? row[6] : ""; - e.spell_list = row[7] ? row[7] : ""; - e.description_override = row[8] ? row[8] : ""; - e.goalid = atoi(row[9]); - e.goal_match_list = row[10] ? row[10] : ""; - e.goalmethod = atoi(row[11]); - e.goalcount = atoi(row[12]); - e.delivertonpc = atoi(row[13]); - e.zones = row[14] ? row[14] : ""; - e.zone_version = atoi(row[15]); - e.optional = atoi(row[16]); + e.req_activity_id = atoi(row[2]); + e.step = atoi(row[3]); + e.activitytype = atoi(row[4]); + e.target_name = row[5] ? row[5] : ""; + e.item_list = row[6] ? row[6] : ""; + e.skill_list = row[7] ? row[7] : ""; + e.spell_list = row[8] ? row[8] : ""; + e.description_override = row[9] ? row[9] : ""; + e.goalid = atoi(row[10]); + e.goal_match_list = row[11] ? row[11] : ""; + e.goalmethod = atoi(row[12]); + e.goalcount = atoi(row[13]); + e.delivertonpc = atoi(row[14]); + e.zones = row[15] ? row[15] : ""; + e.zone_version = atoi(row[16]); + e.optional = atoi(row[17]); all_entries.push_back(e); } diff --git a/common/shared_tasks.h b/common/shared_tasks.h index 0d16ce8ec..119cdb826 100644 --- a/common/shared_tasks.h +++ b/common/shared_tasks.h @@ -3,6 +3,7 @@ #include "database.h" #include "timer.h" +#include "tasks.h" #include "types.h" #include "repositories/character_data_repository.h" #include "repositories/tasks_repository.h" @@ -108,8 +109,10 @@ struct SharedTaskActivityStateEntry { uint32 max_done_count; // goalcount uint32 updated_time; uint32 completed_time; + int req_activity_id; int step; bool optional; + ActivityState activity_state; // world only uses Hidden and Completed states }; struct ServerSharedTaskActivityUpdate_Struct { diff --git a/common/tasks.h b/common/tasks.h index e68fe6ddf..62f537668 100644 --- a/common/tasks.h +++ b/common/tasks.h @@ -2,6 +2,8 @@ #define EQEMU_TASKS_H #include "serialize_buffer.h" +#include +#include #define MAXTASKS 10000 #define MAXTASKSETS 1000 @@ -65,7 +67,8 @@ enum class AltCurrencyType }; struct ActivityInformation { - int step_number; + int req_activity_id; + int step; TaskActivityType activity_type; std::string target_name; // name mob, location -- default empty, max length 64 std::string item_list; // likely defaults to empty @@ -175,11 +178,6 @@ struct ActivityInformation { } }; -typedef enum { - ActivitiesSequential = 0, - ActivitiesStepped = 1 -} SequenceType; - enum class TaskType { Task = 0, // can have at max 1 Shared = 1, // can have at max 1 @@ -215,8 +213,6 @@ struct TaskInformation { int reward_points; AltCurrencyType reward_point_type; int activity_count{}; - SequenceType sequence_mode; - int last_step{}; short min_level{}; short max_level{}; int level_spread; @@ -270,7 +266,6 @@ struct ClientActivityInformation { struct ClientTaskInformation { int slot; // intrusive, but makes things easier :P int task_id; - int current_step; int accepted_time; bool updated; bool was_rewarded; // character has received reward for this task @@ -342,6 +337,79 @@ namespace Tasks { return "Task"; } } + + struct ActiveElements + { + bool is_task_complete; + std::vector active; + }; + + // Processes task activity states and returns those currently active + // It is templated to support the different structs used by zone and world + template + ActiveElements GetActiveElements(const Td& activity_data, const Ts& activity_states, size_t activity_count) + { + ActiveElements result; + result.is_task_complete = true; + result.active.reserve(activity_count); + + std::array completed_ids; + completed_ids.fill(false); + std::fill_n(completed_ids.begin(), activity_count, true); + + bool sequence_mode = true; + int current_step = std::numeric_limits::max(); // lowest step not completed + + // fill non-completed elements and find the current task step + for (int i = 0; i < activity_count; ++i) + { + const auto& el = activity_data[i]; + + if (activity_states[i].activity_state != ActivityCompleted) + { + completed_ids[i] = false; + current_step = std::min(current_step, el.step); + if (!el.optional) + { + result.is_task_complete = false; + } + } + + // if all steps are 0 treat each as a separate step (previously called "sequential" mode) + if (el.step != 0) + { + sequence_mode = false; + } + } + + // fill active elements based on current step and req activity ids + bool added_sequence = false; + for (int i = 0; i < activity_count; ++i) + { + const auto& el = activity_data[i]; + + if (activity_states[i].activity_state != ActivityCompleted) + { + bool has_req_id = el.req_activity_id >= 0 && el.req_activity_id < activity_count; + + // if a valid requirement is set then it's active if its req is completed + // in non-sequence mode all on current step and optionals in previous steps are active + // in sequence mode the first non-complete is active (and any optionals up to it) + if ((has_req_id && completed_ids[el.req_activity_id]) || + (!has_req_id && !sequence_mode && el.step <= current_step) || + (!has_req_id && sequence_mode && !added_sequence)) + { + result.active.push_back(i); + if (!has_req_id && sequence_mode) + { + added_sequence = !el.optional; + } + } + } + } + + return result; + } } namespace TaskStr { diff --git a/common/version.h b/common/version.h index 4fe0406f6..f01324ebb 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9198 +#define CURRENT_BINARY_DATABASE_VERSION 9199 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 41c7aa547..811890bb7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ SET(tests_headers memory_mapped_file_test.h string_util_test.h skills_util_test.h + task_state_test.h ) ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) diff --git a/tests/main.cpp b/tests/main.cpp index 9d72da520..13f49f238 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -29,6 +29,7 @@ #include "string_util_test.h" #include "data_verification_test.h" #include "skills_util_test.h" +#include "task_state_test.h" #include "../common/eqemu_config.h" const EQEmuConfig *Config; @@ -49,6 +50,7 @@ int main() { tests.add(new StringUtilTest()); tests.add(new DataVerificationTest()); tests.add(new SkillsUtilsTest()); + tests.add(new TaskStateTest()); tests.run(*output, true); } catch(...) { return -1; diff --git a/tests/task_state_test.h b/tests/task_state_test.h new file mode 100644 index 000000000..d1a0551b6 --- /dev/null +++ b/tests/task_state_test.h @@ -0,0 +1,1183 @@ +#pragma once + +#include "cppunit/cpptest.h" +#include "../common/eqemu_logsys.h" +#include "../common/tasks.h" +#include "../common/shared_tasks.h" + +class TaskStateTest: public Test::Suite +{ +public: + TaskStateTest() + { + TEST_ADD(TaskStateTest::TestSequenceMode); + TEST_ADD(TaskStateTest::TestSteps); + TEST_ADD(TaskStateTest::TestStepGaps); + TEST_ADD(TaskStateTest::TestUnorderedSteps); + TEST_ADD(TaskStateTest::TestOptionalSteps); + TEST_ADD(TaskStateTest::TestOptionalLastSteps); + TEST_ADD(TaskStateTest::TestOptionalSequence); + TEST_ADD(TaskStateTest::TestWorldTemplateSupport); + TEST_ADD(TaskStateTest::TestReqActivityID); + TEST_ADD(TaskStateTest::TestReqActivityIDOverrideStep); + TEST_ADD(TaskStateTest::TestReqActivityIDSteps); + TEST_ADD(TaskStateTest::TestReqActivityIDUnorderedSteps); + TEST_ADD(TaskStateTest::TestReqActivityIDMixSteps); + TEST_ADD(TaskStateTest::TestReqActivityIDSequenceMode); + TEST_ADD(TaskStateTest::TestReqActivityIDOptional); + TEST_ADD(TaskStateTest::TestReqActivityIDOptionalLastSteps); + } + +private: + void TestSequenceMode(); + void TestSteps(); + void TestStepGaps(); + void TestUnorderedSteps(); + void TestOptionalSteps(); + void TestOptionalLastSteps(); + void TestOptionalSequence(); + void TestWorldTemplateSupport(); + void TestReqActivityID(); + void TestReqActivityIDOverrideStep(); + void TestReqActivityIDSteps(); + void TestReqActivityIDUnorderedSteps(); + void TestReqActivityIDMixSteps(); + void TestReqActivityIDSequenceMode(); + void TestReqActivityIDOptional(); + void TestReqActivityIDOptionalLastSteps(); + + TaskInformation GetMockZoneData(int count) + { + TaskInformation task; + task.activity_count = count; + for (int i = 0; i < task.activity_count; ++i) + { + task.activity_information[i].req_activity_id = -1; + task.activity_information[i].step = 0; + task.activity_information[i].optional = false; + } + return task; + } + + ClientTaskInformation GetMockZoneState(int count) + { + ClientTaskInformation state; + for (int i = 0; i < count; ++i) + { + state.activity[i].activity_id = i; + state.activity[i].activity_state = ActivityState::ActivityHidden; + } + return state; + } + + std::vector GetMockWorldData(int count) + { + std::vector data; + data.resize(count); + for (int i = 0; i < count; ++i) + { + data[i].activityid = i; + data[i].req_activity_id = -1; + data[i].step = 0; + data[i].optional = false; + } + return data; + } + + std::vector GetMockWorldState(int count) + { + std::vector states; + states.resize(count); + for (int i = 0; i < count; ++i) + { + states[i].activity_id = i; + states[i].req_activity_id = -1; + states[i].step = 0; + states[i].optional = false; + states[i].activity_state = ActivityState::ActivityHidden; + } + return states; + } +}; + +void TaskStateTest::TestSequenceMode() +{ + int activity_count = 3; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | hidden (active) + // 1 | 0 | hidden + // 2 | 0 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 0 | hidden (active) + // 2 | 0 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id |step | state + // 0 | 0 | completed + // 1 | 0 | completed + // 2 | 0 | hidden (active) + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 0 | completed + // 2 | 0 | completed + + // task completed, none should be active + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityHidden; + data.activity_information[2].optional = true; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 0 | completed + // 2 | 0 | hidden | optional (active) + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } +} + +void TaskStateTest::TestSteps() +{ + int activity_count = 4; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[1].step = 1; + data.activity_information[2].step = 1; + data.activity_information[3].step = 2; + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | hidden (active) + // 1 | 1 | hidden + // 2 | 1 | hidden + // 3 | 2 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 1 | hidden (active) + // 2 | 1 | hidden (active) + // 3 | 2 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 1 | completed + // 2 | 1 | hidden (active) + // 3 | 2 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 1 | completed + // 2 | 1 | completed + // 3 | 2 | hidden (active) + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 1 | completed + // 2 | 1 | completed + // 3 | 2 | completed + + // should be complete with none active + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityHidden; + data.activity_information[3].optional = true; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // activity_id | step | state + // 0 | 0 | completed + // 1 | 1 | completed + // 2 | 1 | completed + // 3 | 2 | hidden | optional (active) + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } +} + +void TaskStateTest::TestStepGaps() +{ + int activity_count = 5; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 1; + data.activity_information[1].step = 5; + data.activity_information[2].step = 5; + data.activity_information[3].step = 100; + data.activity_information[4].step = 100; + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 0 should be active starting at step 1 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // indexes 1 and 2 should be active at step 5 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // only index 2 should be active with step 5 since index 1 is completed + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // indexes 3 and 4 should be active for step 100 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + state.activity[4].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + +void TaskStateTest::TestUnorderedSteps() +{ + int activity_count = 5; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 100; + data.activity_information[1].step = 100; + data.activity_information[2].step = 3; + data.activity_information[3].step = 20; + data.activity_information[4].step = 1; + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 4 should be active as the lowest step + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[4].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 2 should be the next lowest step (3) after step 1 completed + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[4].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 3 should be active as step 20 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[4].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // indexes 0 and 1 should both be active as step 100 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } +} + +void TaskStateTest::TestOptionalSteps() +{ + int activity_count = 4; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 0; + data[1].step = 1; + data[1].optional = true; + data[2].step = 2; + data[2].optional = true; + data[3].step = 2; + + { + auto res = Tasks::GetActiveElements(data, state, activity_count); + + // activity_id | step | state + // 0 | 0 | hidden (active) + // 1 | 1 | hidden | optional + // 2 | 2 | hidden | optional + // 3 | 2 | hidden + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 1 | hidden | optional (active) + // 2 | 2 | hidden | optional + // 3 | 2 | hidden + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + // since optional is on its own step it's effectively non-optional to open next step + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 1 | complete | optional + // 2 | 2 | hidden | optional (active) + // 3 | 2 | hidden (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + state[3].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 1 | complete | optional + // 2 | 2 | hidden | optional (active) + // 3 | 2 | complete + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + state[2].activity_state = ActivityState::ActivityCompleted; + state[3].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + +void TaskStateTest::TestOptionalLastSteps() +{ + int activity_count = 3; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 0; + data[1].optional = true; + data[1].step = 2; + data[2].optional = true; + data[2].step = 3; + + { + // activity_id | step | state + // 0 | 0 | hidden (active) + // 1 | 1 | hidden | optional + // 2 | 2 | hidden | optional + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 1 | hidden | optional (active) + // 2 | 2 | hidden | optional + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 1 | complete | optional + // 2 | 2 | hidden | optional (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } +} + +void TaskStateTest::TestOptionalSequence() +{ + int activity_count = 3; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 0; + data[1].step = 0; + data[1].optional = true; + data[2].step = 0; + + { + // activity_id | step | state + // 0 | 0 | hidden (active) + // 1 | 0 | hidden | optional + // 2 | 0 | hidden + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 0 | hidden | optional (active) + // 2 | 0 | hidden (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 0 | complete + // 2 | 0 | hidden (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityHidden; + state[2].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | 0 | complete + // 1 | 0 | hidden | optional (active) + // 2 | 0 | complete + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + state[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + +void TaskStateTest::TestWorldTemplateSupport() +{ + int activity_count = 3; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 1; + data[1].step = 5; + data[2].step = 10; + state[0].step = 1; + state[1].step = 5; + state[2].step = 10; + state[0].activity_state = ActivityState::ActivityCompleted; + + { + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + // using the state struct as both data and state source + auto res = Tasks::GetActiveElements(state, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } +} + +void TaskStateTest::TestReqActivityID() +{ + int activity_count = 5; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[1].req_activity_id = 0; + data.activity_information[2].req_activity_id = 0; + data.activity_information[3].req_activity_id = 1; + data.activity_information[4].req_activity_id = 2; + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + state.activity[4].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + +void TaskStateTest::TestReqActivityIDOverrideStep() +{ + int activity_count = 4; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 0; + data.activity_information[1].step = 1; + data.activity_information[2].req_activity_id = 1; + data.activity_information[2].step = 1; + data.activity_information[3].req_activity_id = 1; + data.activity_information[3].step = 1; + + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // indexes 2 and 3 should require index 1 to be completed instead of activating with step 1 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); +} + +void TaskStateTest::TestReqActivityIDSteps() +{ + int activity_count = 5; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 0; + data.activity_information[1].step = 1; + data.activity_information[2].req_activity_id = 0; + data.activity_information[2].step = 2; + data.activity_information[3].req_activity_id = 0; + data.activity_information[3].step = 2; + data.activity_information[4].step = 3; + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 1 should become active as next step, indexes 2 and 3 because of reqs + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 3); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | complete + // 2 | 0 | 2 | hidden (active) + // 3 | 0 | 2 | hidden (active) + // 4 | -1 | 3 | hidden + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 4 (step 3) should not become active until step 2 is completed + // even though index 2 and 3 are active because of reqs + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // should still be on step 2 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + state.activity[4].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + + +void TaskStateTest::TestReqActivityIDUnorderedSteps() +{ + int activity_count = 5; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 0; + data.activity_information[1].step = 1; + data.activity_information[2].req_activity_id = 0; + data.activity_information[2].step = 0; + data.activity_information[3].req_activity_id = 0; + data.activity_information[3].step = 0; + data.activity_information[4].step = 3; + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | hidden + // 2 | 0 | 0 | hidden (active) + // 3 | 0 | 0 | hidden (active) + // 4 | -1 | 3 | hidden + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | hidden (active) + // 2 | 0 | 0 | complete + // 3 | 0 | 0 | complete + // 4 | -1 | 3 | hidden + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | complete + // 2 | 0 | 0 | complete + // 3 | 0 | 0 | complete + // 4 | -1 | 3 | hidden (active) + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // should still be on step 3 + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } +} + +void TaskStateTest::TestReqActivityIDMixSteps() +{ + int activity_count = 6; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[0].step = 0; + data.activity_information[1].step = 1; + data.activity_information[2].step = 2; + data.activity_information[3].req_activity_id = 0; + data.activity_information[3].step = 2; + data.activity_information[4].req_activity_id = 0; + data.activity_information[4].step = 2; + data.activity_information[5].step = 3; + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | complete + // 2 | -1 | 2 | hidden (active) + // 3 | 0 | 2 | hidden (active) + // 4 | 0 | 2 | hidden (active) + // 5 | -1 | 3 | hidden + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 3); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | complete + // 2 | -1 | 2 | complete + // 3 | 0 | 2 | hidden (active) + // 4 | 0 | 2 | hidden (active) + // 5 | -1 | 3 | hidden + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 5 (step 3) should not be active yet after completing only index with non-req id + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 2); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + state.activity[2].activity_state = ActivityState::ActivityCompleted; + state.activity[3].activity_state = ActivityState::ActivityCompleted; + state.activity[4].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | -1 | 1 | complete + // 2 | -1 | 2 | complete + // 3 | 0 | 2 | complete + // 4 | 0 | 2 | complete + // 5 | -1 | 3 | hidden (active) + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 5) != res.active.end()); + } +} + +void TaskStateTest::TestReqActivityIDSequenceMode() +{ + int activity_count = 6; + TaskInformation data = GetMockZoneData(activity_count); + ClientTaskInformation state = GetMockZoneState(activity_count); + data.activity_information[4].req_activity_id = 1; + data.activity_information[5].req_activity_id = 0; + + { + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state.activity[0].activity_state = ActivityState::ActivityCompleted; + state.activity[1].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data.activity_information, state.activity, activity_count); + + // index 2 because of sequence mode, index 4 and 5 due to req indexes being complete + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 3); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 4) != res.active.end()); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 5) != res.active.end()); + } +} + +void TaskStateTest::TestReqActivityIDOptional() +{ + int activity_count = 3; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 0; + data[1].req_activity_id = 0; + data[1].step = 1; + data[1].optional = true; + data[2].req_activity_id = 1; + data[2].step = 2; + + { + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | hidden (active) + // 1 | 0 | 1 | hidden | optional + // 2 | 1 | 2 | hidden + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | 0 | 1 | hidden | optional (active) + // 2 | 1 | 2 | hidden + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + // index 1 is effectively non-optional since non-optional index 2 requires it + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | 0 | 1 | complete | optional + // 2 | 1 | 2 | hidden (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + state[2].activity_state = ActivityState::ActivityCompleted; + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.empty()); + } +} + +void TaskStateTest::TestReqActivityIDOptionalLastSteps() +{ + int activity_count = 3; + auto data = GetMockWorldData(activity_count); + auto state = GetMockWorldState(activity_count); + data[0].step = 0; + data[1].req_activity_id = 0; + data[1].step = 1; + data[1].optional = true; + data[2].req_activity_id = 1; + data[2].step = 2; + data[2].optional = true; + + { + // activity_id | req_activity_id | step | state + // 0 | -1 | 0 | hidden (active) + // 1 | 0 | 1 | hidden | optional + // 2 | 1 | 2 | hidden | optional + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == false); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 0) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | 0 | 1 | hidden | optional (active) + // 2 | 1 | 2 | hidden | optional + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end()); + } + + { + state[0].activity_state = ActivityState::ActivityCompleted; + state[1].activity_state = ActivityState::ActivityCompleted; + + // activity_id | step | state + // 0 | -1 | 0 | complete + // 1 | 0 | 1 | complete | optional + // 2 | 1 | 2 | hidden | optional (active) + + auto res = Tasks::GetActiveElements(data, state, activity_count); + + TEST_ASSERT(res.is_task_complete == true); + TEST_ASSERT(res.active.size() == 1); + TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end()); + } +} diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 680d3c823..0dbc57ef1 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -452,6 +452,7 @@ 9196|2022_07_30_merchantlist_temp.sql|SHOW COLUMNS FROM `merchantlist_temp` LIKE 'zone_id'|empty| 9197|2022_08_01_drop_expansion_account.sql|SHOW COLUMNS FROM `account` LIKE 'expansion'|notempty| 9198|2022_08_14_exp_modifier_instance_versions.sql|SHOW COLUMNS FROM `character_exp_modifiers` LIKE 'instance_version'|empty| +9199|2022_08_08_task_req_activity_id.sql|SHOW COLUMNS FROM `task_activities` LIKE 'req_activity_id'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2022_08_08_task_req_activity_id.sql b/utils/sql/git/required/2022_08_08_task_req_activity_id.sql new file mode 100644 index 000000000..3f8e127ec --- /dev/null +++ b/utils/sql/git/required/2022_08_08_task_req_activity_id.sql @@ -0,0 +1,2 @@ +ALTER TABLE `task_activities` + ADD COLUMN `req_activity_id` INT SIGNED NOT NULL DEFAULT '-1' AFTER `activityid`; diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp index b648456ec..4ea3866a7 100644 --- a/world/shared_task_manager.cpp +++ b/world/shared_task_manager.cpp @@ -15,7 +15,6 @@ #include "../common/repositories/completed_shared_task_members_repository.h" #include "../common/repositories/completed_shared_task_activity_state_repository.h" #include "../common/repositories/shared_task_dynamic_zones_repository.h" -#include #include extern ClientList client_list; @@ -130,6 +129,8 @@ void SharedTaskManager::AttemptSharedTaskCreation( e.max_done_count = a.goalcount; e.step = a.step; e.optional = a.optional; + e.req_activity_id = a.req_activity_id; + e.activity_state = ActivityState::ActivityHidden; shared_task_activity_state.emplace_back(e); } @@ -354,6 +355,8 @@ void SharedTaskManager::LoadSharedTaskState() e.updated_time = sta.updated_time; e.step = ad.step; e.optional = ad.optional; + e.req_activity_id = ad.req_activity_id; + e.activity_state = sta.completed_time == 0 ? ActivityHidden : ActivityCompleted; } } @@ -524,6 +527,7 @@ void SharedTaskManager::SharedTaskActivityUpdate( // if the activity is done, lets mark it as such if (a.done_count == a.max_done_count) { a.completed_time = std::time(nullptr); + a.activity_state = ActivityState::ActivityCompleted; } // sync state as each update comes in (for now) @@ -1702,43 +1706,19 @@ void SharedTaskManager::LockTask(SharedTask* s, bool lock) bool SharedTaskManager::HandleCompletedActivities(SharedTask* s) { - bool is_task_complete = true; - bool lock_task = false; + auto states = s->GetActivityState(); - std::array completed_steps; - completed_steps.fill(true); - - // multiple activity ids may share a step, sort so previous step completions can be checked - auto activity_states = s->GetActivityState(); - std::sort(activity_states.begin(), activity_states.end(), - [](const auto& lhs, const auto& rhs) { return lhs.step < rhs.step; }); - - for (const auto& a : activity_states) - { - if (a.done_count != a.max_done_count && !a.optional) - { - is_task_complete = false; - if (a.step > 0 && a.step <= MAXACTIVITIESPERTASK) - { - completed_steps[a.step - 1] = false; - } - } - - int lock_index = s->GetTaskData().lock_activity_id; - if (a.activity_id == lock_index && a.step > 0 && a.step <= MAXACTIVITIESPERTASK) - { - // lock if element is active (on first step or previous step completed) - lock_task = (a.step == 1 || completed_steps[a.step - 2]); - } - } + // activity state holds both source data and current state + auto res = Tasks::GetActiveElements(states, states, states.size()); // completion locks are silent - if (!is_task_complete && lock_task) + auto it = std::find(res.active.begin(), res.active.end(), s->GetTaskData().lock_activity_id); + if (!res.is_task_complete && it != res.active.end()) { LockTask(s, true); } - return is_task_complete; + return res.is_task_complete; } void SharedTaskManager::HandleCompletedTask(SharedTask* s) diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 14f12da87..0d90337bb 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -366,218 +366,94 @@ static void DeleteCompletedTaskFromDatabase(int character_id, int task_id) bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation &task_info) { - bool all_activities_complete = true; - LogTasksDetail( - "[UnlockActivities] Fetching task info for character_id [{}] task [{}] slot [{}] current_step [{}] accepted_time [{}] updated [{}]", + "[UnlockActivities] Fetching task info for character_id [{}] task [{}] slot [{}] accepted_time [{}] updated [{}]", character_id, task_info.task_id, task_info.slot, - task_info.current_step, task_info.accepted_time, task_info.updated ); - TaskInformation *p_task_data = task_manager->m_task_data[task_info.task_id]; - if (p_task_data == nullptr) { + const TaskInformation* task = task_manager->m_task_data[task_info.task_id]; + if (!task) + { return true; } - for (int i = 0; i < p_task_data->activity_count; i++) { + for (int i = 0; i < task->activity_count; ++i) + { if (task_info.activity[i].activity_id >= 0) { LogTasksDetail( - "[UnlockActivities] character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}] sequence [{}]", + "[UnlockActivities] character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}]", character_id, task_info.task_id, task_info.activity[i].activity_id, task_info.activity[i].done_count, task_info.activity[i].activity_state, - task_info.activity[i].updated, - p_task_data->sequence_mode + task_info.activity[i].updated ); } } - // 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_information as active if not complete. + auto res = Tasks::GetActiveElements(task->activity_information, task_info.activity, task->activity_count); - if (p_task_data->sequence_mode == ActivitiesSequential) { - if (task_info.activity[0].activity_state != ActivityCompleted) { - task_info.activity[0].activity_state = ActivityActive; - } - - // Enable the next Hidden task. - for (int i = 0; i < p_task_data->activity_count; i++) { - if ((task_info.activity[i].activity_state == ActivityActive) && - (!p_task_data->activity_information[i].optional)) { - all_activities_complete = false; - break; - } - - if (task_info.activity[i].activity_state == ActivityHidden) { - task_info.activity[i].activity_state = ActivityActive; - all_activities_complete = false; - break; - } - } - - if (all_activities_complete && RuleB(TaskSystem, RecordCompletedTasks)) { - if (RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { - LogTasks("KeepOneRecord enabled"); - auto iterator = m_completed_tasks.begin(); - int erased_elements = 0; - while (iterator != m_completed_tasks.end()) { - int task_id = (*iterator).task_id; - if (task_id == task_info.task_id) { - iterator = m_completed_tasks.erase(iterator); - erased_elements++; - } - else { - ++iterator; - } - } - - LogTasks("Erased Element count is [{}]", erased_elements); - - if (erased_elements) { - m_last_completed_task_loaded -= erased_elements; - DeleteCompletedTaskFromDatabase(character_id, task_info.task_id); - } - } - - if (p_task_data->type != TaskType::Shared) { - CompletedTaskInformation completed_task_information{}; - completed_task_information.task_id = task_info.task_id; - completed_task_information.completed_time = time(nullptr); - - for (int i = 0; i < p_task_data->activity_count; i++) { - completed_task_information.activity_done[i] = (task_info.activity[i].activity_state == - ActivityCompleted); - } - - m_completed_tasks.push_back(completed_task_information); - } - } - - LogTasks("Returning sequential task, AllActivitiesComplete is [{}]", all_activities_complete); - - return all_activities_complete; - } - - // Stepped Mode - // TODO: This code is probably more complex than it needs to be - - bool current_step_complete = true; - - LogTasks( - "[UnlockActivities] Current step [{}] last_step [{}]", - task_info.current_step, - p_task_data->last_step - ); - - // If current_step 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 (task_info.current_step == -1) { - for (int i = 0; i < p_task_data->activity_count; i++) { - - if (p_task_data->activity_information[i].step_number == 0 && - task_info.activity[i].activity_state == ActivityHidden) { - task_info.activity[i].activity_state = ActivityActive; - // task_info.activity_information[i].updated=true; - } - } - task_info.current_step = 0; - } - - for (int current_step = task_info.current_step; current_step <= p_task_data->last_step; current_step++) { - for (int activity = 0; activity < p_task_data->activity_count; activity++) { - if (p_task_data->activity_information[activity].step_number == (int) task_info.current_step) { - if ((task_info.activity[activity].activity_state != ActivityCompleted) && - (!p_task_data->activity_information[activity].optional)) { - current_step_complete = false; - all_activities_complete = false; - break; - } - } - } - if (!current_step_complete) { - break; - } - task_info.current_step++; - } - - if (all_activities_complete) { - 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)) { - LogTasksDetail("[UnlockActivities] KeepOneRecord enabled"); - auto iterator = m_completed_tasks.begin(); - int erased_elements = 0; - - while (iterator != m_completed_tasks.end()) { - int task_id = (*iterator).task_id; - if (task_id == task_info.task_id) { - iterator = m_completed_tasks.erase(iterator); - erased_elements++; - } - else { - ++iterator; - } - } - - LogTasksDetail("[UnlockActivities] Erased Element count is [{}]", erased_elements); - - if (erased_elements) { - m_last_completed_task_loaded -= erased_elements; - DeleteCompletedTaskFromDatabase(character_id, task_info.task_id); - } - } - - if (p_task_data->type != TaskType::Shared) { - CompletedTaskInformation completed_task_information{}; - completed_task_information.task_id = task_info.task_id; - completed_task_information.completed_time = time(nullptr); - - for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { - completed_task_information.activity_done[activity_id] = - (task_info.activity[activity_id].activity_state == ActivityCompleted); - } - - m_completed_tasks.push_back(completed_task_information); - } - } - return true; - } - - // Mark all non-completed tasks in the current step as active - for (int activity = 0; activity < p_task_data->activity_count; activity++) { - LogTasksDetail( - "[UnlockActivities] - Debug task [{}] activity [{}] step_number [{}] current_step [{}]", - task_info.task_id, - activity, - p_task_data->activity_information[activity].step_number, - (int) task_info.current_step - - ); - - if ((p_task_data->activity_information[activity].step_number == (int) task_info.current_step) && - (task_info.activity[activity].activity_state == ActivityHidden)) { - - LogTasksDetail( - "[UnlockActivities] -- Debug task [{}] activity [{}] (ActivityActive)", - task_info.task_id, - activity - ); - - task_info.activity[activity].activity_state = ActivityActive; - task_info.activity[activity].updated = true; + for (int activity_id : res.active) + { + ClientActivityInformation& client_activity = task_info.activity[activity_id]; + if (client_activity.activity_state == ActivityHidden) + { + LogTasksDetail("[UnlockActivities] task [{}] activity [{}] (ActivityActive)", task_info.task_id, activity_id); + client_activity.activity_state = ActivityActive; + client_activity.updated = true; } } - return false; + if (res.is_task_complete && RuleB(TaskSystem, RecordCompletedTasks)) + { + RecordCompletedTask(character_id, *task, task_info); + } + + return res.is_task_complete; +} + +void ClientTaskState::RecordCompletedTask(uint32_t character_id, const TaskInformation& task, const ClientTaskInformation& client_task) +{ + // 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)) + { + size_t before = m_completed_tasks.size(); + + m_completed_tasks.erase(std::remove_if(m_completed_tasks.begin(), m_completed_tasks.end(), + [&](const CompletedTaskInformation& completed) { return completed.task_id == client_task.task_id; } + ), m_completed_tasks.end()); + + size_t erased = m_completed_tasks.size() - before; + + LogTasksDetail("[RecordCompletedTask] KeepOneRecord erased [{}] elements", erased); + + if (erased > 0) + { + m_last_completed_task_loaded -= erased; + DeleteCompletedTaskFromDatabase(character_id, client_task.task_id); + } + } + + if (task.type != TaskType::Shared) + { + CompletedTaskInformation completed{}; + completed.task_id = client_task.task_id; + completed.completed_time = std::time(nullptr); + + for (int i = 0; i < task.activity_count; ++i) + { + completed.activity_done[i] = (client_task.activity[i].activity_state == ActivityCompleted); + } + + LogTasksDetail("[RecordCompletedTask] [{}] for character [{}]", client_task.task_id, character_id); + m_completed_tasks.push_back(completed); + } } bool ClientTaskState::UpdateTasksOnSpeakWith(Client *client, int npc_type_id) @@ -2396,7 +2272,6 @@ void ClientTaskState::AcceptNewTask( active_slot->task_id = task_id; active_slot->accepted_time = static_cast(accept_time); active_slot->updated = true; - active_slot->current_step = -1; active_slot->was_rewarded = false; for (int activity_id = 0; activity_id < task_manager->m_task_data[task_id]->activity_count; activity_id++) { diff --git a/zone/task_client_state.h b/zone/task_client_state.h index 4e49b4589..bf7dad21a 100644 --- a/zone/task_client_state.h +++ b/zone/task_client_state.h @@ -88,6 +88,7 @@ private: void AddReplayTimer(Client *client, ClientTaskInformation& client_task, TaskInformation& task); void DispatchEventTaskComplete(Client* client, ClientTaskInformation& client_task, int activity_id); void AddOffer(int task_id, uint16_t npc_entity_id) { m_last_offers.push_back({task_id, npc_entity_id}); }; + void RecordCompletedTask(uint32_t character_id, const TaskInformation& task, const ClientTaskInformation& client_task); void IncrementDoneCount( Client *client, diff --git a/zone/task_manager.cpp b/zone/task_manager.cpp index d808e1088..859b466c5 100644 --- a/zone/task_manager.cpp +++ b/zone/task_manager.cpp @@ -120,8 +120,6 @@ bool TaskManager::LoadTasks(int single_task) m_task_data[task_id]->request_timer_group = task.request_timer_group; m_task_data[task_id]->request_timer_seconds = task.request_timer_seconds; m_task_data[task_id]->activity_count = 0; - m_task_data[task_id]->sequence_mode = ActivitiesSequential; - m_task_data[task_id]->last_step = 0; LogTasksDetail( "[LoadTasks] (Task) task_id [{}] type [{}] () duration [{}] duration_code [{}] title [{}] description [{}] " @@ -202,16 +200,6 @@ bool TaskManager::LoadTasks(int single_task) int activity_index = m_task_data[task_id]->activity_count; ActivityInformation *activity_data = &m_task_data[task_id]->activity_information[activity_index]; - m_task_data[task_id]->activity_information[m_task_data[task_id]->activity_count].step_number = step; - - if (step != 0) { - m_task_data[task_id]->sequence_mode = ActivitiesStepped; - } - - if (step > m_task_data[task_id]->last_step) { - m_task_data[task_id]->last_step = step; - } - // Task Activities MUST be numbered sequentially from 0. If not, log an error // and set the task to nullptr. Subsequent activities for this task will raise // ERR_NOTASK errors. @@ -227,6 +215,8 @@ bool TaskManager::LoadTasks(int single_task) } // set activity data + activity_data->req_activity_id = task_activity.req_activity_id; + activity_data->step = step; activity_data->activity_type = static_cast(task_activity.activitytype); activity_data->target_name = task_activity.target_name; activity_data->item_list = task_activity.item_list; @@ -257,7 +247,7 @@ bool TaskManager::LoadTasks(int single_task) LogTasksDetail( "[LoadTasks] (Activity) task_id [{}] activity_id [{}] slot [{}] activity_type [{}] goal_id [{}] goal_method [{}] goal_count [{}] zones [{}]" - " target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}] sequence [{}]", + " target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}]", task_id, activity_id, m_task_data[task_id]->activity_count, @@ -270,8 +260,7 @@ bool TaskManager::LoadTasks(int single_task) activity_data->item_list.c_str(), activity_data->skill_list.c_str(), activity_data->spell_list.c_str(), - activity_data->description_override.c_str(), - (m_task_data[task_id]->sequence_mode == ActivitiesStepped ? "stepped" : "sequential") + activity_data->description_override.c_str() ); m_task_data[task_id]->activity_count++; @@ -852,43 +841,6 @@ int TaskManager::GetActivityCount(int task_id) return 0; } -void TaskManager::ExplainTask(Client *client, int task_id) -{ - - // TODO: This method is not finished (hardly started). It was intended to - // explain in English, what each activity_information did, conditions for step unlocking, etc. - // - return; - - if (!client) { return; } - - if ((task_id <= 0) || (task_id >= MAXTASKS)) { - client->Message(Chat::White, "task_id out-of-range."); - return; - } - - if (m_task_data[task_id] == nullptr) { - client->Message(Chat::White, "Task does not exist."); - return; - } - - char explanation[1000], *ptr; - client->Message(Chat::White, "Task %4i: title: %s", task_id, m_task_data[task_id]->description.c_str()); - client->Message(Chat::White, "%3i Activities", m_task_data[task_id]->activity_count); - ptr = explanation; - for (int i = 0; i < m_task_data[task_id]->activity_count; i++) { - - sprintf(ptr, "Act: %3i: ", i); - ptr = ptr + strlen(ptr); - switch (m_task_data[task_id]->activity_information[i].activity_type) { - case TaskActivityType::Deliver: - sprintf(ptr, "Deliver"); - break; - } - - } -} - bool TaskManager::IsTaskRepeatable(int task_id) { if ((task_id <= 0) || (task_id >= MAXTASKS)) { @@ -1322,7 +1274,6 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s } task_info->task_id = task_id; - task_info->current_step = -1; task_info->accepted_time = character_task.acceptedtime; task_info->updated = false; task_info->was_rewarded = character_task.was_rewarded; @@ -1568,11 +1519,10 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s // purely debugging LogTasksDetail( - "[LoadClientState] Fetching task info for character_id [{}] task [{}] slot [{}] current_step [{}] accepted_time [{}] updated [{}]", + "[LoadClientState] Fetching task info for character_id [{}] task [{}] slot [{}] accepted_time [{}] updated [{}]", character_id, client_task_state->m_active_task.task_id, client_task_state->m_active_task.slot, - client_task_state->m_active_task.current_step, client_task_state->m_active_task.accepted_time, client_task_state->m_active_task.updated ); @@ -1582,14 +1532,13 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s for (int i = 0; i < p_task_data->activity_count; i++) { if (client_task_state->m_active_task.activity[i].activity_id >= 0) { LogTasksDetail( - "[LoadClientState] -- character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}] sequence [{}]", + "[LoadClientState] -- character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}]", character_id, client_task_state->m_active_task.task_id, client_task_state->m_active_task.activity[i].activity_id, client_task_state->m_active_task.activity[i].done_count, client_task_state->m_active_task.activity[i].activity_state, - client_task_state->m_active_task.activity[i].updated, - p_task_data->sequence_mode + client_task_state->m_active_task.activity[i].updated ); } } diff --git a/zone/task_manager.h b/zone/task_manager.h index 05fd31668..c512709d6 100644 --- a/zone/task_manager.h +++ b/zone/task_manager.h @@ -61,7 +61,6 @@ public: bool task_complete = false ); void SendCompletedTasksToClient(Client *c, ClientTaskState *client_task_state); - void ExplainTask(Client *client, int task_id); int FirstTaskInSet(int task_set); int LastTaskInSet(int task_set); int NextTaskInSet(int task_set, int task_id);