eqemu-server/zone/task_client_state.h
hg 7482cfc066
[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.
2022-09-01 19:18:21 -05:00

183 lines
7.0 KiB
C++

#ifndef EQEMU_TASK_CLIENT_STATE_H
#define EQEMU_TASK_CLIENT_STATE_H
#include "tasks.h"
#include "../common/types.h"
#include <list>
#include <vector>
#include <string>
#include <algorithm>
constexpr float MAX_TASK_SELECT_DISTANCE = 60.0f; // client closes window at this distance
struct TaskOffer
{
int task_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 {
public:
ClientTaskState();
~ClientTaskState();
void ShowClientTasks(Client* who, Client *to);
inline int GetActiveTaskCount() { return m_active_task_count; }
int GetActiveTaskID(int index);
bool IsTaskActivityCompleted(TaskType task_type, int index, int activity_id);
int GetTaskActivityDoneCount(TaskType task_type, int index, int activity_id);
int GetTaskActivityDoneCountFromTaskID(int task_id, int activity_id);
int GetTaskStartTime(TaskType task_type, int index);
void AcceptNewTask(Client *client, int task_id, int npc_type_id, time_t accept_time, bool enforce_level_requirement = false);
void FailTask(Client *client, int task_id);
int TaskTimeLeft(int task_id);
int IsTaskCompleted(int task_id);
bool IsTaskActive(int task_id);
bool IsTaskActivityActive(int task_id, int activity_id);
ActivityState GetTaskActivityState(TaskType task_type, int index, int activity_id);
void UpdateTaskActivity(Client *client, int task_id, int activity_id, int count, bool ignore_quest_update = false);
void ResetTaskActivity(Client *client, int task_id, int activity_id);
void CancelTask(Client *c, int sequence_number, TaskType task_type, bool remove_from_db = true);
void CancelAllTasks(Client *client);
void RemoveTask(Client *client, int sequence_number, TaskType task_type);
void RemoveTaskByTaskID(Client *client, uint32 task_id);
bool UpdateTasksByNPC(Client* client, TaskActivityType type, NPC* npc);
void UpdateTasksForItem(Client* client, TaskActivityType type, NPC* npc, int item_id, int count = 1);
void UpdateTasksOnExplore(Client* client, const glm::vec4& loc);
bool UpdateTasksOnSpeakWith(Client* client, NPC* npc);
bool UpdateTasksOnDeliver(Client* client, std::list<EQ::ItemInstance*>& items, int cash, NPC* npc);
void UpdateTasksOnTouch(Client *client, int dz_switch_id);
void ProcessTaskProximities(Client *client, float x, float y, float z);
bool TaskOutOfTime(TaskType task_type, int index);
void TaskPeriodicChecks(Client *client);
void SendTaskHistory(Client *client, int task_index);
void RewardTask(Client* client, const TaskInformation* task_information, ClientTaskInformation& client_task);
void EnableTask(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);
int EnabledTaskCount(int task_set_id);
int ActiveSpeakTask(Client* client, NPC* npc);
int ActiveSpeakActivity(Client* client, NPC* npc, int task_id);
int ActiveTasksInSet(int task_set_id);
int CompletedTasksInSet(int task_set_id);
bool HasSlotForTask(TaskInformation *task);
void CreateTaskDynamicZone(Client* client, int task_id, DynamicZone& dz);
void ListTaskTimers(Client* client);
void KickPlayersSharedTask(Client* client);
void LockSharedTask(Client* client, bool lock);
void ClearLastOffers() { m_last_offers.clear(); }
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; }
friend class TaskManager;
// wrapper to call internal IncrementDoneCount
void SharedTaskIncrementDoneCount(
Client *client,
int task_id,
int activity_id,
int done_count,
bool ignore_quest_update = false
);
const ClientTaskInformation &GetActiveSharedTask() const;
bool HasActiveSharedTask();
private:
const TaskInformation* GetTaskData(const ClientTaskInformation& client_task) const;
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 UpdateTasksOnKill(Client* client, Client* exp_client, NPC* npc);
bool UpdateTasks(Client* client, const TaskUpdateFilter& filter, int count = 1);
void IncrementDoneCount(
Client *client,
const TaskInformation* task_information,
int task_index,
int activity_id,
int count = 1,
bool ignore_quest_update = false
);
bool UnlockActivities(Client* client, ClientTaskInformation& task_info);
inline ClientTaskInformation *GetClientTaskInfo(TaskType task_type, int index)
{
ClientTaskInformation *info = nullptr;
switch (task_type) {
case TaskType::Task:
if (index == TASKSLOTTASK) {
info = &m_active_task;
}
break;
case TaskType::Shared:
if (index == TASKSLOTSHAREDTASK) {
info = &m_active_shared_task;
}
break;
case TaskType::Quest:
if (index < MAXACTIVEQUESTS) {
info = &m_active_quests[index];
}
break;
default:
break;
}
return info;
}
union { // easier to loop over
struct {
ClientTaskInformation m_active_task; // only one
// acts as a read-only "view" of data that is managed by world and the internal task
// system largely behaves like other tasks but shims logic to world where necessary
ClientTaskInformation m_active_shared_task; // only one
ClientTaskInformation m_active_quests[MAXACTIVEQUESTS];
};
ClientTaskInformation m_active_tasks[MAXACTIVEQUESTS + 2] = {};
};
// Shared tasks should be limited to 1 as well
int m_active_task_count;
std::vector<int> m_enabled_tasks;
std::vector<CompletedTaskInformation> m_completed_tasks;
int m_last_completed_task_loaded;
std::vector<TaskOffer> m_last_offers;
bool m_has_explore_task = false;
static void ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c);
void SyncSharedTaskZoneClientDoneCountState(
Client *p_client,
TaskType type,
int task_index,
int activity_id,
uint32 done_count
);
bool HasActiveTasks();
};
#endif //EQEMU_TASK_CLIENT_STATE_H