Last active
September 1, 2018 21:24
-
-
Save mackal/ca259888d28f3176ae5c726b7d96991a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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