mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-03 01:06:36 +00:00
[Tasks] Implement task activity prerequisites (#2374)
Some live tasks make new elements available without requiring all currently active ones to be completed first. This adds the `req_activity_id` field to task activities which will mark an element active if its required activity id is completed. If a valid value is set then it's used instead of checking the current step. The `step` field may still be set on rows with a valid `req_activity_id` to specify its logical step and prevent later steps from becoming active until completed. It's only ignored when deciding if the current element is active. The legacy task logic for unlocking activities was completely refactored for this. A common method has been added so both zone and world can make use of it to determine which elements are currently active. The previous step system should remain unchanged. The world logic for locking shared tasks when an element became active did not account for "sequential" mode (all steps 0), unordered steps, or gaps in step numbers. This also resolves that issue.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+77
-9
@@ -2,6 +2,8 @@
|
||||
#define EQEMU_TASKS_H
|
||||
|
||||
#include "serialize_buffer.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#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<int> active;
|
||||
};
|
||||
|
||||
// Processes task activity states and returns those currently active
|
||||
// It is templated to support the different structs used by zone and world
|
||||
template <typename Td, typename Ts>
|
||||
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<bool, MAXACTIVITIESPERTASK> 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<int>::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 {
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user