Skip to content

Instantly share code, notes, and snippets.

@mackal
Last active September 1, 2018 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mackal/ca259888d28f3176ae5c726b7d96991a to your computer and use it in GitHub Desktop.
Save mackal/ca259888d28f3176ae5c726b7d96991a to your computer and use it in GitHub Desktop.
diff --git a/common/emu_oplist.h b/common/emu_oplist.h
index 5078cca68..3f4556a07 100644
--- a/common/emu_oplist.h
+++ b/common/emu_oplist.h
@@ -6,6 +6,7 @@ N(OP_0x0347),
N(OP_AAAction),
N(OP_AAExpUpdate),
N(OP_AcceptNewTask),
+N(OP_AcceptNewSharedTask),
N(OP_AckPacket),
N(OP_Action),
N(OP_Action2),
diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h
index a35c264e0..7a56d01ab 100644
--- a/common/eq_packet_structs.h
+++ b/common/eq_packet_structs.h
@@ -3788,6 +3788,14 @@ struct AcceptNewTask_Struct {
uint32 task_master_id; //entity ID
};
+struct AcceptNewSharedTask_Struct {
+ uint32 unknown00;
+ uint32 unknown04;
+ uint32 task_master_id;
+ uint32 task_id;
+ float unknown16;
+};
+
//was all 0's from client, server replied with same op, all 0's
struct CancelTask_Struct {
uint32 SequenceNumber;
diff --git a/common/serialize_buffer.h b/common/serialize_buffer.h
index 9b350b4fa..909b9ca9a 100644
--- a/common/serialize_buffer.h
+++ b/common/serialize_buffer.h
@@ -187,6 +187,7 @@ public:
const unsigned char *buffer() const { return m_buffer; }
friend class BasePacket;
+ friend class ServerPacket;
private:
void Grow(size_t new_size);
diff --git a/common/servertalk.h b/common/servertalk.h
index e1d019fc4..eb357a180 100644
--- a/common/servertalk.h
+++ b/common/servertalk.h
@@ -4,6 +4,7 @@
#include "../common/types.h"
#include "../common/packet_functions.h"
#include "../common/eq_packet_structs.h"
+#include "../common/serialize_buffer.h"
#include "../net/packet.h"
#include <cereal/cereal.hpp>
#include <cereal/types/string.hpp>
@@ -151,6 +152,12 @@
#define ServerOP_LSRemoteAddr 0x1009
#define ServerOP_LSAccountUpdate 0x100A
+#define ServerOP_TaskRequest 0x0300
+#define ServerOP_TaskGrant 0x0301
+#define ServerOP_TaskReject 0x0302
+#define ServerOP_TaskAddPlayer 0x0303
+#define ServerOP_TaskRemovePlayer 0x0304
+
#define ServerOP_EncapPacket 0x2007 // Packet within a packet
#define ServerOP_WorldListUpdate 0x2008
#define ServerOP_WorldListRemove 0x2009
@@ -246,6 +253,19 @@ public:
_rpos = 0;
}
+ ServerPacket(uint16 in_opcode, SerializeBuffer &buf)
+ {
+ compressed = false;
+ size = buf.m_pos;
+ buf.m_pos = 0;
+ opcode = in_opcode;
+ pBuffer = buf.m_buffer;
+ buf.m_buffer = 0;
+ buf.m_capacity = 0;
+ _wpos = 0;
+ _rpos = 0;
+ }
+
ServerPacket* Copy() {
if (this == 0) {
return 0;
diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf
index 9467ab337..016586ba8 100644
--- a/utils/patches/patch_RoF.conf
+++ b/utils/patches/patch_RoF.conf
@@ -563,6 +563,7 @@ OP_TaskHistoryRequest=0x6cf6
OP_TaskHistoryReply=0x25eb
OP_DeclineAllTasks=0x0000
OP_TaskRequestTimer=0x4b76
+OP_AcceptNewSharedTask=0x3e5e
# Title opcodes
OP_NewTitlesAvailable=0x45d1
diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf
index f410a3193..cf6704b55 100644
--- a/utils/patches/patch_RoF2.conf
+++ b/utils/patches/patch_RoF2.conf
@@ -568,6 +568,7 @@ OP_TaskHistoryRequest=0x5f1c
OP_TaskHistoryReply=0x3d05
OP_DeclineAllTasks=0x0000
OP_TaskRequestTimer=0x7a48
+OP_AcceptNewSharedTask=0x6646
# Title opcodes
OP_NewTitlesAvailable=0x0d32
diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf
index d836c6360..d5c3f7931 100644
--- a/utils/patches/patch_SoD.conf
+++ b/utils/patches/patch_SoD.conf
@@ -534,6 +534,7 @@ OP_TaskHistoryReply=0x3d2a # C
OP_CancelTask=0x726b # C
OP_DeclineAllTasks=0x0000 #
OP_TaskRequestTimer=0x2e70
+OP_AcceptNewSharedTask=0x4751
OP_Shroud=0x6d1f
diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf
index a10715918..ab5bf766a 100644
--- a/utils/patches/patch_SoF.conf
+++ b/utils/patches/patch_SoF.conf
@@ -510,6 +510,7 @@ OP_TaskRemovePlayer=0x516f
OP_TaskPlayerList=0x0ad6
OP_TaskQuit=0x2c8c
OP_TaskRequestTimer=0x0b08
+OP_AcceptNewSharedTask=0x5bed
#Title opcodes
OP_NewTitlesAvailable=0x179c #
diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf
index 9a7e1e800..3f6042386 100644
--- a/utils/patches/patch_Titanium.conf
+++ b/utils/patches/patch_Titanium.conf
@@ -475,6 +475,7 @@ OP_TaskRemovePlayer=0x37b9
OP_TaskPlayerList=0x3961
OP_TaskQuit=0x35dd
OP_TaskRequestTimer=0x6a1d
+OP_AcceptNewSharedTask=0x194d
#task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes)
diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf
index 71966dcbb..9bc3013ff 100644
--- a/utils/patches/patch_UF.conf
+++ b/utils/patches/patch_UF.conf
@@ -557,6 +557,7 @@ OP_TaskHistoryReply=0x4524 # C
OP_CancelTask=0x3bf5 # C
OP_DeclineAllTasks=0x0000 #
OP_TaskRequestTimer=0x719e
+OP_AcceptNewSharedTask=0x6ded
# Title opcodes
OP_NewTitlesAvailable=0x4b49 # C
diff --git a/zone/client.h b/zone/client.h
index ead552f73..015cc1670 100644
--- a/zone/client.h
+++ b/zone/client.h
@@ -229,6 +229,14 @@ struct ClientReward
uint32 amount;
};
+#define PENDING_TASK_TIMEOUT 60000
+struct PendingSharedTask {
+ int id;
+ int task_master_id;
+ Timer timeout; // so if we take a very long time to get messages back from world, we time out and fail
+ PendingSharedTask() : id(0), task_master_id(0) {}
+};
+
class ClientFactory {
public:
Client *MakeClient(std::shared_ptr<EQStreamInterface> ieqs);
@@ -1020,8 +1028,8 @@ public:
inline void UpdateTasksOnExplore(int ExploreID) { if(taskstate) taskstate->UpdateTasksOnExplore(this, ExploreID); }
inline bool UpdateTasksOnSpeakWith(int NPCTypeID) { if(taskstate) return taskstate->UpdateTasksOnSpeakWith(this, NPCTypeID); else return false; }
inline bool UpdateTasksOnDeliver(std::list<EQEmu::ItemInstance*>& Items, int Cash, int NPCTypeID) { if (taskstate) return taskstate->UpdateTasksOnDeliver(this, Items, Cash, NPCTypeID); else return false; }
- inline void TaskSetSelector(Mob *mob, int TaskSetID) { if(taskmanager) taskmanager->TaskSetSelector(this, taskstate, mob, TaskSetID); }
- inline void TaskQuestSetSelector(Mob *mob, int count, int *tasks) { if(taskmanager) taskmanager->TaskQuestSetSelector(this, taskstate, mob, count, tasks); }
+ inline void TaskSetSelector(Mob *mob, int TaskSetID, bool shared = false) { if(taskmanager) taskmanager->TaskSetSelector(this, taskstate, mob, TaskSetID, shared); }
+ inline void TaskQuestSetSelector(Mob *mob, int count, int *tasks, bool shared = false) { if(taskmanager) taskmanager->TaskQuestSetSelector(this, taskstate, mob, count, tasks, shared); }
inline void EnableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->EnableTask(CharacterID(), TaskCount, TaskList); }
inline void DisableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->DisableTask(CharacterID(), TaskCount, TaskList); }
inline bool IsTaskEnabled(int TaskID) { return (taskstate ? taskstate->IsTaskEnabled(TaskID) : false); }
@@ -1043,6 +1051,13 @@ public:
inline int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID) { return (taskstate ? taskstate->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID) :0); }
inline int ActiveTasksInSet(int TaskSet) { return (taskstate ? taskstate->ActiveTasksInSet(TaskSet) :0); }
inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); }
+ inline int GetTaskLockoutExpire(int id) { return 0; } // stub
+
+ inline void SetPendingTask(int id, int task_master_id) { pending_task.id = id; pending_task.task_master_id = task_master_id; }
+ inline bool HasPendingTask() const { return pending_task.id == 0; }
+ inline int GetPendingTaskID() const { return pending_task.id; }
+ inline int GetPendingTaskMasterID() const { return pending_task.task_master_id; }
+ inline void StartPendingTimer() { pending_task.timeout.Start(PENDING_TASK_TIMEOUT); }
inline const EQEmu::versions::ClientVersion ClientVersion() const { return m_ClientVersion; }
inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; }
@@ -1569,6 +1584,7 @@ private:
std::set<uint32> zone_flags;
ClientTaskState *taskstate;
+ PendingSharedTask pending_task;
int TotalSecondsPlayed;
//Anti Spam Stuff
diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp
index 5cafbaee2..48552c7ec 100644
--- a/zone/client_packet.cpp
+++ b/zone/client_packet.cpp
@@ -114,6 +114,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193;
ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction;
ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask;
+ ConnectedOpcodes[OP_AcceptNewSharedTask] = &Client::Handle_OP_AcceptNewSharedTask;
ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest;
ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest;
ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase;
@@ -1840,6 +1841,20 @@ void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app)
taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id);
}
+void Client::Handle_OP_AcceptNewSharedTask(const EQApplicationPacket *app)
+{
+ if (app->size != sizeof(AcceptNewSharedTask_Struct)) {
+ Log(Logs::General, Logs::None, "Size mismatch in OP_AcceptNewSharedTask expected %i got %i",
+ sizeof(AcceptNewSharedTask_Struct), app->size);
+ DumpPacket(app);
+ return;
+ }
+ auto *ant = (AcceptNewSharedTask_Struct*)app->pBuffer;
+
+ if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate)
+ taskstate->PendSharedTask(this, ant->task_id, ant->task_master_id);
+}
+
void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app)
{
if (app->size < sizeof(EntityId_Struct))
diff --git a/zone/client_packet.h b/zone/client_packet.h
index 9605dc8cf..1163bbba5 100644
--- a/zone/client_packet.h
+++ b/zone/client_packet.h
@@ -25,6 +25,7 @@
void Handle_0x01e7(const EQApplicationPacket *app);
void Handle_OP_AAAction(const EQApplicationPacket *app);
void Handle_OP_AcceptNewTask(const EQApplicationPacket *app);
+ void Handle_OP_AcceptNewSharedTask(const EQApplicationPacket *app);
void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app);
diff --git a/zone/client_process.cpp b/zone/client_process.cpp
index bd14dc70a..a87529665 100644
--- a/zone/client_process.cpp
+++ b/zone/client_process.cpp
@@ -159,6 +159,12 @@ bool Client::Process() {
if (TaskPeriodic_Timer.Check() && taskstate)
taskstate->TaskPeriodicChecks(this);
+ if (pending_task.timeout.Check(false)) {
+ Message(13, "Shared task timed out.");
+ pending_task.id = 0;
+ pending_task.task_master_id = 0;
+ }
+
if (linkdead_timer.Check()) {
LeaveGroup();
Save();
diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp
index 18c84ccdd..9a3b183c7 100644
--- a/zone/embparser_api.cpp
+++ b/zone/embparser_api.cpp
@@ -2048,6 +2048,23 @@ XS(XS__taskselector) {
XSRETURN_EMPTY;
}
+
+XS(XS__sharedtaskselector);
+XS(XS__sharedtaskselector) {
+ dXSARGS;
+ if ((items >= 1) && (items <= MAXCHOOSERENTRIES)) {
+ int tasks[MAXCHOOSERENTRIES];
+ for (int i = 0; i < items; i++) {
+ tasks[i] = (int) SvIV(ST(i));
+ }
+ quest_manager.taskselector(items, tasks, true);
+ } else {
+ Perl_croak(aTHX_ "Usage: quest::sharedtaskselector(int task_id, 2, 3, 4, 5 [up to 40])");
+ }
+
+ XSRETURN_EMPTY;
+}
+
XS(XS__task_setselector);
XS(XS__task_setselector) {
dXSARGS;
@@ -3770,6 +3787,7 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "targlobal"), XS__targlobal, file);
newXS(strcpy(buf, "taskexploredarea"), XS__taskexploredarea, file);
newXS(strcpy(buf, "taskselector"), XS__taskselector, file);
+ newXS(strcpy(buf, "sharedtaskselector"), XS__sharedtaskselector, file);
newXS(strcpy(buf, "task_setselector"), XS__task_setselector, file);
newXS(strcpy(buf, "tasktimeleft"), XS__tasktimeleft, file);
newXS(strcpy(buf, "toggle_spawn_event"), XS__toggle_spawn_event, file);
diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp
index da62dc305..d4bc7a830 100644
--- a/zone/lua_general.cpp
+++ b/zone/lua_general.cpp
@@ -581,6 +581,32 @@ void lua_task_selector(luabind::adl::object table) {
quest_manager.taskselector(count, tasks);
}
+void lua_task_selector(luabind::adl::object table, bool shared) {
+ if(luabind::type(table) != LUA_TTABLE) {
+ return;
+ }
+
+ int tasks[MAXCHOOSERENTRIES] = { 0 };
+ int count = 0;
+
+ for(int i = 1; i <= MAXCHOOSERENTRIES; ++i) {
+ auto cur = table[i];
+ int cur_value = 0;
+ if(luabind::type(cur) != LUA_TNIL) {
+ try {
+ cur_value = luabind::object_cast<int>(cur);
+ } catch(luabind::cast_failed) {
+ }
+ } else {
+ count = i - 1;
+ break;
+ }
+
+ tasks[i - 1] = cur_value;
+ }
+ quest_manager.taskselector(count, tasks, shared);
+}
+
void lua_task_set_selector(int task_set) {
quest_manager.tasksetselector(task_set);
}
@@ -1630,7 +1656,8 @@ luabind::scope lua_register_general() {
luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses),
luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count),
luabind::def("bury_player_corpse", &lua_bury_player_corpse),
- luabind::def("task_selector", &lua_task_selector),
+ luabind::def("task_selector", (void(*)(luabind::adl::object))&lua_task_selector),
+ luabind::def("task_selector", (void(*)(luabind::adl::object,bool))&lua_task_selector),
luabind::def("task_set_selector", &lua_task_set_selector),
luabind::def("enable_task", &lua_enable_task),
luabind::def("disable_task", &lua_disable_task),
diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp
index 1393b0221..39c46da00 100644
--- a/zone/questmgr.cpp
+++ b/zone/questmgr.cpp
@@ -2146,10 +2146,10 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
#endif //BOTS
-void QuestManager::taskselector(int taskcount, int *tasks) {
+void QuestManager::taskselector(int taskcount, int *tasks, bool shared) {
QuestManagerCurrentQuestVars();
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager)
- initiator->TaskQuestSetSelector(owner, taskcount, tasks);
+ initiator->TaskQuestSetSelector(owner, taskcount, tasks, shared);
}
void QuestManager::enabletask(int taskcount, int *tasks) {
QuestManagerCurrentQuestVars();
@@ -2174,11 +2174,11 @@ bool QuestManager::istaskenabled(int taskid) {
return false;
}
-void QuestManager::tasksetselector(int tasksetid) {
+void QuestManager::tasksetselector(int tasksetid, bool shared) {
QuestManagerCurrentQuestVars();
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSetSelector called for task set %i", tasksetid);
if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager)
- initiator->TaskSetSelector(owner, tasksetid);
+ initiator->TaskSetSelector(owner, tasksetid, shared);
}
bool QuestManager::istaskactive(int task) {
diff --git a/zone/questmgr.h b/zone/questmgr.h
index a3b9fac43..41dff79db 100644
--- a/zone/questmgr.h
+++ b/zone/questmgr.h
@@ -188,8 +188,8 @@ public:
void playerfeature(char *feature, int setting);
void npcfeature(char *feature, int setting);
void popup(const char *title, const char *text, uint32 popupid, uint32 buttons, uint32 Duration);
- void taskselector(int taskcount, int *tasks);
- void tasksetselector(int tasksettid);
+ void taskselector(int taskcount, int *tasks, bool shared = false);
+ void tasksetselector(int tasksettid, bool shared = false);
void enabletask(int taskcount, int *tasks);
void disabletask(int taskcount, int *tasks);
bool istaskenabled(int taskid);
diff --git a/zone/string_ids.h b/zone/string_ids.h
index 2c223ffbd..410d718f4 100644
--- a/zone/string_ids.h
+++ b/zone/string_ids.h
@@ -359,6 +359,7 @@
#define LDON_NO_LOCKPICK 7564 //You must have a lock pick in your inventory to do this.
#define LDON_WAS_NOT_LOCKED 7565 //%1 was not locked.
#define LDON_WAS_NOT_TRAPPED 7566 //%1 was not trapped
+#define TASK_REJECT_LOCKEDOUT_ME 8017 //This task can not be assigned to you because you must wait %1d:%2h:%3m before you can do another task of this type.
#define GAIN_GROUP_LEADERSHIP_POINT 8585 //
#define GAIN_RAID_LEADERSHIP_POINT 8589 //
#define MAX_GROUP_LEADERSHIP_POINTS 8584 //
@@ -369,6 +370,15 @@
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
+#define TASK_REJECT_MAX_COUNT 8891 //You can not be assigned this shared task because your party exceeds the maximum allowed number of players.
+#define TASK_REJECT_LEADER_REQ 8892 //You can not be assigned this shared task because the leader does not meet the shared task requirements.
+#define TASK_REJECT_MIN_COUNT 8895 //You can not be assigned this shared task because your party does not contain the minimum required number of players.
+#define TASK_REJECT_HAVE_ONE 8935 //You may not request a shared task because you already have one.
+#define TASK_REJECT_RAID_HAVE_ONE 8936 //You may not request a shared task because someone in your raid, %1, already has one.
+#define TASK_REJECT_GROUP_HAVE_ONE 8937 //You may not request a shared task because someone in your group, %1, already has one.
+#define TASK_REJECT_LOCKEDOUT 8946 //You may not request this shared task because you must wait %1d:%2h:%3m before you can do another task of this type.
+#define TASK_REJECT_LOCKEDOUT_OTHER 8947 //You may not request this shared task because %1 must wait %2d:%3h:%4m before they can do another task of this type.
+#define SHARED_TASK_LOCK 8961 //Your shared task is now locked. You may no longer add or remove players.
#define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps.
#define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell.
#define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
diff --git a/zone/tasks.cpp b/zone/tasks.cpp
index 56297bf3b..acffb0ee6 100644
--- a/zone/tasks.cpp
+++ b/zone/tasks.cpp
@@ -30,16 +30,19 @@ Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net)
#include "../common/rulesys.h"
#include "../common/string_util.h"
#include "../common/say_link.h"
+#include "../common/data_verification.h"
#include "client.h"
#include "entity.h"
#include "mob.h"
#include "string_ids.h"
+#include "worldserver.h"
#include "queryserv.h"
#include "quest_parser_collection.h"
extern QueryServ* QServ;
+extern WorldServer worldserver;
TaskManager::TaskManager() {
for(int i=0; i<MAXTASKS; i++)
@@ -116,15 +119,26 @@ bool TaskManager::LoadTasks(int singleTask)
if (!LoadTaskSets())
Log(Logs::Detail, Logs::Tasks, "TaskManager::LoadTasks LoadTaskSets failed");
- query = StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
- "`reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`,"
- "`minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` < %i",
- MAXTASKS);
+ if (!LoadReplayGroups())
+ Log(Logs::Detail, Logs::Tasks, "TaskManager::LoadTasks LoadReplayGroups failed");
+
+ query =
+ StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
+ "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
+ "`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
+ "`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
+ "`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
+ "`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `id` < %i",
+ MAXTASKS);
} else
- query = StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, "
- "`reward`, `rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`,"
- "`minlevel`, `maxlevel`, `repeatable`, `completion_emote` FROM `tasks` WHERE `id` = %i",
- singleTask);
+ query =
+ StringFormat("SELECT `id`, `type`, `duration`, `duration_code`, `title`, `description`, `reward`, "
+ "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, `faction_reward`, `minlevel`, "
+ "`maxlevel`, `repeatable`, `completion_emote`, `reward_points`, `reward_type`, "
+ "`replay_group`, `min_players`, `max_players`, `task_lock_step`, `instance_zone_id`, "
+ "`zone_version`, `zone_in_zone_id`, `zone_in_x`, `zone_in_y`, `zone_in_object_id`, "
+ "`dest_x`, `dest_y`, `dest_z`, `dest_h` FROM `tasks` WHERE `id` = %i",
+ singleTask);
const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s";
@@ -160,6 +174,22 @@ bool TaskManager::LoadTasks(int singleTask)
Tasks[taskID]->MaxLevel = atoi(row[13]);
Tasks[taskID]->Repeatable = atoi(row[14]);
Tasks[taskID]->completion_emote = row[15];
+ Tasks[taskID]->reward_points = atoi(row[16]);
+ Tasks[taskID]->reward_type = static_cast<PointType>(atoi(row[17]));
+ Tasks[taskID]->replay_group = atoi(row[18]);
+ Tasks[taskID]->min_players = atoi(row[19]);
+ Tasks[taskID]->max_players = atoi(row[20]);
+ Tasks[taskID]->task_lock_step = atoi(row[21]);
+ Tasks[taskID]->instance_zone_id = atoi(row[22]);
+ Tasks[taskID]->zone_version = atoi(row[23]);
+ Tasks[taskID]->zone_in_zone_id = atoi(row[24]);
+ Tasks[taskID]->zone_in_x = atof(row[25]);
+ Tasks[taskID]->zone_in_y = atof(row[26]);
+ Tasks[taskID]->zone_in_object_id = atoi(row[27]);
+ Tasks[taskID]->dest_x = atof(row[28]);
+ Tasks[taskID]->dest_y = atof(row[29]);
+ Tasks[taskID]->dest_z = atof(row[30]);
+ Tasks[taskID]->dest_h = atof(row[31]);
Tasks[taskID]->ActivityCount = 0;
Tasks[taskID]->SequenceMode = ActivitiesSequential;
Tasks[taskID]->LastStep = 0;
@@ -281,6 +311,22 @@ bool TaskManager::LoadTasks(int singleTask)
return true;
}
+bool TaskManager::LoadReplayGroups()
+{
+ replay_groups.clear();
+ std::string query = "SELECT `id`, `name`, `duration` FROM `task_replay_groups` WHERE `id` > 0 ORDER BY `id` ASC";
+
+ auto results = database.QueryDatabase(query);
+
+ if (!results.Success())
+ return false;
+
+ for (auto row = results.begin(); row != results.end(); ++row)
+ replay_groups[atoi(row[0])] = {row[1], atoi(row[2])};
+
+ return true;
+}
+
bool TaskManager::SaveClientState(Client *c, ClientTaskState *state)
{
// I am saving the slot in the ActiveTasks table, because unless a Task is cancelled/completed, the client
@@ -905,7 +951,7 @@ bool ClientTaskState::HasSlotForTask(TaskInformation *task)
case TaskType::Task:
return ActiveTask.TaskID == TASKSLOTEMPTY;
case TaskType::Shared:
- return false; // todo
+ return ActiveSharedTask == nullptr; // todo
case TaskType::Quest:
for (int i = 0; i < MAXACTIVEQUESTS; ++i)
if (ActiveQuests[i].TaskID == TASKSLOTEMPTY)
@@ -988,7 +1034,7 @@ int TaskManager::GetTaskMaxLevel(int TaskID)
return -1;
}
-void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID)
+void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID, bool shared)
{
int TaskList[MAXCHOOSERENTRIES];
int TaskListIndex = 0;
@@ -1024,14 +1070,14 @@ void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, i
// we aren't currently on another, and if it's enabled if not all_enabled
if ((all_enabled || state->IsTaskEnabled(task)) && AppropriateLevel(task, PlayerLevel) &&
!state->IsTaskActive(task) && state->HasSlotForTask(Tasks[task]) && // this slot checking is a bit silly, but we allow mixing of task types ...
- (IsTaskRepeatable(task) || !state->IsTaskCompleted(task)))
+ (IsTaskRepeatable(task) || !state->IsTaskCompleted(task)) && (shared == (Tasks[task]->type == TaskType::Shared)))
TaskList[TaskListIndex++] = task;
++Iterator;
}
if (TaskListIndex > 0) {
- SendTaskSelector(c, mob, TaskListIndex, TaskList);
+ SendTaskSelector(c, mob, TaskListIndex, TaskList, shared);
} else {
mob->SayTo_StringID(c, CC_Yellow, MAX_ACTIVE_TASKS, c->GetName()); // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
}
@@ -1041,7 +1087,7 @@ void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, i
// unlike the non-Quest version of this function, it does not check enabled, that is assumed the responsibility of the quest to handle
// we do however still want it to check the other stuff like level, active, room, etc
-void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks)
+void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks, bool shared)
{
int TaskList[MAXCHOOSERENTRIES];
int TaskListIndex = 0;
@@ -1058,12 +1104,12 @@ void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *m
// we aren't currently on another, and if it's enabled if not all_enabled
if (AppropriateLevel(task, PlayerLevel) &&
!state->IsTaskActive(task) && state->HasSlotForTask(Tasks[task]) && // this slot checking is a bit silly, but we allow mixing of task types ...
- (IsTaskRepeatable(task) || !state->IsTaskCompleted(task)))
+ (IsTaskRepeatable(task) || !state->IsTaskCompleted(task)) && (shared == (Tasks[task]->type == TaskType::Shared)))
TaskList[TaskListIndex++] = task;
}
if (TaskListIndex > 0) {
- SendTaskSelector(c, mob, TaskListIndex, TaskList);
+ SendTaskSelector(c, mob, TaskListIndex, TaskList, shared);
} else {
mob->SayTo_StringID(c, CC_Yellow, MAX_ACTIVE_TASKS, c->GetName()); // check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow
}
@@ -1071,11 +1117,11 @@ void TaskManager::TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *m
return;
}
-void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList) {
+void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared) {
if (c->ClientVersion() >= EQEmu::versions::ClientVersion::RoF)
{
- SendTaskSelectorNew(c, mob, TaskCount, TaskList);
+ SendTaskSelectorNew(c, mob, TaskCount, TaskList, shared);
return;
}
// Titanium OpCode: 0x5e7c
@@ -1108,7 +1154,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
buf.WriteUInt32(ValidTasks);
- buf.WriteUInt32(2); // task type, live doesn't let you send more than one type, but we do?
+ buf.WriteUInt32(shared ? static_cast<uint32>(TaskType::Shared) : static_cast<uint32>(TaskType::Quest)); // hack, we need to send only shared tasks when doing shared tasks since they use different reply ops
buf.WriteUInt32(mob->GetID());
for (int i = 0; i < TaskCount; i++) {
@@ -1158,7 +1204,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
}
-void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList)
+void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared)
{
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSelector for %i Tasks", TaskCount);
@@ -1188,9 +1234,7 @@ void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *T
SerializeBuffer buf(50 * ValidTasks);
buf.WriteUInt32(ValidTasks); // TaskCount
- buf.WriteUInt32(2); // Type, valid values: 0-3. 0 = Task, 1 = Shared Task, 2 = Quest, 3 = ??? -- should fix maybe some day, but we let more than 1 type through :P
- // so I guess an NPC can only offer one type of quests or we can only open a selection with one type :P (so quest call can tell us I guess)
- // this is also sent in OP_TaskDescription
+ buf.WriteUInt32(shared ? static_cast<uint32>(TaskType::Shared) : static_cast<uint32>(TaskType::Quest)); // hack, we need to send only shared tasks when doing shared tasks since they use different reply ops
buf.WriteUInt32(mob->GetID()); // TaskGiver
for (int i = 0; i < TaskCount; i++) { // max 40
@@ -1304,7 +1348,8 @@ ClientTaskState::ClientTaskState() {
ActiveTask.slot = 0;
ActiveTask.TaskID = TASKSLOTEMPTY;
- // TODO: shared task
+
+ ActiveSharedTask = nullptr;
}
ClientTaskState::~ClientTaskState() {
@@ -3324,6 +3369,164 @@ void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID, bool enfor
parse->EventNPC(EVENT_TASK_ACCEPTED, npc, c, buf.c_str(), 0);
}
+// This function will do a bunch of verification, then set up a pending state which will then send a request
+// to world and send off requests to out of group zones to ask if they can join the task
+// Once the we get all of the replies that pass, we will then assign the task
+void ClientTaskState::PendSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement)
+{
+ if (!taskmanager || TaskID < 0 || TaskID >= MAXTASKS) {
+ c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID);
+ return;
+ }
+
+ auto task = taskmanager->Tasks[TaskID];
+
+ if (task == nullptr) {
+ c->Message(13, "Invalid TaskID %i", TaskID);
+ return;
+ }
+
+ if (task->type != TaskType::Shared) {
+ c->Message(13, "Trying to shared task a non shared task %i", TaskID);
+ return;
+ }
+
+ if (ActiveSharedTask != nullptr) {
+ c->Message_StringID(13, TASK_REJECT_HAVE_ONE);
+ return;
+ }
+
+ if (enforce_level_requirement && !taskmanager->AppropriateLevel(TaskID, c->GetLevel())) {
+ c->Message(13, "You are outside the level range of this task.");
+ return;
+ }
+
+ if (!taskmanager->IsTaskRepeatable(TaskID) && IsTaskCompleted(TaskID))
+ return;
+
+ if (task->replay_group) {
+ auto expires = c->GetTaskLockoutExpire(task->replay_group);
+ if (expires) {
+ auto diff = expires - Timer::GetCurrentTime();
+ std::string days = std::to_string(diff / 86400);
+ diff = diff % 86400;
+ std::string hours = std::to_string(diff / 3600);
+ diff = diff % 3600;
+ std::string minutes = std::to_string(diff / 60);
+ c->Message_StringID(13, TASK_REJECT_LOCKEDOUT, days.c_str(), hours.c_str(), minutes.c_str());
+ return;
+ }
+ }
+
+ // Now we need to verify we meet min_player and max_players for raid/group
+ Group *group = nullptr;
+ Raid *raid = nullptr;
+ int player_count = 1; // 1 is just us!
+ if (c->IsGrouped()) {
+ group = c->GetGroup();
+ player_count = group->GroupCount();
+ } else if (c->IsRaidGrouped()) {
+ raid = c->GetRaid();
+ player_count = raid->RaidCount();
+ }
+
+ // TODO: check task lockouts I guess it's simpler to require everyone to be in zone so we can verify lockouts ...
+
+ if (!EQEmu::ValueWithin(player_count, task->min_players, task->max_players)) {
+ if (player_count < task->min_players)
+ c->Message_StringID(13, TASK_REJECT_MIN_COUNT);
+ else
+ c->Message_StringID(13, TASK_REJECT_MAX_COUNT);
+ return;
+ }
+
+ std::vector<std::string> missing_players; // names of players not in this zone so we can put the checks off to world
+ bool task_failed = false;
+ if (group) {
+ for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
+ if (group->members[i] && group->members[i]->IsClient()) {
+ auto *client = group->members[i]->CastToClient();
+ auto *task_state = client->GetTaskState();
+ if (!task_state->HasSlotForTask(task)) {
+ task_failed = true;
+ c->Message_StringID(13, TASK_REJECT_GROUP_HAVE_ONE, c->GetName());
+ } else {
+ if (task->replay_group) {
+ auto expires = client->GetTaskLockoutExpire(task->replay_group);
+ if (expires) {
+ task_failed = true;
+ auto diff = expires - Timer::GetCurrentTime();
+ std::string days = std::to_string(diff / 86400);
+ diff = diff % 86400;
+ std::string hours = std::to_string(diff / 3600);
+ diff = diff % 3600;
+ std::string minutes = std::to_string(diff / 60);
+ c->Message_StringID(13, TASK_REJECT_LOCKEDOUT_OTHER,
+ client->GetName(), days.c_str(),
+ hours.c_str(), minutes.c_str());
+ client->Message_StringID(13, TASK_REJECT_LOCKEDOUT_ME,
+ days.c_str(), hours.c_str(),
+ minutes.c_str());
+ }
+ }
+ }
+ } else if (group->members[i] == nullptr) { // out of zone
+ missing_players.push_back(group->membername[i]);
+ }
+ }
+ } else if (raid) {
+ for (int i = 0; i < MAX_RAID_MEMBERS; ++i) {
+ if (raid->members[i].member) {
+ auto *client = raid->members[i].member;
+ auto *task_state = client->GetTaskState();
+ if (!task_state->HasSlotForTask(task)) {
+ task_failed = true;
+ c->Message_StringID(13, TASK_REJECT_RAID_HAVE_ONE, c->GetName());
+ } else {
+ if (task->replay_group) {
+ auto expires = client->GetTaskLockoutExpire(task->replay_group);
+ if (expires) {
+ task_failed = true;
+ auto diff = expires - Timer::GetCurrentTime();
+ std::string days = std::to_string(diff / 86400);
+ diff = diff % 86400;
+ std::string hours = std::to_string(diff / 3600);
+ diff = diff % 3600;
+ std::string minutes = std::to_string(diff / 60);
+ c->Message_StringID(13, TASK_REJECT_LOCKEDOUT_OTHER,
+ client->GetName(), days.c_str(),
+ hours.c_str(), minutes.c_str());
+ client->Message_StringID(13, TASK_REJECT_LOCKEDOUT_ME,
+ days.c_str(), hours.c_str(),
+ minutes.c_str());
+ }
+ }
+ }
+ } else if (raid->members[i].membername[0] != '\0') { // out of zone
+ missing_players.push_back(raid->members[i].membername);
+ }
+ }
+ }
+
+ if (task_failed) // we already yelled at them
+ return;
+
+ // so we've verified all the clients we can and didn't fail, time to pend and yell at world
+ c->SetPendingTask(TaskID, NPCID);
+ c->StartPendingTimer(); // in case something goes wrong and takes ages, we time out
+
+ SerializeBuffer buf(25 + 10 * missing_players.size());
+ buf.WriteInt32(TaskID); // Task ID
+ buf.WriteString(c->GetName()); // leader name
+ buf.WriteInt32(missing_players.size()); // count
+ for (auto && name : missing_players)
+ buf.WriteString(name);
+
+ auto pack = new ServerPacket(ServerOP_TaskRequest, buf);
+ worldserver.SendPacket(pack);
+ delete pack;
+}
+
void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) {
float LastX = c->ProximityX();
@@ -3537,3 +3740,34 @@ int TaskProximityManager::CheckProximities(float X, float Y, float Z) {
return 0;
}
+void SharedTaskState::LockTask()
+{
+ SetLocked(true);
+
+ for (auto & m : members)
+ if (m.entity != nullptr)
+ m.entity->Message_StringID(CC_Yellow, SHARED_TASK_LOCK);
+}
+
+void SharedTaskState::MemberZoned(Mob *player)
+{
+ auto it = std::find_if(members.begin(), members.end(),
+ [&player](const SharedTaskMember &a) { return a.name == player->GetName(); });
+
+ if (it == members.end()) // guess they weren't in this group, w/e
+ return;
+
+ it->entity = nullptr;
+}
+
+void SharedTaskState::MemberEnterZone(Mob *player)
+{
+ auto it = std::find_if(members.begin(), members.end(),
+ [&player](const SharedTaskMember &a) { return a.name == player->GetName(); });
+
+ if (it == members.end()) // guess they weren't in this group, w/e
+ return;
+
+ it->entity = player;
+}
+
diff --git a/zone/tasks.h b/zone/tasks.h
index 0bc45c146..97792bcd2 100644
--- a/zone/tasks.h
+++ b/zone/tasks.h
@@ -26,6 +26,8 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
#include <vector>
#include <string>
#include <algorithm>
+#include <unordered_map>
+#include <map>
#define MAXTASKS 10000
#define MAXTASKSETS 1000
@@ -141,6 +143,13 @@ enum class DurationCode {
Long = 3
};
+// need to capture more, shared are just Radiant/Ebon though
+enum class PointType {
+ None = 0,
+ Radiant = 4,
+ Ebon = 5,
+};
+
struct TaskInformation {
TaskType type;
int Duration;
@@ -155,12 +164,33 @@ struct TaskInformation {
int XPReward;
int faction_reward; // just a npc_faction_id
TaskMethodType RewardMethod;
+ int reward_points; // DoN crystals for shared. Generic "points" for non-shared
+ PointType reward_type; // 4 for Radiant Crystals else Ebon crystals when shared task
int ActivityCount;
SequenceType SequenceMode;
int LastStep;
short MinLevel;
short MaxLevel;
bool Repeatable;
+ int replay_group; // ID of our replay timer group (0 means none)
+ int min_players; // shared tasks
+ int max_players;
+ int task_lock_step; // task locks after this step is completed
+ uint32 instance_zone_id; // instance shit
+ uint32 zone_version;
+ uint16 zone_in_zone_id;
+ float zone_in_x;
+ float zone_in_y;
+ uint16 zone_in_object_id;
+ float dest_x;
+ float dest_y;
+ float dest_z;
+ float dest_h;
+ /* int graveyard_zone_id;
+ float graveyard_x;
+ float graveyard_y;
+ float graveyard_z;
+ float graveyard_radius; */
ActivityInformation Activity[MAXACTIVITIESPERTASK];
};
@@ -193,6 +223,35 @@ struct CompletedTaskInformation {
bool ActivityDone[MAXACTIVITIESPERTASK];
};
+struct SharedTaskMember {
+ std::string name;
+ Mob *entity; // needs to be managed
+ bool leader;
+ SharedTaskMember() : entity(nullptr), leader(false) {}
+};
+
+class SharedTaskState {
+public:
+ SharedTaskState() : locked(false) {}
+// ~SharedTaskState();
+
+ inline const bool IsLocked() const { return locked; }
+ inline void SetLocked(bool v) { locked = v; }
+ void LockTask(); // notified clients (if they are etc)
+
+ void MemberZoned(Mob *player); // player left zone, update their pointer
+ void MemberEnterZone(Mob *player); // player entered zone, update their pointer
+
+ ClientTaskInformation *GetActivity() { return &activity; }
+
+ friend class TaskManager;
+
+private:
+ std::vector<SharedTaskMember> members;
+ ClientTaskInformation activity;
+ bool locked;
+};
+
class ClientTaskState {
public:
@@ -206,6 +265,8 @@ public:
int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID);
int GetTaskStartTime(TaskType type, int index);
void AcceptNewTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false);
+// void AcceptNewSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false);
+ void PendSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false);
void FailTask(Client *c, int TaskID);
int TaskTimeLeft(int TaskID);
int IsTaskCompleted(int TaskID);
@@ -255,6 +316,8 @@ private:
info = &ActiveTask;
break;
case TaskType::Shared:
+ if (index == 0 && ActiveSharedTask)
+ info = ActiveSharedTask->GetActivity();
break;
case TaskType::Quest:
if (index < MAXACTIVEQUESTS)
@@ -273,6 +336,7 @@ private:
};
ClientTaskInformation ActiveTasks[MAXACTIVEQUESTS + 1];
};
+ SharedTaskState *ActiveSharedTask; // pointer to our shared task managed by TaskManager
// Shared tasks should be limited to 1 as well
std::vector<int> EnabledTasks;
std::vector<CompletedTaskInformation> CompletedTasks;
@@ -280,6 +344,17 @@ private:
bool CheckedTouchActivities;
};
+// used for timer lockouts and /tasktimers
+struct TaskTimer {
+ int ID; // ID used in task timer
+ int original_id; // original ID of the task
+ int expires; // UNIX timestamp of when it expires, what happens with DLS? Fuck it.
+};
+
+struct TaskReplayGroups {
+ std::string name;
+ int duration;
+};
class TaskManager {
@@ -289,18 +364,19 @@ public:
int GetActivityCount(int TaskID);
bool LoadSingleTask(int TaskID);
bool LoadTasks(int SingleTask=0);
+ bool LoadReplayGroups();
void ReloadGoalLists();
inline void LoadProximities(int ZoneID) { ProximityManager.LoadProximities(ZoneID); }
bool LoadTaskSets();
bool LoadClientState(Client *c, ClientTaskState *state);
bool SaveClientState(Client *c, ClientTaskState *state);
- void SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList);
- void SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList);
+ void SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared = false); // dumb hack cuz we do dumb things
+ void SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList, bool shared = false);
bool AppropriateLevel(int TaskID, int PlayerLevel);
int GetTaskMinLevel(int TaskID);
int GetTaskMaxLevel(int TaskID);
- void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID);
- void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks); // task list provided by QuestManager (perl/lua)
+ void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID, bool shared = false);
+ void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks, bool shared = false); // task list provided by QuestManager (perl/lua)
void SendActiveTasksToClient(Client *c, bool TaskComplete=false);
void SendSingleActiveTaskToClient(Client *c, ClientTaskInformation &task_info, bool TaskComplete, bool BringUpTaskJournal = false);
void SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex);
@@ -316,6 +392,7 @@ public:
bool IsTaskRepeatable(int TaskID);
friend class ClientTaskState;
+ void LoadSharedTask(int id); // loads the shared task state
private:
TaskGoalListManager GoalListManager;
@@ -323,6 +400,8 @@ private:
TaskInformation* Tasks[MAXTASKS];
std::vector<int> TaskSets[MAXTASKSETS];
void SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInformation &task_info, int StartTime, int Duration, bool BringUpTaskJournal=false);
+ std::unordered_map<int, SharedTaskState> SharedTasks;
+ std::map<int, TaskReplayGroups> replay_groups;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment