[Tasks] Replace task goals with explicit fields (#2402)

The task goal system made implementing tasks a little confusing since
the goal could be ambiguous depending on type. This also didn't support
filtering on multiple goals (e.g. looting items from matching npc names
inside an area). Deliver types could specify an npc id in `delivertonpc`
but the database may have multiple npcs with the same name or a task
might want to match partial npc names.

This replaces goalids with explicit fields for npcs, items, proximity
areas, and touch switch ids. These changes make managing task data
easier without needing to update multiple tables and allows filtering
task updates by multiple criteria. To mitigate any performance impact
from merging task proximities, only clients with explore tasks in the
current zone are checked during client movement updates.

Items and npcs still support goallists but it would be possible to
denormalize entries into delimited strings to combine with the match
lists. This would also decouple task goals from reward lists.

The client task update functions were refactored to run through a single
filtering function which significantly reduces duplicated code from the
legacy task system. This will also make it easier to later implement
any unhandled types.

Since the new fields will handle filtering single entries and lists
based on having values set, `goalmethod` now only distinguishes quest
controlled from source controlled.

This is a breaking api change, `taskexploredarea` has been removed
since explore ids no longer exist.
This commit is contained in:
hg 2022-09-01 20:18:21 -04:00 committed by GitHub
parent 8851b410d2
commit 7482cfc066
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 659 additions and 1344 deletions

View File

@ -260,7 +260,6 @@ SET(repositories
repositories/base/base_pets_equipmentset_repository.h repositories/base/base_pets_equipmentset_repository.h
repositories/base/base_pets_equipmentset_entries_repository.h repositories/base/base_pets_equipmentset_entries_repository.h
repositories/base/base_player_titlesets_repository.h repositories/base/base_player_titlesets_repository.h
repositories/base/base_proximities_repository.h
repositories/base/base_quest_globals_repository.h repositories/base/base_quest_globals_repository.h
repositories/base/base_raid_details_repository.h repositories/base/base_raid_details_repository.h
repositories/base/base_raid_members_repository.h repositories/base/base_raid_members_repository.h
@ -437,7 +436,6 @@ SET(repositories
repositories/pets_equipmentset_repository.h repositories/pets_equipmentset_repository.h
repositories/pets_equipmentset_entries_repository.h repositories/pets_equipmentset_entries_repository.h
repositories/player_titlesets_repository.h repositories/player_titlesets_repository.h
repositories/proximities_repository.h
repositories/quest_globals_repository.h repositories/quest_globals_repository.h
repositories/raid_details_repository.h repositories/raid_details_repository.h
repositories/raid_members_repository.h repositories/raid_members_repository.h

View File

@ -225,7 +225,6 @@ namespace DatabaseSchema {
"pets_beastlord_data", "pets_beastlord_data",
"pets_equipmentset", "pets_equipmentset",
"pets_equipmentset_entries", "pets_equipmentset_entries",
"proximities",
"skill_caps", "skill_caps",
"spawn2", "spawn2",
"spawn_conditions", "spawn_conditions",

View File

@ -1,393 +0,0 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
*/
#ifndef EQEMU_BASE_PROXIMITIES_REPOSITORY_H
#define EQEMU_BASE_PROXIMITIES_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseProximitiesRepository {
public:
struct Proximities {
uint32_t zoneid;
uint32_t exploreid;
float minx;
float maxx;
float miny;
float maxy;
float minz;
float maxz;
};
static std::string PrimaryKey()
{
return std::string("zoneid");
}
static std::vector<std::string> Columns()
{
return {
"zoneid",
"exploreid",
"minx",
"maxx",
"miny",
"maxy",
"minz",
"maxz",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"zoneid",
"exploreid",
"minx",
"maxx",
"miny",
"maxy",
"minz",
"maxz",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("proximities");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static Proximities NewEntity()
{
Proximities e{};
e.zoneid = 0;
e.exploreid = 0;
e.minx = 0.000000;
e.maxx = 0.000000;
e.miny = 0.000000;
e.maxy = 0.000000;
e.minz = 0.000000;
e.maxz = 0.000000;
return e;
}
static Proximities GetProximities(
const std::vector<Proximities> &proximitiess,
int proximities_id
)
{
for (auto &proximities : proximitiess) {
if (proximities.zoneid == proximities_id) {
return proximities;
}
}
return NewEntity();
}
static Proximities FindOne(
Database& db,
int proximities_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
BaseSelect(),
proximities_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
Proximities e{};
e.zoneid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.exploreid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.minx = strtof(row[2], nullptr);
e.maxx = strtof(row[3], nullptr);
e.miny = strtof(row[4], nullptr);
e.maxy = strtof(row[5], nullptr);
e.minz = strtof(row[6], nullptr);
e.maxz = strtof(row[7], nullptr);
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int proximities_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
proximities_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const Proximities &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.zoneid));
v.push_back(columns[1] + " = " + std::to_string(e.exploreid));
v.push_back(columns[2] + " = " + std::to_string(e.minx));
v.push_back(columns[3] + " = " + std::to_string(e.maxx));
v.push_back(columns[4] + " = " + std::to_string(e.miny));
v.push_back(columns[5] + " = " + std::to_string(e.maxy));
v.push_back(columns[6] + " = " + std::to_string(e.minz));
v.push_back(columns[7] + " = " + std::to_string(e.maxz));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.zoneid
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static Proximities InsertOne(
Database& db,
Proximities e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.zoneid));
v.push_back(std::to_string(e.exploreid));
v.push_back(std::to_string(e.minx));
v.push_back(std::to_string(e.maxx));
v.push_back(std::to_string(e.miny));
v.push_back(std::to_string(e.maxy));
v.push_back(std::to_string(e.minz));
v.push_back(std::to_string(e.maxz));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.zoneid = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<Proximities> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.zoneid));
v.push_back(std::to_string(e.exploreid));
v.push_back(std::to_string(e.minx));
v.push_back(std::to_string(e.maxx));
v.push_back(std::to_string(e.miny));
v.push_back(std::to_string(e.maxy));
v.push_back(std::to_string(e.minz));
v.push_back(std::to_string(e.maxz));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<Proximities> All(Database& db)
{
std::vector<Proximities> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Proximities e{};
e.zoneid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.exploreid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.minx = strtof(row[2], nullptr);
e.maxx = strtof(row[3], nullptr);
e.miny = strtof(row[4], nullptr);
e.maxy = strtof(row[5], nullptr);
e.minz = strtof(row[6], nullptr);
e.maxz = strtof(row[7], nullptr);
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<Proximities> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<Proximities> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Proximities e{};
e.zoneid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.exploreid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.minx = strtof(row[2], nullptr);
e.maxx = strtof(row[3], nullptr);
e.miny = strtof(row[4], nullptr);
e.maxy = strtof(row[5], nullptr);
e.minz = strtof(row[6], nullptr);
e.maxz = strtof(row[7], nullptr);
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
};
#endif //EQEMU_BASE_PROXIMITIES_REPOSITORY_H

View File

@ -25,15 +25,25 @@ public:
int32_t step; int32_t step;
uint8_t activitytype; uint8_t activitytype;
std::string target_name; std::string target_name;
std::string item_list;
std::string skill_list;
std::string spell_list;
std::string description_override;
uint32_t goalid;
std::string goal_match_list;
uint32_t goalmethod; uint32_t goalmethod;
int32_t goalcount; int32_t goalcount;
uint32_t delivertonpc; std::string description_override;
uint32_t npc_id;
uint32_t npc_goal_id;
std::string npc_match_list;
uint32_t item_id;
uint32_t item_goal_id;
std::string item_id_list;
std::string item_list;
int32_t dz_switch_id;
float min_x;
float min_y;
float min_z;
float max_x;
float max_y;
float max_z;
std::string skill_list;
std::string spell_list;
std::string zones; std::string zones;
int32_t zone_version; int32_t zone_version;
int8_t optional; int8_t optional;
@ -53,15 +63,25 @@ public:
"step", "step",
"activitytype", "activitytype",
"target_name", "target_name",
"item_list",
"skill_list",
"spell_list",
"description_override",
"goalid",
"goal_match_list",
"goalmethod", "goalmethod",
"goalcount", "goalcount",
"delivertonpc", "description_override",
"npc_id",
"npc_goal_id",
"npc_match_list",
"item_id",
"item_goal_id",
"item_id_list",
"item_list",
"dz_switch_id",
"min_x",
"min_y",
"min_z",
"max_x",
"max_y",
"max_z",
"skill_list",
"spell_list",
"zones", "zones",
"zone_version", "zone_version",
"optional", "optional",
@ -77,15 +97,25 @@ public:
"step", "step",
"activitytype", "activitytype",
"target_name", "target_name",
"item_list",
"skill_list",
"spell_list",
"description_override",
"goalid",
"goal_match_list",
"goalmethod", "goalmethod",
"goalcount", "goalcount",
"delivertonpc", "description_override",
"npc_id",
"npc_goal_id",
"npc_match_list",
"item_id",
"item_goal_id",
"item_id_list",
"item_list",
"dz_switch_id",
"min_x",
"min_y",
"min_z",
"max_x",
"max_y",
"max_z",
"skill_list",
"spell_list",
"zones", "zones",
"zone_version", "zone_version",
"optional", "optional",
@ -135,15 +165,25 @@ public:
e.step = 0; e.step = 0;
e.activitytype = 0; e.activitytype = 0;
e.target_name = ""; e.target_name = "";
e.item_list = "";
e.skill_list = "-1";
e.spell_list = "0";
e.description_override = "";
e.goalid = 0;
e.goal_match_list = "";
e.goalmethod = 0; e.goalmethod = 0;
e.goalcount = 1; e.goalcount = 1;
e.delivertonpc = 0; e.description_override = "";
e.npc_id = 0;
e.npc_goal_id = 0;
e.npc_match_list = "";
e.item_id = 0;
e.item_goal_id = 0;
e.item_id_list = "";
e.item_list = "";
e.dz_switch_id = 0;
e.min_x = 0;
e.min_y = 0;
e.min_z = 0;
e.max_x = 0;
e.max_y = 0;
e.max_z = 0;
e.skill_list = "-1";
e.spell_list = "0";
e.zones = ""; e.zones = "";
e.zone_version = -1; e.zone_version = -1;
e.optional = 0; e.optional = 0;
@ -188,18 +228,28 @@ public:
e.step = static_cast<int32_t>(atoi(row[3])); e.step = static_cast<int32_t>(atoi(row[3]));
e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.target_name = row[5] ? row[5] : ""; e.target_name = row[5] ? row[5] : "";
e.item_list = row[6] ? row[6] : ""; e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.skill_list = row[7] ? row[7] : ""; e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.spell_list = row[8] ? row[8] : ""; e.description_override = row[8] ? row[8] : "";
e.description_override = row[9] ? row[9] : ""; e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.goalid = static_cast<uint32_t>(strtoul(row[10], nullptr, 10)); e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.goal_match_list = row[11] ? row[11] : ""; e.npc_match_list = row[11] ? row[11] : "";
e.goalmethod = static_cast<uint32_t>(strtoul(row[12], nullptr, 10)); e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[13])); e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.delivertonpc = static_cast<uint32_t>(strtoul(row[14], nullptr, 10)); e.item_id_list = row[14] ? row[14] : "";
e.zones = row[15] ? row[15] : ""; e.item_list = row[15] ? row[15] : "";
e.zone_version = static_cast<int32_t>(atoi(row[16])); e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.optional = static_cast<int8_t>(atoi(row[17])); e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
return e; return e;
} }
@ -239,18 +289,28 @@ public:
v.push_back(columns[3] + " = " + std::to_string(e.step)); v.push_back(columns[3] + " = " + std::to_string(e.step));
v.push_back(columns[4] + " = " + std::to_string(e.activitytype)); v.push_back(columns[4] + " = " + std::to_string(e.activitytype));
v.push_back(columns[5] + " = '" + Strings::Escape(e.target_name) + "'"); v.push_back(columns[5] + " = '" + Strings::Escape(e.target_name) + "'");
v.push_back(columns[6] + " = '" + Strings::Escape(e.item_list) + "'"); v.push_back(columns[6] + " = " + std::to_string(e.goalmethod));
v.push_back(columns[7] + " = '" + Strings::Escape(e.skill_list) + "'"); v.push_back(columns[7] + " = " + std::to_string(e.goalcount));
v.push_back(columns[8] + " = '" + Strings::Escape(e.spell_list) + "'"); v.push_back(columns[8] + " = '" + Strings::Escape(e.description_override) + "'");
v.push_back(columns[9] + " = '" + Strings::Escape(e.description_override) + "'"); v.push_back(columns[9] + " = " + std::to_string(e.npc_id));
v.push_back(columns[10] + " = " + std::to_string(e.goalid)); v.push_back(columns[10] + " = " + std::to_string(e.npc_goal_id));
v.push_back(columns[11] + " = '" + Strings::Escape(e.goal_match_list) + "'"); v.push_back(columns[11] + " = '" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(columns[12] + " = " + std::to_string(e.goalmethod)); v.push_back(columns[12] + " = " + std::to_string(e.item_id));
v.push_back(columns[13] + " = " + std::to_string(e.goalcount)); v.push_back(columns[13] + " = " + std::to_string(e.item_goal_id));
v.push_back(columns[14] + " = " + std::to_string(e.delivertonpc)); v.push_back(columns[14] + " = '" + Strings::Escape(e.item_id_list) + "'");
v.push_back(columns[15] + " = '" + Strings::Escape(e.zones) + "'"); v.push_back(columns[15] + " = '" + Strings::Escape(e.item_list) + "'");
v.push_back(columns[16] + " = " + std::to_string(e.zone_version)); v.push_back(columns[16] + " = " + std::to_string(e.dz_switch_id));
v.push_back(columns[17] + " = " + std::to_string(e.optional)); v.push_back(columns[17] + " = " + std::to_string(e.min_x));
v.push_back(columns[18] + " = " + std::to_string(e.min_y));
v.push_back(columns[19] + " = " + std::to_string(e.min_z));
v.push_back(columns[20] + " = " + std::to_string(e.max_x));
v.push_back(columns[21] + " = " + std::to_string(e.max_y));
v.push_back(columns[22] + " = " + std::to_string(e.max_z));
v.push_back(columns[23] + " = '" + Strings::Escape(e.skill_list) + "'");
v.push_back(columns[24] + " = '" + Strings::Escape(e.spell_list) + "'");
v.push_back(columns[25] + " = '" + Strings::Escape(e.zones) + "'");
v.push_back(columns[26] + " = " + std::to_string(e.zone_version));
v.push_back(columns[27] + " = " + std::to_string(e.optional));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@ -278,15 +338,25 @@ public:
v.push_back(std::to_string(e.step)); v.push_back(std::to_string(e.step));
v.push_back(std::to_string(e.activitytype)); v.push_back(std::to_string(e.activitytype));
v.push_back("'" + Strings::Escape(e.target_name) + "'"); v.push_back("'" + Strings::Escape(e.target_name) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back("'" + Strings::Escape(e.skill_list) + "'");
v.push_back("'" + Strings::Escape(e.spell_list) + "'");
v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.goalid));
v.push_back("'" + Strings::Escape(e.goal_match_list) + "'");
v.push_back(std::to_string(e.goalmethod)); v.push_back(std::to_string(e.goalmethod));
v.push_back(std::to_string(e.goalcount)); v.push_back(std::to_string(e.goalcount));
v.push_back(std::to_string(e.delivertonpc)); v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.npc_goal_id));
v.push_back("'" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_goal_id));
v.push_back("'" + Strings::Escape(e.item_id_list) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back(std::to_string(e.dz_switch_id));
v.push_back(std::to_string(e.min_x));
v.push_back(std::to_string(e.min_y));
v.push_back(std::to_string(e.min_z));
v.push_back(std::to_string(e.max_x));
v.push_back(std::to_string(e.max_y));
v.push_back(std::to_string(e.max_z));
v.push_back("'" + Strings::Escape(e.skill_list) + "'");
v.push_back("'" + Strings::Escape(e.spell_list) + "'");
v.push_back("'" + Strings::Escape(e.zones) + "'"); v.push_back("'" + Strings::Escape(e.zones) + "'");
v.push_back(std::to_string(e.zone_version)); v.push_back(std::to_string(e.zone_version));
v.push_back(std::to_string(e.optional)); v.push_back(std::to_string(e.optional));
@ -325,15 +395,25 @@ public:
v.push_back(std::to_string(e.step)); v.push_back(std::to_string(e.step));
v.push_back(std::to_string(e.activitytype)); v.push_back(std::to_string(e.activitytype));
v.push_back("'" + Strings::Escape(e.target_name) + "'"); v.push_back("'" + Strings::Escape(e.target_name) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back("'" + Strings::Escape(e.skill_list) + "'");
v.push_back("'" + Strings::Escape(e.spell_list) + "'");
v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.goalid));
v.push_back("'" + Strings::Escape(e.goal_match_list) + "'");
v.push_back(std::to_string(e.goalmethod)); v.push_back(std::to_string(e.goalmethod));
v.push_back(std::to_string(e.goalcount)); v.push_back(std::to_string(e.goalcount));
v.push_back(std::to_string(e.delivertonpc)); v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.npc_goal_id));
v.push_back("'" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_goal_id));
v.push_back("'" + Strings::Escape(e.item_id_list) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back(std::to_string(e.dz_switch_id));
v.push_back(std::to_string(e.min_x));
v.push_back(std::to_string(e.min_y));
v.push_back(std::to_string(e.min_z));
v.push_back(std::to_string(e.max_x));
v.push_back(std::to_string(e.max_y));
v.push_back(std::to_string(e.max_z));
v.push_back("'" + Strings::Escape(e.skill_list) + "'");
v.push_back("'" + Strings::Escape(e.spell_list) + "'");
v.push_back("'" + Strings::Escape(e.zones) + "'"); v.push_back("'" + Strings::Escape(e.zones) + "'");
v.push_back(std::to_string(e.zone_version)); v.push_back(std::to_string(e.zone_version));
v.push_back(std::to_string(e.optional)); v.push_back(std::to_string(e.optional));
@ -376,18 +456,28 @@ public:
e.step = static_cast<int32_t>(atoi(row[3])); e.step = static_cast<int32_t>(atoi(row[3]));
e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.target_name = row[5] ? row[5] : ""; e.target_name = row[5] ? row[5] : "";
e.item_list = row[6] ? row[6] : ""; e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.skill_list = row[7] ? row[7] : ""; e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.spell_list = row[8] ? row[8] : ""; e.description_override = row[8] ? row[8] : "";
e.description_override = row[9] ? row[9] : ""; e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.goalid = static_cast<uint32_t>(strtoul(row[10], nullptr, 10)); e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.goal_match_list = row[11] ? row[11] : ""; e.npc_match_list = row[11] ? row[11] : "";
e.goalmethod = static_cast<uint32_t>(strtoul(row[12], nullptr, 10)); e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[13])); e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.delivertonpc = static_cast<uint32_t>(strtoul(row[14], nullptr, 10)); e.item_id_list = row[14] ? row[14] : "";
e.zones = row[15] ? row[15] : ""; e.item_list = row[15] ? row[15] : "";
e.zone_version = static_cast<int32_t>(atoi(row[16])); e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.optional = static_cast<int8_t>(atoi(row[17])); e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
all_entries.push_back(e); all_entries.push_back(e);
} }
@ -418,18 +508,28 @@ public:
e.step = static_cast<int32_t>(atoi(row[3])); e.step = static_cast<int32_t>(atoi(row[3]));
e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.activitytype = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.target_name = row[5] ? row[5] : ""; e.target_name = row[5] ? row[5] : "";
e.item_list = row[6] ? row[6] : ""; e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.skill_list = row[7] ? row[7] : ""; e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.spell_list = row[8] ? row[8] : ""; e.description_override = row[8] ? row[8] : "";
e.description_override = row[9] ? row[9] : ""; e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.goalid = static_cast<uint32_t>(strtoul(row[10], nullptr, 10)); e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.goal_match_list = row[11] ? row[11] : ""; e.npc_match_list = row[11] ? row[11] : "";
e.goalmethod = static_cast<uint32_t>(strtoul(row[12], nullptr, 10)); e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[13])); e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.delivertonpc = static_cast<uint32_t>(strtoul(row[14], nullptr, 10)); e.item_id_list = row[14] ? row[14] : "";
e.zones = row[15] ? row[15] : ""; e.item_list = row[15] ? row[15] : "";
e.zone_version = static_cast<int32_t>(atoi(row[16])); e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.optional = static_cast<int8_t>(atoi(row[17])); e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
all_entries.push_back(e); all_entries.push_back(e);
} }

View File

@ -1,50 +0,0 @@
#ifndef EQEMU_PROXIMITIES_REPOSITORY_H
#define EQEMU_PROXIMITIES_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_proximities_repository.h"
class ProximitiesRepository: public BaseProximitiesRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* ProximitiesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* ProximitiesRepository::GetWhereNeverExpires()
* ProximitiesRepository::GetWhereXAndY()
* ProximitiesRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_PROXIMITIES_REPOSITORY_H

View File

@ -18,8 +18,7 @@
// Command Codes for worldserver ServerOP_ReloadTasks // Command Codes for worldserver ServerOP_ReloadTasks
#define RELOADTASKS 0 #define RELOADTASKS 0
#define RELOADTASKGOALLISTS 1 #define RELOADTASKGOALLISTS 1
#define RELOADTASKPROXIMITIES 2 #define RELOADTASKSETS 2
#define RELOADTASKSETS 3
typedef enum { typedef enum {
METHODSINGLEID = 0, METHODSINGLEID = 0,
@ -77,17 +76,28 @@ struct ActivityInformation {
std::string description_override; // overrides auto generated description -- default empty, max length 128 std::string description_override; // overrides auto generated description -- default empty, max length 128
int skill_id; // older clients, first id from above int skill_id; // older clients, first id from above
int spell_id; // older clients, first id from above int spell_id; // older clients, first id from above
int goal_id;
std::string goal_match_list;
TaskMethodType goal_method; TaskMethodType goal_method;
int goal_count; int goal_count;
int deliver_to_npc; uint32_t npc_id;
uint32_t npc_goal_id;
std::string npc_match_list; // delimited by '|' for partial name matches but also supports ids
uint32_t item_id;
uint32_t item_goal_id;
std::string item_id_list; // delimited by '|' to support multiple item ids
int dz_switch_id;
float min_x;
float min_y;
float min_z;
float max_x;
float max_y;
float max_z;
std::vector<int> zone_ids; std::vector<int> zone_ids;
std::string zones; // IDs ; separated, ZoneID is the first in this list for older clients -- default empty string, max length 64 std::string zones; // IDs ; separated, ZoneID is the first in this list for older clients -- default empty string, max length 64
int zone_version; int zone_version;
bool optional; bool optional;
bool has_area; // non-database field
inline bool CheckZone(int zone_id, int version) inline bool CheckZone(int zone_id, int version) const
{ {
if (zone_ids.empty()) { if (zone_ids.empty()) {
return true; return true;
@ -166,7 +176,7 @@ struct ActivityInformation {
out.WriteInt32(zone_ids.empty() ? 0 : zone_ids.front()); out.WriteInt32(zone_ids.empty() ? 0 : zone_ids.front());
} }
out.WriteInt32(activity_type == TaskActivityType::Touch ? goal_id : 0); // dz_switch_id (maybe add separate field) out.WriteInt32(dz_switch_id);
out.WriteString(description_override); out.WriteString(description_override);
out.WriteInt32(done_count); out.WriteInt32(done_count);
out.WriteInt8(1); // unknown out.WriteInt8(1); // unknown

View File

@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9202 #define CURRENT_BINARY_DATABASE_VERSION 9203
#ifdef BOTS #ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029

View File

@ -456,6 +456,7 @@
9200|2022_08_19_zone_expansion_consistency.sql|SELECT * FROM db_version WHERE version >= 9200|empty| 9200|2022_08_19_zone_expansion_consistency.sql|SELECT * FROM db_version WHERE version >= 9200|empty|
9201|2022_08_22_npc_types_heroic_strikethrough.sql|SHOW COLUMNS FROM `npc_types` LIKE 'heroic_strikethrough'|empty| 9201|2022_08_22_npc_types_heroic_strikethrough.sql|SHOW COLUMNS FROM `npc_types` LIKE 'heroic_strikethrough'|empty|
9202|2022_08_24_task_activities_step.sql|SHOW COLUMNS FROM `task_activities` LIKE 'step'|contains|unsigned 9202|2022_08_24_task_activities_step.sql|SHOW COLUMNS FROM `task_activities` LIKE 'step'|contains|unsigned
9203|2022_08_07_replace_task_goals.sql|SHOW COLUMNS FROM `task_activities` LIKE 'item_id'|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,95 @@
-- backup original since this is a complex migration
CREATE TABLE `task_activities_backup_9203` LIKE `task_activities`;
INSERT INTO `task_activities_backup_9203` SELECT * FROM `task_activities`;
ALTER TABLE `task_activities`
CHANGE COLUMN `description_override` `description_override` VARCHAR(128) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci' AFTER `goalcount`,
ADD COLUMN `npc_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `description_override`,
ADD COLUMN `npc_goal_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `npc_id`,
ADD COLUMN `npc_match_list` TEXT NULL DEFAULT NULL AFTER `npc_goal_id`,
ADD COLUMN `item_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `npc_match_list`,
ADD COLUMN `item_goal_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `item_id`,
ADD COLUMN `item_id_list` TEXT NULL DEFAULT NULL AFTER `item_goal_id`,
CHANGE COLUMN `item_list` `item_list` VARCHAR(128) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci' AFTER `item_id_list`,
ADD COLUMN `dz_switch_id` INT(11) NOT NULL DEFAULT '0' AFTER `delivertonpc`,
ADD COLUMN `min_x` FLOAT NOT NULL DEFAULT 0 AFTER `dz_switch_id`,
ADD COLUMN `min_y` FLOAT NOT NULL DEFAULT 0 AFTER `min_x`,
ADD COLUMN `min_z` FLOAT NOT NULL DEFAULT 0 AFTER `min_y`,
ADD COLUMN `max_x` FLOAT NOT NULL DEFAULT 0 AFTER `min_z`,
ADD COLUMN `max_y` FLOAT NOT NULL DEFAULT 0 AFTER `max_x`,
ADD COLUMN `max_z` FLOAT NOT NULL DEFAULT 0 AFTER `max_y`,
CHANGE COLUMN `skill_list` `skill_list` VARCHAR(64) NOT NULL DEFAULT '-1' COLLATE 'latin1_swedish_ci' AFTER `max_z`,
CHANGE COLUMN `spell_list` `spell_list` VARCHAR(64) NOT NULL DEFAULT '0' COLLATE 'latin1_swedish_ci' AFTER `skill_list`;
-- move Explore (5) goalid proximities to the new location fields
-- does not migrate where zone was different and ignores lists (unsupported)
UPDATE `task_activities`
INNER JOIN `proximities`
ON `task_activities`.`goalid` = `proximities`.`exploreid`
AND CAST(`task_activities`.`zones` AS INT) = `proximities`.`zoneid`
SET
`task_activities`.`goalid` = 0,
`task_activities`.`min_x` = `proximities`.`minx`,
`task_activities`.`min_y` = `proximities`.`miny`,
`task_activities`.`min_z` = `proximities`.`minz`,
`task_activities`.`max_x` = `proximities`.`maxx`,
`task_activities`.`max_y` = `proximities`.`maxy`,
`task_activities`.`max_z` = `proximities`.`maxz`
WHERE
`task_activities`.`goalmethod` = 0
AND `task_activities`.`activitytype` = 5;
-- dz_switch_id for Touch (11)
UPDATE `task_activities`
SET `task_activities`.`dz_switch_id` = `task_activities`.`goalid`
WHERE `task_activities`.`goalmethod` = 0
AND `task_activities`.`activitytype` = 11;
-- single item ids for Deliver (1), Loot (3), TradeSkill (6), Fish (7), Forage (8)
UPDATE `task_activities`
SET `task_activities`.`item_id` = `task_activities`.`goalid`
WHERE `task_activities`.`goalmethod` = 0
AND `task_activities`.`activitytype` IN (1, 3, 6, 7, 8);
-- item goallist id
UPDATE `task_activities`
SET `task_activities`.`item_goal_id` = `task_activities`.`goalid`
WHERE `task_activities`.`goalmethod` = 1
AND `task_activities`.`activitytype` IN (1, 3, 6, 7, 8);
-- item id match list
UPDATE `task_activities`
SET `task_activities`.`item_id_list` = `task_activities`.`goal_match_list`
WHERE `task_activities`.`goalmethod` = 1
AND `task_activities`.`activitytype` IN (1, 3, 6, 7, 8);
-- single npc ids for Kill (2), SpeakWith (4)
UPDATE `task_activities`
SET `task_activities`.`npc_id` = `task_activities`.`goalid`
WHERE `task_activities`.`goalmethod` = 0
AND `task_activities`.`activitytype` IN (2, 4);
-- npc goallist id
UPDATE `task_activities`
SET `task_activities`.`npc_goal_id` = `task_activities`.`goalid`
WHERE `task_activities`.`goalmethod` = 1
AND `task_activities`.`activitytype` IN (2, 4);
-- npc match list
UPDATE `task_activities`
SET `task_activities`.`npc_match_list` = `task_activities`.`goal_match_list`
WHERE `task_activities`.`goalmethod` = 1
AND `task_activities`.`activitytype` IN (2, 4);
-- delivertonpc npc_ids for Deliver (1), GiveCash (100)
UPDATE `task_activities`
SET `task_activities`.`npc_id` = `task_activities`.`delivertonpc`
WHERE `task_activities`.`activitytype` IN (1, 100);
ALTER TABLE `task_activities`
DROP COLUMN `goalid`,
DROP COLUMN `goal_match_list`,
DROP COLUMN `delivertonpc`;
-- leave proximities table backup in case of regressions
ALTER TABLE `proximities` RENAME `proximities_backup_9203`;

View File

@ -142,7 +142,6 @@ SET(zone_sources
task_client_state.cpp task_client_state.cpp
task_goal_list_manager.cpp task_goal_list_manager.cpp
task_manager.cpp task_manager.cpp
task_proximity_manager.cpp
tasks.cpp tasks.cpp
titles.cpp titles.cpp
tradeskills.cpp tradeskills.cpp
@ -268,7 +267,6 @@ SET(zone_headers
task_client_state.h task_client_state.h
task_goal_list_manager.h task_goal_list_manager.h
task_manager.h task_manager.h
task_proximity_manager.h
tasks.h tasks.h
titles.h titles.h
trap.h trap.h

View File

@ -2474,7 +2474,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
give_exp_client->GetCleanName(), give_exp_client->GetCleanName(),
GetNPCTypeID() GetNPCTypeID()
); );
task_manager->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID(), this); task_manager->HandleUpdateTasksOnKill(give_exp_client, this);
} }
if (kr) { if (kr) {

View File

@ -1193,7 +1193,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language);
if(RuleB(TaskSystem, EnableTaskSystem)) { if(RuleB(TaskSystem, EnableTaskSystem)) {
if(UpdateTasksOnSpeakWith(tar->GetNPCTypeID())) { if (UpdateTasksOnSpeakWith(tar)) {
tar->DoQuestPause(this); tar->DoQuestPause(this);
} }
} }

View File

@ -1139,48 +1139,32 @@ public:
} }
inline void UpdateTasksForItem( inline void UpdateTasksForItem(
TaskActivityType activity_type, TaskActivityType activity_type,
NPC* npc,
int item_id, int item_id,
int count = 1 int count = 1
) )
{ {
if (task_state) { if (task_state) {
task_state->UpdateTasksForItem(this, activity_type, item_id, count); task_state->UpdateTasksForItem(this, activity_type, npc, item_id, count);
} }
} }
inline void UpdateTasksOnExplore(int explore_id) inline void UpdateTasksOnExplore(const glm::vec4& pos)
{ {
if (task_state) { if (task_state) {
task_state->UpdateTasksOnExplore( task_state->UpdateTasksOnExplore(this, pos);
this,
explore_id
);
} }
} }
inline bool UpdateTasksOnSpeakWith(int npc_type_id) inline bool UpdateTasksOnSpeakWith(NPC* npc)
{ {
if (task_state) { return task_state && task_state->UpdateTasksOnSpeakWith(this, npc);
return task_state->UpdateTasksOnSpeakWith(
this,
npc_type_id
);
}
else { return false; }
} }
inline bool UpdateTasksOnDeliver( inline bool UpdateTasksOnDeliver(
std::list<EQ::ItemInstance *> &items, std::list<EQ::ItemInstance *> &items,
int cash, int cash,
int npc_type_id NPC* npc
) )
{ {
if (task_state) { return task_state && task_state->UpdateTasksOnDeliver(this, items, cash, npc);
return task_state->UpdateTasksOnDeliver(
this,
items,
cash,
npc_type_id
);
}
else { return false; }
} }
void UpdateTasksOnTouchSwitch(int dz_switch_id) void UpdateTasksOnTouchSwitch(int dz_switch_id)
{ {
@ -1235,12 +1219,7 @@ public:
inline void ProcessTaskProximities(float x, float y, float z) inline void ProcessTaskProximities(float x, float y, float z)
{ {
if (task_state) { if (task_state) {
task_state->ProcessTaskProximities( task_state->ProcessTaskProximities(this, x, y, z);
this,
x,
y,
z
);
} }
} }
inline void AssignTask( inline void AssignTask(
@ -1252,22 +1231,19 @@ public:
task_state->AcceptNewTask(this, task_id, npc_id, std::time(nullptr), enforce_level_requirement); task_state->AcceptNewTask(this, task_id, npc_id, std::time(nullptr), enforce_level_requirement);
} }
} }
inline int ActiveSpeakTask(int npc_type_id) inline int ActiveSpeakTask(NPC* npc)
{ {
if (task_state) { if (task_state) {
return task_state->ActiveSpeakTask(npc_type_id); return task_state->ActiveSpeakTask(this, npc);
} }
else { else {
return 0; return 0;
} }
} }
inline int ActiveSpeakActivity(int npc_type_id, int task_id) inline int ActiveSpeakActivity(NPC* npc, int task_id)
{ {
if (task_state) { if (task_state) {
return task_state->ActiveSpeakActivity( return task_state->ActiveSpeakActivity(this, npc, task_id);
npc_type_id,
task_id
);
} }
else { return 0; } else { return 0; }
} }

View File

@ -1381,7 +1381,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
/* Update any tasks that have an activity to loot this item */ /* Update any tasks that have an activity to loot this item */
if (RuleB(TaskSystem, EnableTaskSystem)) if (RuleB(TaskSystem, EnableTaskSystem))
client->UpdateTasksForItem(TaskActivityType::Loot, item->ID); client->UpdateTasksForItem(TaskActivityType::Loot, IsNPCCorpse() ? CastToNPC() : nullptr, item->ID);
/* Remove it from Corpse */ /* Remove it from Corpse */
if (item_data) { if (item_data) {

View File

@ -1162,11 +1162,6 @@ void Perl__resettaskactivity(int task_id, int activity_id)
quest_manager.resettaskactivity(task_id, activity_id); quest_manager.resettaskactivity(task_id, activity_id);
} }
void Perl__taskexploredarea(int explore_id)
{
quest_manager.taskexploredarea(explore_id);
}
void Perl__assigntask(int task_id) void Perl__assigntask(int task_id)
{ {
quest_manager.assigntask(task_id); quest_manager.assigntask(task_id);
@ -4238,7 +4233,6 @@ void perl_register_quest()
package.add("summonitem", (void(*)(int, int))&Perl__summonitem); package.add("summonitem", (void(*)(int, int))&Perl__summonitem);
package.add("surname", &Perl__surname); package.add("surname", &Perl__surname);
package.add("targlobal", &Perl__targlobal); package.add("targlobal", &Perl__targlobal);
package.add("taskexploredarea", &Perl__taskexploredarea);
package.add("taskselector", &Perl__taskselector); package.add("taskselector", &Perl__taskselector);
package.add("task_setselector", &Perl__task_setselector); package.add("task_setselector", &Perl__task_setselector);
package.add("tasktimeleft", &Perl__tasktimeleft); package.add("tasktimeleft", &Perl__tasktimeleft);

View File

@ -367,7 +367,7 @@ void Client::GoFish()
PushItemOnCursor(*inst); PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if (RuleB(TaskSystem, EnableTaskSystem)) if (RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(TaskActivityType::Fish, food_id); UpdateTasksForItem(TaskActivityType::Fish, nullptr, food_id);
safe_delete(inst); safe_delete(inst);
inst = m_inv.GetItem(EQ::invslot::slotCursor); inst = m_inv.GetItem(EQ::invslot::slotCursor);
@ -486,7 +486,7 @@ void Client::ForageItem(bool guarantee) {
PushItemOnCursor(*inst); PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if(RuleB(TaskSystem, EnableTaskSystem)) { if(RuleB(TaskSystem, EnableTaskSystem)) {
UpdateTasksForItem(TaskActivityType::Forage, foragedfood); UpdateTasksForItem(TaskActivityType::Forage, nullptr, foragedfood);
} }
safe_delete(inst); safe_delete(inst);

View File

@ -45,13 +45,6 @@ void command_task(Client *c, const Seperator *sep)
Saylink::Silent("#task reload lists", "reload lists") Saylink::Silent("#task reload lists", "reload lists")
).c_str() ).c_str()
); );
c->Message(
Chat::White,
fmt::format(
"--- [{}] Reload proximity information",
Saylink::Silent("#task reload prox", "reload prox")
).c_str()
);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
@ -139,13 +132,6 @@ void command_task(Client *c, const Seperator *sep)
Saylink::Silent("#task reload lists", "reload lists") Saylink::Silent("#task reload lists", "reload lists")
).c_str() ).c_str()
); );
c->Message(
Chat::White,
fmt::format(
"--- [{}] Reload proximity information",
Saylink::Silent("#task reload prox", "reload prox")
).c_str()
);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
@ -210,11 +196,6 @@ void command_task(Client *c, const Seperator *sep)
worldserver.SendReloadTasks(RELOADTASKGOALLISTS); worldserver.SendReloadTasks(RELOADTASKGOALLISTS);
c->Message(Chat::Yellow, "Successfully reloaded goal lists."); c->Message(Chat::Yellow, "Successfully reloaded goal lists.");
return; return;
} else if (!strcasecmp(sep->arg[2], "prox")) {
c->Message(Chat::Yellow, "Attempting to reload task proximites.");
worldserver.SendReloadTasks(RELOADTASKPROXIMITIES);
c->Message(Chat::Yellow, "Successfully reloaded task proximites.");
return;
} else if (!strcasecmp(sep->arg[2], "sets")) { } else if (!strcasecmp(sep->arg[2], "sets")) {
c->Message(Chat::Yellow, "Attempting to reload task sets."); c->Message(Chat::Yellow, "Attempting to reload task sets.");
worldserver.SendReloadTasks(RELOADTASKSETS); worldserver.SendReloadTasks(RELOADTASKSETS);

View File

@ -699,10 +699,6 @@ void lua_reset_task_activity(int task, int activity) {
quest_manager.resettaskactivity(task, activity); quest_manager.resettaskactivity(task, activity);
} }
void lua_task_explored_area(int explore_id) {
quest_manager.taskexploredarea(explore_id);
}
void lua_assign_task(int task_id) { void lua_assign_task(int task_id) {
quest_manager.assigntask(task_id); quest_manager.assigntask(task_id);
} }
@ -3717,7 +3713,6 @@ luabind::scope lua_register_general() {
luabind::def("get_task_activity_done_count", &lua_get_task_activity_done_count), luabind::def("get_task_activity_done_count", &lua_get_task_activity_done_count),
luabind::def("update_task_activity", &lua_update_task_activity), luabind::def("update_task_activity", &lua_update_task_activity),
luabind::def("reset_task_activity", &lua_reset_task_activity), luabind::def("reset_task_activity", &lua_reset_task_activity),
luabind::def("task_explored_area", &lua_task_explored_area),
luabind::def("assign_task", &lua_assign_task), luabind::def("assign_task", &lua_assign_task),
luabind::def("fail_task", &lua_fail_task), luabind::def("fail_task", &lua_fail_task),
luabind::def("task_time_left", &lua_task_time_left), luabind::def("task_time_left", &lua_task_time_left),

View File

@ -2363,13 +2363,6 @@ void QuestManager::resettaskactivity(int task, int activity) {
initiator->ResetTaskActivity(task, activity); initiator->ResetTaskActivity(task, activity);
} }
void QuestManager::taskexploredarea(int exploreid) {
QuestManagerCurrentQuestVars();
if(RuleB(TaskSystem, EnableTaskSystem) && initiator)
initiator->UpdateTasksOnExplore(exploreid);
}
void QuestManager::assigntask(int taskid, bool enforce_level_requirement) { void QuestManager::assigntask(int taskid, bool enforce_level_requirement) {
QuestManagerCurrentQuestVars(); QuestManagerCurrentQuestVars();
@ -2432,16 +2425,16 @@ int QuestManager::nexttaskinset(int taskset, int taskid) {
int QuestManager::activespeaktask() { int QuestManager::activespeaktask() {
QuestManagerCurrentQuestVars(); QuestManagerCurrentQuestVars();
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner) if (RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && owner->IsNPC())
return initiator->ActiveSpeakTask(owner->GetNPCTypeID()); return initiator->ActiveSpeakTask(owner->CastToNPC());
return 0; return 0;
} }
int QuestManager::activespeakactivity(int taskid) { int QuestManager::activespeakactivity(int taskid) {
QuestManagerCurrentQuestVars(); QuestManagerCurrentQuestVars();
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner) if (RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && owner->IsNPC())
return initiator->ActiveSpeakActivity(owner->GetNPCTypeID(), taskid); return initiator->ActiveSpeakActivity(owner->CastToNPC(), taskid);
return 0; return 0;
} }

View File

@ -219,7 +219,6 @@ public:
int gettaskactivitydonecount(int task, int activity); int gettaskactivitydonecount(int task, int activity);
void updatetaskactivity(int task, int activity, int count, bool ignore_quest_update = false); void updatetaskactivity(int task, int activity, int count, bool ignore_quest_update = false);
void resettaskactivity(int task, int activity); void resettaskactivity(int task, int activity);
void taskexploredarea(int exploreid);
void assigntask(int taskid, bool enforce_level_requirement = false); void assigntask(int taskid, bool enforce_level_requirement = false);
void failtask(int taskid); void failtask(int taskid);
int tasktimeleft(int taskid); int tasktimeleft(int taskid);

View File

@ -364,11 +364,11 @@ static void DeleteCompletedTaskFromDatabase(int character_id, int task_id)
); );
} }
bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation &task_info) bool ClientTaskState::UnlockActivities(Client* client, ClientTaskInformation& task_info)
{ {
LogTasksDetail( LogTasksDetail(
"[UnlockActivities] Fetching task info for character_id [{}] task [{}] slot [{}] accepted_time [{}] updated [{}]", "[UnlockActivities] Fetching task info for character_id [{}] task [{}] slot [{}] accepted_time [{}] updated [{}]",
character_id, client->CharacterID(),
task_info.task_id, task_info.task_id,
task_info.slot, task_info.slot,
task_info.accepted_time, task_info.accepted_time,
@ -386,7 +386,7 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation &
if (task_info.activity[i].activity_id >= 0) { if (task_info.activity[i].activity_id >= 0) {
LogTasksDetail( LogTasksDetail(
"[UnlockActivities] character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}]", "[UnlockActivities] character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}]",
character_id, client->CharacterID(),
task_info.task_id, task_info.task_id,
task_info.activity[i].activity_id, task_info.activity[i].activity_id,
task_info.activity[i].done_count, task_info.activity[i].done_count,
@ -411,9 +411,12 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation &
if (res.is_task_complete && RuleB(TaskSystem, RecordCompletedTasks)) if (res.is_task_complete && RuleB(TaskSystem, RecordCompletedTasks))
{ {
RecordCompletedTask(character_id, *task, task_info); RecordCompletedTask(client->CharacterID(), *task, task_info);
} }
// check if client has an explore task in current zone to enable task explore processing
m_has_explore_task = HasExploreTask(client);
return res.is_task_complete; return res.is_task_complete;
} }
@ -456,188 +459,237 @@ void ClientTaskState::RecordCompletedTask(uint32_t character_id, const TaskInfor
} }
} }
bool ClientTaskState::UpdateTasksOnSpeakWith(Client *client, int npc_type_id) const TaskInformation* ClientTaskState::GetTaskData(const ClientTaskInformation& client_task) const
{ {
return UpdateTasksByNPC(client, TaskActivityType::SpeakWith, npc_type_id); if (client_task.task_id == TASKSLOTEMPTY)
{
return nullptr;
} }
bool ClientTaskState::UpdateTasksByNPC(Client *client, TaskActivityType activity_type, int npc_type_id) return task_manager->m_task_data[client_task.task_id];
}
bool ClientTaskState::CanUpdate(Client* client, const TaskUpdateFilter& filter, int task_id,
const ActivityInformation& activity, const ClientActivityInformation& client_activity) const
{
if (activity.goal_method == METHODQUEST && activity.goal_method != filter.method)
{ {
int is_updating = false;
if (!HasActiveTasks()) {
return false; return false;
} }
// loop over the union of tasks and quests // todo: some tasks do allow hidden/unlocked elements to silently update
for (auto &active_task : m_active_tasks) { if (client_activity.activity_state != ActivityActive)
auto current_task = &active_task; {
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
// Check if there are any active kill activities for this p_task_data
auto p_task_data = task_manager->m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
return false; return false;
} }
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { if (activity.activity_type != filter.type)
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
// We are only interested in Kill activities
if (activity_info->activity_type != activity_type) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
LogTasks(
"[UPDATE] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check",
client->GetName(),
current_task->task_id,
activity_id,
static_cast<int32_t>(activity_type),
npc_type_id
);
continue;
}
// Is the activity_information to kill this type of NPC ?
switch (activity_info->goal_method) {
case METHODSINGLEID:
if (activity_info->goal_id != npc_type_id) {
continue;
}
break;
case METHODLIST:
if (!task_manager->m_goal_list_manager.IsInList(
activity_info->goal_id,
npc_type_id
) && !TaskGoalListManager::IsInMatchList(
activity_info->goal_match_list,
std::to_string(npc_type_id)
)) {
continue;
}
break;
default:
// If METHODQUEST, don't updated the activity_information here
continue;
}
// We found an active p_task_data to kill this type of NPC, so increment the done count
LogTasksDetail("Calling increment done count ByNPC");
IncrementDoneCount(client, p_task_data, current_task->slot, activity_id);
is_updating = true;
}
}
return is_updating;
}
int ClientTaskState::ActiveSpeakTask(int npc_type_id)
{ {
return false;
}
if (activity.dz_switch_id != 0 && activity.dz_switch_id != filter.dz_switch_id)
{
return false;
}
// item is only checked for updates that provide an item (unlike npc which may be null for non-npcs)
if (activity.item_id != 0 && filter.item_id != 0 && activity.item_id != filter.item_id)
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item id filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (activity.npc_id != 0 && (!filter.npc || activity.npc_id != filter.npc->GetNPCTypeID()))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc id filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (!activity.CheckZone(zone->GetZoneID(), zone->GetInstanceVersion()))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed zone filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (activity.has_area && !filter.ignore_area && RuleB(TaskSystem, EnableTaskProximity))
{
const glm::vec4& pos = filter.use_pos ? filter.pos : client->GetPosition();
if (pos.x < activity.min_x || pos.x > activity.max_x ||
pos.y < activity.min_y || pos.y > activity.max_y ||
pos.z < activity.min_z || pos.z > activity.max_z)
{
LogTasksDetail("[CanUpdate] client [{}] task [{}]-[{}] failed area filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
}
if (activity.item_goal_id != 0 && filter.item_id != 0 &&
!task_manager->m_goal_list_manager.IsInList(activity.item_goal_id, filter.item_id))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item goallist filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (!activity.item_id_list.empty() && filter.item_id != 0 &&
!TaskGoalListManager::IsInMatchList(activity.item_id_list, std::to_string(filter.item_id)))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item match filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (activity.npc_goal_id != 0 && (!filter.npc ||
!task_manager->m_goal_list_manager.IsInList(activity.npc_goal_id, filter.npc->GetNPCTypeID())))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc goallist filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
// npc filter supports both npc names and ids in match lists
if (!activity.npc_match_list.empty() && (!filter.npc ||
(!TaskGoalListManager::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetName()) &&
!TaskGoalListManager::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetCleanName()) &&
!TaskGoalListManager::IsInMatchList(activity.npc_match_list, std::to_string(filter.npc->GetNPCTypeID())))))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc match filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
return true;
}
bool ClientTaskState::UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count)
{
if (!task_manager)
{
return false;
}
bool any_updated = false;
for (const auto& client_task : m_active_tasks)
{
const TaskInformation* task = GetTaskData(client_task);
if (!task)
{
continue;
}
// legacy eqemu task update logic loops through group on kill of npc to update a single task
// shared tasks only require one client to receive an update to propagate
if (filter.type == TaskActivityType::Kill && task->type == TaskType::Shared && client != filter.exp_client)
{
continue;
}
for (const ClientActivityInformation& client_activity : client_task.activity)
{
const ActivityInformation& activity = task->activity_information[client_activity.activity_id];
if (CanUpdate(client, filter, client_task.task_id, activity, client_activity))
{
LogTasks("[UpdateTasks] client [{}] task [{}] activity [{}] increment [{}]",
client->GetName(), client_task.task_id, client_activity.activity_id, count);
IncrementDoneCount(client, task, client_task.slot, client_activity.activity_id, count);
any_updated = true;
break; // only one element updated per task, move to next task
}
}
}
return any_updated;
}
std::pair<int, int> ClientTaskState::FindTask(Client* client, const TaskUpdateFilter& filter) const
{
if (!task_manager)
{
return std::make_pair(0, 0);
}
for (const auto& client_task : m_active_tasks)
{
const TaskInformation* task = GetTaskData(client_task);
if (!task || (filter.task_id != 0 && client_task.task_id != filter.task_id))
{
continue;
}
for (const ClientActivityInformation& client_activity : client_task.activity)
{
const ActivityInformation& activity = task->activity_information[client_activity.activity_id];
if (CanUpdate(client, filter, client_task.task_id, activity, client_activity))
{
return std::make_pair(client_task.task_id, client_activity.activity_id);
}
}
}
return std::make_pair(0, 0);
}
bool ClientTaskState::HasExploreTask(Client* client) const
{
TaskUpdateFilter filter{};
filter.type = TaskActivityType::Explore;
filter.ignore_area = true; // we don't care if client is currently in the explore area
auto result = FindTask(client, filter);
bool has_explore = result.first != 0;
LogTasksDetail("[HasExploreTask] client [{}] has explore task in current zone [{}]", client->GetName(), has_explore);
return has_explore;
}
bool ClientTaskState::UpdateTasksOnSpeakWith(Client* client, NPC* npc)
{
return UpdateTasksByNPC(client, TaskActivityType::SpeakWith, npc);
}
bool ClientTaskState::UpdateTasksByNPC(Client* client, TaskActivityType type, NPC* npc)
{
TaskUpdateFilter filter{};
filter.type = type;
filter.npc = npc;
return UpdateTasks(client, filter);
}
int ClientTaskState::ActiveSpeakTask(Client* client, NPC* npc)
{
// This method is to be used from Perl quests only and returns the task_id of the first // This method is to be used from Perl quests only and returns the task_id of the first
// active task found which has an active SpeakWith activity_information for this NPC. // active task found which has an active SpeakWith activity_information for this NPC.
if (!HasActiveTasks()) { TaskUpdateFilter filter{};
return 0; filter.type = TaskActivityType::SpeakWith;
filter.npc = npc;
filter.method = METHODQUEST;
auto result = FindTask(client, filter);
return result.first; // task id
} }
// loop over the union of tasks and quests int ClientTaskState::ActiveSpeakActivity(Client* client, NPC* npc, int task_id)
for (auto &active_task : m_active_tasks) {
auto current_task = &active_task;
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
TaskInformation *p_task_data = task_manager->m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
continue;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
if (activity_info->activity_type != TaskActivityType::SpeakWith) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
continue;
}
// Is the activity_information to speak with this type of NPC ?
if (activity_info->goal_method == METHODQUEST && activity_info->goal_id == npc_type_id) {
return current_task->task_id;
}
}
}
return 0;
}
int ClientTaskState::ActiveSpeakActivity(int npc_type_id, int task_id)
{ {
// This method is to be used from Perl quests only and returns the activity_id of the first // This method is to be used from Perl quests only and returns the activity_id of the first
// active activity_information found in the specified task which is to SpeakWith this NPC. // active activity_information found in the specified task which is to SpeakWith this NPC.
if (!HasActiveTasks()) {
return -1; if (task_id <= 0 || task_id >= MAXTASKS)
} {
if (task_id <= 0 || task_id >= MAXTASKS) {
return -1; return -1;
} }
// loop over the union of tasks and quests TaskUpdateFilter filter{};
for (auto &active_task : m_active_tasks) { filter.type = TaskActivityType::SpeakWith;
auto current_task = &active_task; filter.npc = npc;
if (current_task->task_id != task_id) { filter.method = METHODQUEST;
continue; filter.task_id = task_id;
auto result = FindTask(client, filter);
return result.first != 0 ? result.second : -1; // activity id
} }
TaskInformation *p_task_data = task_manager->m_task_data[current_task->task_id]; void ClientTaskState::UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count)
if (p_task_data == nullptr) {
continue;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
if (activity_info->activity_type != TaskActivityType::SpeakWith) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
continue;
}
// Is the activity_information to speak with this type of NPC ?
if (activity_info->goal_method == METHODQUEST && activity_info->goal_id == npc_type_id) {
return activity_id;
}
}
return 0;
}
return 0;
}
void ClientTaskState::UpdateTasksForItem(Client *client, TaskActivityType activity_type, int item_id, int count)
{ {
// This method updates the client's task activities of the specified type which relate // This method updates the client's task activities of the specified type which relate
@ -645,269 +697,62 @@ void ClientTaskState::UpdateTasksForItem(Client *client, TaskActivityType activi
// //
// Type should be one of ActivityLoot, ActivityTradeSkill, ActivityFish or ActivityForage // Type should be one of ActivityLoot, ActivityTradeSkill, ActivityFish or ActivityForage
// If the client has no tasks, there is nothing further to check. LogTasks("[UpdateTasksForItem] activity_type [{}] item_id [{}]", static_cast<int>(type), item_id);
LogTasks( TaskUpdateFilter filter{};
"[UpdateTasksForItem] activity_type [{}] item_id [{}]", filter.type = type;
static_cast<int32_t>(activity_type), filter.npc = npc; // looting may filter on npc id or name
item_id filter.item_id = item_id;
);
if (!HasActiveTasks()) { UpdateTasks(client, filter, count);
return;
} }
// loop over the union of tasks and quests void ClientTaskState::UpdateTasksOnExplore(Client* client, const glm::vec4& pos)
for (auto &active_task : m_active_tasks) {
auto current_task = &active_task;
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
// Check if there are any active loot activities for this task
TaskInformation *p_task_data = task_manager->m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
return;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
// We are only interested in the ActivityType we were called with
if (activity_info->activity_type != activity_type) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
LogTasks(
"[UpdateTasksForItem] Error: Character [{}] activity_information type [{}] for Item [{}] failed zone check",
client->GetName(),
static_cast<int32_t>(activity_type),
item_id
);
continue;
}
// Is the activity_information related to this item ?
//
switch (activity_info->goal_method) {
case METHODSINGLEID:
if (activity_info->goal_id != item_id) { continue; }
break;
case METHODLIST:
if (!task_manager->m_goal_list_manager.IsInList(
activity_info->goal_id,
item_id
) && !TaskGoalListManager::IsInMatchList(
activity_info->goal_match_list,
std::to_string(item_id)
)) { continue; }
break;
default:
// If METHODQUEST, don't updated the activity_information here
continue;
}
// We found an active task related to this item, so increment the done count
LogTasksDetail("[UpdateTasksForItem] Calling increment done count ForItem");
IncrementDoneCount(client, p_task_data, current_task->slot, activity_id, count);
}
}
}
void ClientTaskState::UpdateTasksOnExplore(Client *client, int explore_id)
{ {
LogTasks("[UpdateTasksOnExplore] explore_id [{}]", explore_id); LogTasksDetail("[UpdateTasksOnExplore] client [{}]", client->GetName());
if (!HasActiveTasks()) { TaskUpdateFilter filter{};
return; filter.type = TaskActivityType::Explore;
} filter.pos = pos;
filter.use_pos = true;
// loop over the union of tasks and quests UpdateTasks(client, filter);
for (auto &active_task : m_active_tasks) {
auto current_task = &active_task;
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
// Check if there are any active explore activities for this task
TaskInformation *task_data = task_manager->m_task_data[current_task->task_id];
if (task_data == nullptr) {
return;
}
for (int activity_id = 0; activity_id < task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
// We are only interested in explore activities
if (activity_info->activity_type != TaskActivityType::Explore) {
continue;
}
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
LogTasks(
"[UpdateTasksOnExplore] character [{}] explore_id [{}] failed zone check",
client->GetName(),
explore_id
);
continue;
}
// Is the activity_information to explore this area id ?
switch (activity_info->goal_method) {
case METHODSINGLEID:
if (activity_info->goal_id != explore_id) {
continue;
}
break;
case METHODLIST:
if (!task_manager->m_goal_list_manager.IsInList(
activity_info->goal_id,
explore_id
) && !TaskGoalListManager::IsInMatchList(
activity_info->goal_match_list,
std::to_string(explore_id)
)) {
continue;
}
break;
default:
// If METHODQUEST, don't updated the activity_information here
continue;
}
// We found an active task to explore this area, so set done count to goal count
// (Only a goal count of 1 makes sense for explore activities?)
LogTasks(
"[UpdateTasksOnExplore] character [{}] explore_id [{}] increment on explore",
client->GetName(),
explore_id
);
IncrementDoneCount(
client,
task_data,
current_task->slot,
activity_id,
activity_info->goal_count - current_task->activity[activity_id].done_count
);
}
}
} }
bool ClientTaskState::UpdateTasksOnDeliver( bool ClientTaskState::UpdateTasksOnDeliver(
Client *client, Client *client,
std::list<EQ::ItemInstance *> &items, std::list<EQ::ItemInstance *> &items,
int cash, int cash,
int npc_type_id NPC* npc
) )
{ {
LogTasks("[UpdateTasksOnDeliver] npc [{}]", npc->GetName());
bool is_updated = false; bool is_updated = false;
LogTasks("[UpdateTasksOnDeliver] [{}]", npc_type_id); TaskUpdateFilter filter{};
filter.npc = npc;
if (!HasActiveTasks()) { if (cash != 0)
return false; {
} filter.type = TaskActivityType::GiveCash;
if (UpdateTasks(client, filter, cash))
// loop over the union of tasks and quests {
for (int i = 0; i < MAXACTIVEQUESTS + 1; i++) { // todo: remove used coin and use Deliver with explicit coin fields instead of custom type
auto current_task = &m_active_tasks[i];
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
// Check if there are any active deliver activities for this task
TaskInformation *p_task_data = task_manager->m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
return false;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
// We are only interested in Deliver activities
if (activity_info->activity_type != TaskActivityType::Deliver &&
activity_info->activity_type != TaskActivityType::GiveCash) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
Log(
Logs::General, Logs::Tasks,
"[UPDATE] Char: %s Deliver activity_information failed zone check (current zone %i, need zone "
"%s",
client->GetName(), zone->GetZoneID(), activity_info->zones.c_str());
continue;
}
// Is the activity_information to deliver to this NPCTypeID ?
if (activity_info->deliver_to_npc != npc_type_id) {
continue;
}
// Is the activity_information related to these items ?
//
if ((activity_info->activity_type == TaskActivityType::GiveCash) && cash) {
LogTasks("[UpdateTasksOnDeliver] Increment on GiveCash");
IncrementDoneCount(client, p_task_data, i, activity_id, cash);
is_updated = true;
}
else {
for (auto &item : items) {
switch (activity_info->goal_method) {
case METHODSINGLEID:
if (activity_info->goal_id != item->GetID()) {
continue;
}
break;
case METHODLIST:
if (!task_manager->m_goal_list_manager.IsInList(
activity_info->goal_id,
item->GetID()
) && !TaskGoalListManager::IsInMatchList(
activity_info->goal_match_list,
std::to_string(item->GetID())
)) {
continue;
}
break;
default:
// If METHODQUEST, don't updated the activity_information here
continue;
}
// We found an active task related to this item, so increment the done count
LogTasks("[UpdateTasksOnDeliver] Increment on GiveItem");
IncrementDoneCount(
client,
p_task_data,
current_task->slot,
activity_id,
item->GetCharges() <= 0 ? 1 : item->GetCharges()
);
is_updated = true; is_updated = true;
} }
} }
filter.type = TaskActivityType::Deliver;
for (EQ::ItemInstance* item : items)
{
filter.item_id = item->GetID();
int count = item->IsStackable() ? item->GetCharges() : 1;
if (UpdateTasks(client, filter, count))
{
// todo: remove items used in update (highest in case multiple tasks consume same item)
is_updated = true;
} }
} }
@ -916,69 +761,30 @@ bool ClientTaskState::UpdateTasksOnDeliver(
void ClientTaskState::UpdateTasksOnTouch(Client *client, int dz_switch_id) void ClientTaskState::UpdateTasksOnTouch(Client *client, int dz_switch_id)
{ {
// If the client has no tasks, there is nothing further to check. LogTasks("[UpdateTasksOnTouch] dz switch [{}] ", dz_switch_id);
LogTasks("[UpdateTasksOnTouch] [{}] ", dz_switch_id); TaskUpdateFilter filter{};
filter.type = TaskActivityType::Touch;
filter.dz_switch_id = dz_switch_id;
if (!HasActiveTasks()) { UpdateTasks(client, filter);
return;
} }
// loop over the union of tasks and quests void ClientTaskState::UpdateTasksOnKill(Client* client, Client* exp_client, NPC* npc)
for (auto &active_task : m_active_tasks) { {
auto current_task = &active_task; TaskUpdateFilter filter{};
if (current_task->task_id == TASKSLOTEMPTY) { filter.type = TaskActivityType::Kill;
continue; filter.npc = npc;
} filter.pos = npc->GetPosition(); // or should areas be filtered by client position?
filter.use_pos = true;
filter.exp_client = exp_client;
// Check if there are any active explore activities for this task UpdateTasks(client, filter);
TaskInformation *p_task_data = task_manager->m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
return;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (current_task->activity[activity_id].activity_state != ActivityActive) {
continue;
}
// We are only interested in touch activities
if (activity_info->activity_type != TaskActivityType::Touch) {
continue;
}
if (activity_info->goal_method != METHODSINGLEID) {
continue;
}
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
LogTasks(
"[UpdateTasksOnTouch] character [{}] Touch activity_information failed zone check",
client->GetName()
);
continue;
}
if (activity_info->goal_id != dz_switch_id) {
continue;
}
// We found an active task to zone into this zone, so set done count to goal count
// (Only a goal count of 1 makes sense for touch activities?)
LogTasks("[UpdateTasksOnTouch] Increment on Touch");
IncrementDoneCount(
client,
p_task_data,
current_task->slot,
activity_id,
activity_info->goal_count - current_task->activity[activity_id].done_count
);
}
}
} }
void ClientTaskState::IncrementDoneCount( void ClientTaskState::IncrementDoneCount(
Client *client, Client *client,
TaskInformation *task_information, const TaskInformation* task_information,
int task_index, int task_index,
int activity_id, int activity_id,
int count, int count,
@ -1024,7 +830,7 @@ void ClientTaskState::IncrementDoneCount(
SyncSharedTaskZoneClientDoneCountState( SyncSharedTaskZoneClientDoneCountState(
client, client,
task_information, task_information->type,
task_index, task_index,
activity_id, activity_id,
r->done_count r->done_count
@ -1065,7 +871,7 @@ void ClientTaskState::IncrementDoneCount(
// Flag the activity_information as complete // Flag the activity_information as complete
info->activity[activity_id].activity_state = ActivityCompleted; info->activity[activity_id].activity_state = ActivityCompleted;
// Unlock subsequent activities for this task // Unlock subsequent activities for this task
bool task_complete = UnlockActivities(client->CharacterID(), *info); bool task_complete = UnlockActivities(client, *info);
LogTasks("[IncrementDoneCount] task_complete is [{}]", task_complete); LogTasks("[IncrementDoneCount] task_complete is [{}]", task_complete);
// and by the 'Task Stage Completed' message // and by the 'Task Stage Completed' message
client->SendTaskActivityComplete(info->task_id, activity_id, task_index, task_information->type); client->SendTaskActivityComplete(info->task_id, activity_id, task_index, task_information->type);
@ -1159,7 +965,7 @@ void ClientTaskState::DispatchEventTaskComplete(Client* client, ClientTaskInform
parse->EventPlayer(EVENT_TASK_COMPLETE, client, export_string, 0); parse->EventPlayer(EVENT_TASK_COMPLETE, client, export_string, 0);
} }
void ClientTaskState::RewardTask(Client *client, TaskInformation *task_information, ClientTaskInformation& client_task) void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_information, ClientTaskInformation& client_task)
{ {
if (!task_information || !client || client_task.was_rewarded) { if (!task_information || !client || client_task.was_rewarded) {
@ -2000,6 +1806,7 @@ void ClientTaskState::CancelTask(Client *c, int sequence_number, TaskType task_t
// persistence // persistence
if (remove_from_db) { if (remove_from_db) {
RemoveTask(c, sequence_number, task_type); RemoveTask(c, sequence_number, task_type);
m_has_explore_task = HasExploreTask(c);
} }
} }
@ -2281,7 +2088,7 @@ void ClientTaskState::AcceptNewTask(
active_slot->activity[activity_id].updated = true; active_slot->activity[activity_id].updated = true;
} }
UnlockActivities(client->CharacterID(), *active_slot); UnlockActivities(client, *active_slot);
if (task->type == TaskType::Quest) { if (task->type == TaskType::Quest) {
m_active_task_count++; m_active_task_count++;
@ -2332,6 +2139,10 @@ void ClientTaskState::AcceptNewTask(
void ClientTaskState::ProcessTaskProximities(Client *client, float x, float y, float z) void ClientTaskState::ProcessTaskProximities(Client *client, float x, float y, float z)
{ {
if (!m_has_explore_task) {
return;
}
float last_x = client->ProximityX(); float last_x = client->ProximityX();
float last_y = client->ProximityY(); float last_y = client->ProximityY();
float last_z = client->ProximityZ(); float last_z = client->ProximityZ();
@ -2340,19 +2151,7 @@ void ClientTaskState::ProcessTaskProximities(Client *client, float x, float y, f
return; return;
} }
LogTasksDetail("[ProcessTaskProximities] Checking proximities for Position x[{}] y[{}] z[{}]", x, y, z); UpdateTasksOnExplore(client, glm::vec4(x, y, z, 0.0f));
int explore_id = task_manager->m_proximity_manager.CheckProximities(x, y, z);
if (explore_id > 0) {
LogTasksDetail(
"[ProcessTaskProximities] Position x[{}] y[{}] z[{}] is within proximity explore_id [{}]",
x,
y,
z,
explore_id
);
UpdateTasksOnExplore(client, explore_id);
}
} }
void ClientTaskState::SharedTaskIncrementDoneCount( void ClientTaskState::SharedTaskIncrementDoneCount(
@ -2505,7 +2304,7 @@ void ClientTaskState::ListTaskTimers(Client* client)
} }
} }
void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& client_task, TaskInformation& task) void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& client_task, const TaskInformation& task)
{ {
if (task.replay_timer_seconds > 0) if (task.replay_timer_seconds > 0)
{ {
@ -2557,7 +2356,7 @@ void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& clie
// zone-level before sending updates to world // zone-level before sending updates to world
void ClientTaskState::SyncSharedTaskZoneClientDoneCountState( void ClientTaskState::SyncSharedTaskZoneClientDoneCountState(
Client *p_client, Client *p_client,
TaskInformation *p_information, TaskType type,
int task_index, int task_index,
int activity_id, int activity_id,
uint32 done_count uint32 done_count
@ -2566,7 +2365,7 @@ void ClientTaskState::SyncSharedTaskZoneClientDoneCountState(
for (auto &e : entity_list.GetClientList()) { for (auto &e : entity_list.GetClientList()) {
auto c = e.second; auto c = e.second;
if (c->GetSharedTaskId() == p_client->GetSharedTaskId()) { if (c->GetSharedTaskId() == p_client->GetSharedTaskId()) {
auto t = c->GetTaskState()->GetClientTaskInfo(p_information->type, task_index); auto t = c->GetTaskState()->GetClientTaskInfo(type, task_index);
if (t == nullptr) { if (t == nullptr) {
continue; continue;
} }

View File

@ -16,6 +16,20 @@ struct TaskOffer
uint16_t npc_entity_id; uint16_t npc_entity_id;
}; };
struct TaskUpdateFilter
{
int task_id = 0;
int dz_switch_id = 0;
uint32_t item_id = 0;
glm::vec4 pos;
bool use_pos = false; // if true uses pos instead of client position for area filters
bool ignore_area = false; // if true, area check is disabled
NPC* npc = nullptr;
Client* exp_client = nullptr; // used by Kill tasks to filter shared task updates
TaskActivityType type = TaskActivityType::None;
TaskMethodType method = TaskMethodType::METHODSINGLEID;
};
class ClientTaskState { class ClientTaskState {
public: public:
@ -41,23 +55,23 @@ public:
void CancelAllTasks(Client *client); void CancelAllTasks(Client *client);
void RemoveTask(Client *client, int sequence_number, TaskType task_type); void RemoveTask(Client *client, int sequence_number, TaskType task_type);
void RemoveTaskByTaskID(Client *client, uint32 task_id); void RemoveTaskByTaskID(Client *client, uint32 task_id);
bool UpdateTasksByNPC(Client *client, TaskActivityType activity_type, int npc_type_id); bool UpdateTasksByNPC(Client* client, TaskActivityType type, NPC* npc);
void UpdateTasksForItem(Client *client, TaskActivityType activity_type, int item_id, int count = 1); void UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count = 1);
void UpdateTasksOnExplore(Client *client, int explore_id); void UpdateTasksOnExplore(Client* client, const glm::vec4& loc);
bool UpdateTasksOnSpeakWith(Client *client, int npc_type_id); bool UpdateTasksOnSpeakWith(Client* client, NPC* npc);
bool UpdateTasksOnDeliver(Client *client, std::list<EQ::ItemInstance *> &items, int cash, int npc_type_id); bool UpdateTasksOnDeliver(Client* client, std::list<EQ::ItemInstance*>& items, int cash, NPC* npc);
void UpdateTasksOnTouch(Client *client, int dz_switch_id); void UpdateTasksOnTouch(Client *client, int dz_switch_id);
void ProcessTaskProximities(Client *client, float x, float y, float z); void ProcessTaskProximities(Client *client, float x, float y, float z);
bool TaskOutOfTime(TaskType task_type, int index); bool TaskOutOfTime(TaskType task_type, int index);
void TaskPeriodicChecks(Client *client); void TaskPeriodicChecks(Client *client);
void SendTaskHistory(Client *client, int task_index); void SendTaskHistory(Client *client, int task_index);
void RewardTask(Client *client, TaskInformation *task_information, ClientTaskInformation& client_task); void RewardTask(Client* client, const TaskInformation* task_information, ClientTaskInformation& client_task);
void EnableTask(int character_id, int task_count, int *task_list); void EnableTask(int character_id, int task_count, int *task_list);
void DisableTask(int character_id, int task_count, int *task_list); void DisableTask(int character_id, int task_count, int *task_list);
bool IsTaskEnabled(int task_id); bool IsTaskEnabled(int task_id);
int EnabledTaskCount(int task_set_id); int EnabledTaskCount(int task_set_id);
int ActiveSpeakTask(int npc_type_id); int ActiveSpeakTask(Client* client, NPC* npc);
int ActiveSpeakActivity(int npc_type_id, int task_id); int ActiveSpeakActivity(Client* client, NPC* npc, int task_id);
int ActiveTasksInSet(int task_set_id); int ActiveTasksInSet(int task_set_id);
int CompletedTasksInSet(int task_set_id); int CompletedTasksInSet(int task_set_id);
bool HasSlotForTask(TaskInformation *task); bool HasSlotForTask(TaskInformation *task);
@ -67,6 +81,7 @@ public:
void LockSharedTask(Client* client, bool lock); void LockSharedTask(Client* client, bool lock);
void ClearLastOffers() { m_last_offers.clear(); } void ClearLastOffers() { m_last_offers.clear(); }
bool CanAcceptNewTask(Client* client, int task_id, int npc_entity_id) const; bool CanAcceptNewTask(Client* client, int task_id, int npc_entity_id) const;
bool HasExploreTask(Client* client) const;
inline bool HasFreeTaskSlot() { return m_active_task.task_id == TASKSLOTEMPTY; } inline bool HasFreeTaskSlot() { return m_active_task.task_id == TASKSLOTEMPTY; }
@ -85,21 +100,28 @@ public:
bool HasActiveSharedTask(); bool HasActiveSharedTask();
private: private:
void AddReplayTimer(Client *client, ClientTaskInformation& client_task, TaskInformation& task); const TaskInformation* GetTaskData(const ClientTaskInformation& client_task) const;
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 AddOffer(int task_id, uint16_t npc_entity_id) { m_last_offers.push_back({task_id, npc_entity_id}); };
void AddReplayTimer(Client *client, ClientTaskInformation& client_task, const TaskInformation& task);
bool CanUpdate(Client* client, const TaskUpdateFilter& filter, int task_id,
const ActivityInformation& activity, const ClientActivityInformation& client_activity) const;
void DispatchEventTaskComplete(Client* client, ClientTaskInformation& client_task, int activity_id);
std::pair<int, int> FindTask(Client* client, const TaskUpdateFilter& filter) const;
void RecordCompletedTask(uint32_t character_id, const TaskInformation& task, const ClientTaskInformation& client_task); void RecordCompletedTask(uint32_t character_id, const TaskInformation& task, const ClientTaskInformation& client_task);
void UpdateTasksOnKill(Client* client, Client* exp_client, NPC* npc);
bool UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count = 1);
void IncrementDoneCount( void IncrementDoneCount(
Client *client, Client *client,
TaskInformation *task_information, const TaskInformation* task_information,
int task_index, int task_index,
int activity_id, int activity_id,
int count = 1, int count = 1,
bool ignore_quest_update = false bool ignore_quest_update = false
); );
bool UnlockActivities(int character_id, ClientTaskInformation &task_info); bool UnlockActivities(Client* client, ClientTaskInformation& task_info);
inline ClientTaskInformation *GetClientTaskInfo(TaskType task_type, int index) inline ClientTaskInformation *GetClientTaskInfo(TaskType task_type, int index)
{ {
@ -143,12 +165,13 @@ private:
std::vector<CompletedTaskInformation> m_completed_tasks; std::vector<CompletedTaskInformation> m_completed_tasks;
int m_last_completed_task_loaded; int m_last_completed_task_loaded;
std::vector<TaskOffer> m_last_offers; std::vector<TaskOffer> m_last_offers;
bool m_has_explore_task = false;
static void ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c); static void ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c);
void SyncSharedTaskZoneClientDoneCountState( void SyncSharedTaskZoneClientDoneCountState(
Client *p_client, Client *p_client,
TaskInformation *p_information, TaskType type,
int task_index, int task_index,
int activity_id, int activity_id,
uint32 done_count uint32 done_count
@ -156,5 +179,4 @@ private:
bool HasActiveTasks(); bool HasActiveTasks();
}; };
#endif //EQEMU_TASK_CLIENT_STATE_H #endif //EQEMU_TASK_CLIENT_STATE_H

View File

@ -225,12 +225,30 @@ bool TaskManager::LoadTasks(int single_task)
activity_data->spell_list = task_activity.spell_list; activity_data->spell_list = task_activity.spell_list;
activity_data->spell_id = Strings::IsNumber(task_activity.spell_list) ? std::stoi(task_activity.spell_list) : 0; // for older clients activity_data->spell_id = Strings::IsNumber(task_activity.spell_list) ? std::stoi(task_activity.spell_list) : 0; // for older clients
activity_data->description_override = task_activity.description_override; activity_data->description_override = task_activity.description_override;
activity_data->goal_id = task_activity.goalid; activity_data->npc_id = task_activity.npc_id;
activity_data->npc_goal_id = task_activity.npc_goal_id;
activity_data->npc_match_list = task_activity.npc_match_list;
activity_data->item_id = task_activity.item_id;
activity_data->item_goal_id = task_activity.item_goal_id;
activity_data->item_id_list = task_activity.item_id_list;
activity_data->dz_switch_id = task_activity.dz_switch_id;
activity_data->goal_method = (TaskMethodType) task_activity.goalmethod; activity_data->goal_method = (TaskMethodType) task_activity.goalmethod;
activity_data->goal_match_list = task_activity.goal_match_list;
activity_data->goal_count = task_activity.goalcount; activity_data->goal_count = task_activity.goalcount;
activity_data->deliver_to_npc = task_activity.delivertonpc; activity_data->min_x = task_activity.min_x;
activity_data->min_y = task_activity.min_y;
activity_data->min_z = task_activity.min_z;
activity_data->max_x = task_activity.max_x;
activity_data->max_y = task_activity.max_y;
activity_data->max_z = task_activity.max_z;
activity_data->zone_version = task_activity.zone_version >= 0 ? task_activity.zone_version : -1; activity_data->zone_version = task_activity.zone_version >= 0 ? task_activity.zone_version : -1;
activity_data->has_area = false;
if (std::abs(task_activity.max_x - task_activity.min_x) > 0.0f &&
std::abs(task_activity.max_y - task_activity.min_y) > 0.0f &&
std::abs(task_activity.max_z - task_activity.min_z) > 0.0f)
{
activity_data->has_area = true;
}
// zones // zones
activity_data->zones = task_activity.zones; activity_data->zones = task_activity.zones;
@ -246,13 +264,12 @@ bool TaskManager::LoadTasks(int single_task)
activity_data->optional = task_activity.optional; activity_data->optional = task_activity.optional;
LogTasksDetail( LogTasksDetail(
"[LoadTasks] (Activity) task_id [{}] activity_id [{}] slot [{}] activity_type [{}] goal_id [{}] goal_method [{}] goal_count [{}] zones [{}]" "[LoadTasks] (Activity) task_id [{}] activity_id [{}] slot [{}] activity_type [{}] goal_method [{}] goal_count [{}] zones [{}]"
" target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}]", " target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}]",
task_id, task_id,
activity_id, activity_id,
m_task_data[task_id]->activity_count, m_task_data[task_id]->activity_count,
static_cast<int32_t>(activity_data->activity_type), static_cast<int32_t>(activity_data->activity_type),
activity_data->goal_id,
activity_data->goal_method, activity_data->goal_method,
activity_data->goal_count, activity_data->goal_count,
activity_data->zones.c_str(), activity_data->zones.c_str(),
@ -1515,7 +1532,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
client_task_state->m_active_task.slot client_task_state->m_active_task.slot
); );
if (client_task_state->m_active_task.task_id != TASKSLOTEMPTY) { if (client_task_state->m_active_task.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(character_id, client_task_state->m_active_task); client_task_state->UnlockActivities(client, client_task_state->m_active_task);
// purely debugging // purely debugging
LogTasksDetail( LogTasksDetail(
@ -1552,13 +1569,13 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
client_task_state->m_active_shared_task.slot client_task_state->m_active_shared_task.slot
); );
if (client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) { if (client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(character_id, client_task_state->m_active_shared_task); client_task_state->UnlockActivities(client, client_task_state->m_active_shared_task);
} }
// quests (max 20 or 40 depending on client) // quests (max 20 or 40 depending on client)
for (auto &active_quest : client_task_state->m_active_quests) { for (auto &active_quest : client_task_state->m_active_quests) {
if (active_quest.task_id != TASKSLOTEMPTY) { if (active_quest.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(character_id, active_quest); client_task_state->UnlockActivities(client, active_quest);
} }
} }
@ -1781,7 +1798,7 @@ void TaskManager::SyncClientSharedTaskStateToLocal(
} }
} }
void TaskManager::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id, NPC* npc) void TaskManager::HandleUpdateTasksOnKill(Client* client, NPC* npc)
{ {
for (auto &c: client->GetPartyMembers()) { for (auto &c: client->GetPartyMembers()) {
if (!c->ClientDataLoaded() || !c->HasTaskState()) { if (!c->ClientDataLoaded() || !c->HasTaskState()) {
@ -1790,96 +1807,7 @@ void TaskManager::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id, NP
LogTasksDetail("[HandleUpdateTasksOnKill] Looping through client [{}]", c->GetCleanName()); LogTasksDetail("[HandleUpdateTasksOnKill] Looping through client [{}]", c->GetCleanName());
// loop over the union of tasks and quests c->GetTaskState()->UpdateTasksOnKill(c, client, npc);
for (auto &active_task : c->GetTaskState()->m_active_tasks) {
auto current_task = &active_task;
if (current_task->task_id == TASKSLOTEMPTY) {
continue;
}
// Check if there are any active kill activities for this p_task_data
auto p_task_data = m_task_data[current_task->task_id];
if (p_task_data == nullptr) {
return;
}
for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) {
ClientActivityInformation *client_activity = &current_task->activity[activity_id];
ActivityInformation *activity_info = &p_task_data->activity_information[activity_id];
// We are not interested in completed or hidden activities
if (client_activity->activity_state != ActivityActive) {
continue;
}
// We are only interested in Kill activities
if (activity_info->activity_type != TaskActivityType::Kill) {
continue;
}
// Is there a zone restriction on the activity_information ?
if (!activity_info->CheckZone(zone->GetZoneID(), zone->GetInstanceVersion())) {
LogTasks(
"[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check",
client->GetName(),
current_task->task_id,
activity_id,
static_cast<int32_t>(TaskActivityType::Kill),
npc_type_id
);
continue;
}
// Is the activity_information to kill this type of NPC ?
switch (activity_info->goal_method) {
case METHODSINGLEID:
if (activity_info->goal_id != npc_type_id) {
LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal");
continue;
}
break;
case METHODLIST:
if (!m_goal_list_manager.IsInList(
activity_info->goal_id,
(int) npc_type_id
) && !TaskGoalListManager::IsInMatchList(
activity_info->goal_match_list,
std::to_string(npc_type_id)
) && !TaskGoalListManager::IsInMatchListPartial(
activity_info->goal_match_list,
npc->GetCleanName()
) && !TaskGoalListManager::IsInMatchListPartial(
activity_info->goal_match_list,
npc->GetName()
)) {
LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal");
continue;
}
break;
default:
// If METHODQUEST, don't updated the activity_information here
continue;
}
LogTasksDetail("[HandleUpdateTasksOnKill] passed checks");
// handle actual update
// legacy eqemu task update logic loops through group on kill of npc to update a single task
if (p_task_data->type != TaskType::Shared) {
LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update");
c->GetTaskState()->IncrementDoneCount(c, p_task_data, current_task->slot, activity_id);
continue;
}
LogTasksDetail("[HandleUpdateTasksOnKill] Shared update");
// shared tasks only require one client to receive an update to propagate
if (c == client) {
c->GetTaskState()->IncrementDoneCount(c, p_task_data, current_task->slot, activity_id);
}
}
}
} }
} }

View File

@ -3,7 +3,6 @@
#include "tasks.h" #include "tasks.h"
#include "task_client_state.h" #include "task_client_state.h"
#include "task_proximity_manager.h"
#include "task_goal_list_manager.h" #include "task_goal_list_manager.h"
#include "../common/types.h" #include "../common/types.h"
#include "../common/repositories/character_tasks_repository.h" #include "../common/repositories/character_tasks_repository.h"
@ -24,10 +23,6 @@ public:
int GetActivityCount(int task_id); int GetActivityCount(int task_id);
bool LoadTasks(int single_task = 0); bool LoadTasks(int single_task = 0);
void ReloadGoalLists(); void ReloadGoalLists();
inline void LoadProximities(int zone_id)
{
m_proximity_manager.LoadProximities(zone_id);
}
bool LoadTaskSets(); bool LoadTaskSets();
bool LoadClientState(Client *client, ClientTaskState *client_task_state); bool LoadClientState(Client *client, ClientTaskState *client_task_state);
bool SaveClientState(Client *client, ClientTaskState *client_task_state); bool SaveClientState(Client *client, ClientTaskState *client_task_state);
@ -72,11 +67,10 @@ public:
// shared tasks // shared tasks
void SyncClientSharedTaskState(Client *c, ClientTaskState *cts); void SyncClientSharedTaskState(Client *c, ClientTaskState *cts);
void HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id, NPC* npc); void HandleUpdateTasksOnKill(Client* client, NPC* npc);
private: private:
TaskGoalListManager m_goal_list_manager; TaskGoalListManager m_goal_list_manager;
TaskProximityManager m_proximity_manager;
TaskInformation *m_task_data[MAXTASKS]{}; TaskInformation *m_task_data[MAXTASKS]{};
std::vector<int> m_task_sets[MAXTASKSETS]; std::vector<int> m_task_sets[MAXTASKSETS];
void SendActiveTaskDescription( void SendActiveTaskDescription(

View File

@ -1,80 +0,0 @@
#include "../common/global_define.h"
#include "../common/repositories/proximities_repository.h"
#include "../common/rulesys.h"
#include "client.h"
#include "mob.h"
#include "quest_parser_collection.h"
#include "task_proximity_manager.h"
#include "tasks.h"
#include "zonedb.h"
TaskProximityManager::TaskProximityManager()
{
}
TaskProximityManager::~TaskProximityManager()
{
}
bool TaskProximityManager::LoadProximities(int zone_id)
{
TaskProximity proximity{};
m_task_proximities.clear();
auto proximities = ProximitiesRepository::GetWhere(
content_db,
fmt::format("zoneid = {} ORDER BY `zoneid` ASC", zone_id)
);
for (auto &row: proximities) {
proximity.explore_id = row.exploreid;
proximity.min_x = row.minx;
proximity.max_x = row.maxx;
proximity.min_y = row.miny;
proximity.max_y = row.maxy;
proximity.min_z = row.minz;
proximity.max_z = row.maxz;
m_task_proximities.push_back(proximity);
}
LogTasks("Loaded [{}] Task Proximities", proximities.size());
return true;
}
int TaskProximityManager::CheckProximities(float x, float y, float z)
{
for (auto &task_proximity : m_task_proximities) {
TaskProximity *p_proximity = &task_proximity;
Log(
Logs::General,
Logs::Tasks,
"[Proximity] Checking %8.3f, %8.3f, %8.3f against %8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f",
x,
y,
z,
p_proximity->min_x,
p_proximity->max_x,
p_proximity->min_y,
p_proximity->max_y,
p_proximity->min_z,
p_proximity->max_z
);
if (x < p_proximity->min_x || x > p_proximity->max_x || y < p_proximity->min_y || y > p_proximity->max_y ||
z < p_proximity->min_z || z > p_proximity->max_z) {
continue;
}
return p_proximity->explore_id;
}
return 0;
}

View File

@ -1,27 +0,0 @@
#ifndef EQEMU_TASK_PROXIMITY_MANAGER_H
#define EQEMU_TASK_PROXIMITY_MANAGER_H
struct TaskProximity {
int explore_id;
float min_x;
float max_x;
float min_y;
float max_y;
float min_z;
float max_z;
};
// This class is used for managing proximities so that Quest NPC proximities don't need to be used.
class TaskProximityManager {
public:
TaskProximityManager();
~TaskProximityManager();
bool LoadProximities(int zone_id);
int CheckProximities(float x, float y, float z);
private:
std::vector <TaskProximity> m_task_proximities;
};
#endif //EQEMU_TASK_PROXIMITY_MANAGER_H

View File

@ -1096,7 +1096,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
} }
if (RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
UpdateTasksForItem(TaskActivityType::TradeSkill, itr->first, itr->second); UpdateTasksForItem(TaskActivityType::TradeSkill, nullptr, itr->first, itr->second);
} }
++itr; ++itr;

View File

@ -947,7 +947,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
if(RuleB(TaskSystem, EnableTaskSystem)) { if(RuleB(TaskSystem, EnableTaskSystem)) {
int Cash = trade->cp + (trade->sp * 10) + (trade->gp * 100) + (trade->pp * 1000); int Cash = trade->cp + (trade->sp * 10) + (trade->gp * 100) + (trade->pp * 1000);
if(UpdateTasksOnDeliver(items, Cash, tradingWith->GetNPCTypeID())) { if (UpdateTasksOnDeliver(items, Cash, tradingWith->CastToNPC())) {
if(!tradingWith->IsMoving()) if(!tradingWith->IsMoving())
tradingWith->FaceTarget(this); tradingWith->FaceTarget(this);

View File

@ -3451,10 +3451,6 @@ void WorldServer::HandleReloadTasks(ServerPacket *pack)
task_manager = new TaskManager; task_manager = new TaskManager;
task_manager->LoadTasks(); task_manager->LoadTasks();
if (zone) {
task_manager->LoadProximities(zone->GetZoneID());
}
entity_list.ReloadAllClientsTaskState(); entity_list.ReloadAllClientsTaskState();
} else { } else {
LogTasks("Global reload of Task ID [{}]", rts->task_id); LogTasks("Global reload of Task ID [{}]", rts->task_id);
@ -3464,15 +3460,6 @@ void WorldServer::HandleReloadTasks(ServerPacket *pack)
break; break;
} }
case RELOADTASKPROXIMITIES:
{
if (zone) {
LogTasks("Global reload of all Task Proximities");
task_manager->LoadProximities(zone->GetZoneID());
}
break;
}
case RELOADTASKGOALLISTS: case RELOADTASKGOALLISTS:
{ {
LogTasks("Global reload of all Task Goal Lists"); LogTasks("Global reload of all Task Goal Lists");

View File

@ -977,10 +977,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
tradevar = 0; tradevar = 0;
lootvar = 0; lootvar = 0;
if (RuleB(TaskSystem, EnableTaskSystem)) {
task_manager->LoadProximities(zoneid);
}
short_name = strcpy(new char[strlen(in_short_name)+1], in_short_name); short_name = strcpy(new char[strlen(in_short_name)+1], in_short_name);
strlwr(short_name); strlwr(short_name);
memset(file_name, 0, sizeof(file_name)); memset(file_name, 0, sizeof(file_name));