diff --git a/zone/client.h b/zone/client.h index f898103c8..9b71cdc21 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1166,27 +1166,16 @@ public: { if (task_state) { task_state->UpdateTasksOnTouch(this, dz_switch_id); } } - inline void TaskSetSelector(Mob *mob, int task_set_id) + inline void TaskSetSelector(Mob* mob, int task_set_id, bool ignore_cooldown) { - if (task_manager) { - task_manager->TaskSetSelector( - this, - task_state, - mob, - task_set_id - ); + if (task_manager && task_state) { + task_manager->TaskSetSelector(this, mob, task_set_id, ignore_cooldown); } } - inline void TaskQuestSetSelector(Mob *mob, int count, int *tasks) + inline void TaskQuestSetSelector(Mob* mob, const std::vector& tasks, bool ignore_cooldown) { - if (task_manager) { - task_manager->TaskQuestSetSelector( - this, - task_state, - mob, - count, - tasks - ); + if (task_manager && task_state) { + task_manager->TaskQuestSetSelector(this, mob, tasks, ignore_cooldown); } } inline void EnableTask(int task_count, int *task_list) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 735ec7e05..0b31ca871 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1088,12 +1088,22 @@ void Perl__taskselector(perl::array task_ids) throw std::runtime_error(fmt::format("Exceeded max number of task offers [{}]", MAXCHOOSERENTRIES)); } - int tasks[MAXCHOOSERENTRIES]; + std::vector tasks; for (int i = 0; i < task_ids.size(); ++i) { - tasks[i] = task_ids[i]; + tasks.push_back(task_ids[i]); } - quest_manager.taskselector(static_cast(task_ids.size()), tasks); + quest_manager.taskselector(tasks); +} + +void Perl__taskselector_nocooldown(perl::array task_ids) +{ + std::vector tasks; + for (int i = 0; i < task_ids.size() && i < MAXCHOOSERENTRIES; ++i) + { + tasks.push_back(task_ids[i]); + } + quest_manager.taskselector(tasks, true); } void Perl__task_setselector(int task_set_id) @@ -1101,6 +1111,11 @@ void Perl__task_setselector(int task_set_id) quest_manager.tasksetselector(task_set_id); } +void Perl__task_setselector(int task_set_id, bool ignore_cooldown) +{ + quest_manager.tasksetselector(task_set_id, ignore_cooldown); +} + void Perl__enabletask(perl::array task_ids) { int count = 0; @@ -4240,7 +4255,9 @@ void perl_register_quest() package.add("surname", &Perl__surname); package.add("targlobal", &Perl__targlobal); package.add("taskselector", &Perl__taskselector); - package.add("task_setselector", &Perl__task_setselector); + package.add("taskselector_nocooldown", &Perl__taskselector_nocooldown); + package.add("task_setselector", (void(*)(int))&Perl__task_setselector); + package.add("task_setselector", (void(*)(int, bool))&Perl__task_setselector); package.add("tasktimeleft", &Perl__tasktimeleft); package.add("toggle_spawn_event", &Perl__toggle_spawn_event); package.add("toggledoorstate", &Perl__toggledoorstate); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index af73b7d2c..31db4eb83 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2512,32 +2512,25 @@ int Lua_Client::GetSpellDamage() { } void Lua_Client::TaskSelector(luabind::adl::object table) { + TaskSelector(table, false); +} + +void Lua_Client::TaskSelector(luabind::adl::object table, bool ignore_cooldown) { Lua_Safe_Call_Void(); if(luabind::type(table) != LUA_TTABLE) { return; } - int tasks[MAXCHOOSERENTRIES] = { 0 }; - int task_count = 0; - + std::vector tasks; 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(cur); - } catch(luabind::cast_failed &) { - } - } else { - task_count = i - 1; - break; + if (luabind::type(cur) == LUA_TNUMBER) { + tasks.push_back(luabind::object_cast(cur)); } - - tasks[i - 1] = cur_value; } - self->TaskQuestSetSelector(self, task_count, tasks); + self->TaskQuestSetSelector(self, tasks, ignore_cooldown); } bool Lua_Client::TeleportToPlayerByCharID(uint32 character_id) { @@ -2983,6 +2976,7 @@ luabind::scope lua_register_client() { .def("TakePlatinum", (bool(Lua_Client::*)(uint32))&Lua_Client::TakePlatinum) .def("TakePlatinum", (bool(Lua_Client::*)(uint32,bool))&Lua_Client::TakePlatinum) .def("TaskSelector", (void(Lua_Client::*)(luabind::adl::object))&Lua_Client::TaskSelector) + .def("TaskSelector", (void(Lua_Client::*)(luabind::adl::object, bool))&Lua_Client::TaskSelector) .def("TeleportToPlayerByCharID", (bool(Lua_Client::*)(uint32))&Lua_Client::TeleportToPlayerByCharID) .def("TeleportToPlayerByName", (bool(Lua_Client::*)(std::string))&Lua_Client::TeleportToPlayerByName) .def("TeleportGroupToPlayerByCharID", (bool(Lua_Client::*)(uint32))&Lua_Client::TeleportGroupToPlayerByCharID) diff --git a/zone/lua_client.h b/zone/lua_client.h index 966c75acd..dea3b2986 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -437,6 +437,7 @@ public: void SetPrimaryWeaponOrnamentation(uint32 model_id); void SetSecondaryWeaponOrnamentation(uint32 model_id); void TaskSelector(luabind::adl::object table); + void TaskSelector(luabind::adl::object table, bool ignore_cooldown); void SetClientMaxLevel(uint8 max_level); uint8 GetClientMaxLevel(); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index e840d03a9..fda3c5beb 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -597,30 +597,35 @@ bool lua_bury_player_corpse(uint32 char_id) { return quest_manager.buryplayercorpse(char_id); } -void lua_task_selector(luabind::adl::object table) { +void lua_task_selector(luabind::adl::object table, bool ignore_cooldown) { if(luabind::type(table) != LUA_TTABLE) { return; } - int tasks[MAXCHOOSERENTRIES] = { 0 }; - int count = 0; - + std::vector tasks; for (int i = 1; i <= MAXCHOOSERENTRIES; ++i) { if (luabind::type(table[i]) == LUA_TNUMBER) { - tasks[i - 1] = luabind::object_cast(table[i]); - ++count; + tasks.push_back(luabind::object_cast(table[i])); } } - quest_manager.taskselector(count, tasks); + quest_manager.taskselector(tasks, ignore_cooldown); +} + +void lua_task_selector(luabind::adl::object table) { + lua_task_selector(table, false); } void lua_task_set_selector(int task_set) { quest_manager.tasksetselector(task_set); } +void lua_task_set_selector(int task_set, bool ignore_cooldown) { + quest_manager.tasksetselector(task_set, ignore_cooldown); +} + void lua_enable_task(luabind::adl::object table) { if(luabind::type(table) != LUA_TTABLE) { return; @@ -3703,8 +3708,10 @@ luabind::scope lua_register_general() { luabind::def("get_player_corpse_count_by_zone_id", &lua_get_player_corpse_count_by_zone_id), 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_set_selector", &lua_task_set_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", (void(*)(int))&lua_task_set_selector), + luabind::def("task_set_selector", (void(*)(int, bool))&lua_task_set_selector), luabind::def("enable_task", &lua_enable_task), luabind::def("disable_task", &lua_disable_task), luabind::def("is_task_enabled", &lua_is_task_enabled), diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index db7a40503..2236a0ec3 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2407,16 +2407,24 @@ int Perl_Client_GetSpellDamage(Client* self) void Perl_Client_TaskSelector(Client* self, perl::array task_ids) { - int tasks[MAXCHOOSERENTRIES] = { 0 }; - int task_count = 0; - + std::vector tasks; for (int i = 0; i < task_ids.size() && i < MAXCHOOSERENTRIES; ++i) { - tasks[i] = task_ids[i]; - ++task_count; + tasks.push_back(task_ids[i]); } - self->TaskQuestSetSelector(self, task_count, tasks); + self->TaskQuestSetSelector(self, tasks, false); +} + +void Perl_Client_TaskSelectorNoCooldown(Client* self, perl::array task_ids) +{ + std::vector tasks; + for (int i = 0; i < task_ids.size() && i < MAXCHOOSERENTRIES; ++i) + { + tasks.push_back(task_ids[i]); + } + + self->TaskQuestSetSelector(self, tasks, true); } bool Perl_Client_TeleportToPlayerByCharacterID(Client* self, uint32 character_id) @@ -2872,6 +2880,7 @@ void perl_register_client() package.add("TeleportRaidToPlayerByCharID", &Perl_Client_TeleportRaidToPlayerByCharacterID); package.add("TeleportRaidToPlayerByName", &Perl_Client_TeleportRaidToPlayerByName); package.add("TaskSelector", &Perl_Client_TaskSelector); + package.add("TaskSelectorNoCooldown", &Perl_Client_TaskSelectorNoCooldown); package.add("Thirsty", &Perl_Client_Thirsty); package.add("TrainDiscBySpellID", &Perl_Client_TrainDiscBySpellID); package.add("UnFreeze", &Perl_Client_UnFreeze); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index c70ec7b3f..acf7d38f2 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2294,10 +2294,10 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level #endif //BOTS -void QuestManager::taskselector(int taskcount, int *tasks) { +void QuestManager::taskselector(const std::vector& tasks, bool ignore_cooldown) { QuestManagerCurrentQuestVars(); if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && task_manager) - initiator->TaskQuestSetSelector(owner, taskcount, tasks); + initiator->TaskQuestSetSelector(owner, tasks, ignore_cooldown); } void QuestManager::enabletask(int taskcount, int *tasks) { QuestManagerCurrentQuestVars(); @@ -2322,11 +2322,11 @@ bool QuestManager::istaskenabled(int taskid) { return false; } -void QuestManager::tasksetselector(int tasksetid) { +void QuestManager::tasksetselector(int tasksetid, bool ignore_cooldown) { QuestManagerCurrentQuestVars(); Log(Logs::General, Logs::Tasks, "[UPDATE] TaskSetSelector called for task set %i", tasksetid); if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && task_manager) - initiator->TaskSetSelector(owner, tasksetid); + initiator->TaskSetSelector(owner, tasksetid, ignore_cooldown); } bool QuestManager::istaskactive(int task) { diff --git a/zone/questmgr.h b/zone/questmgr.h index f71c1f72c..17e94f171 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -210,8 +210,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(const std::vector& tasks, bool ignore_cooldown = false); + void tasksetselector(int tasksettid, bool ignore_cooldown = false); void enabletask(int taskcount, int *tasks); void disabletask(int taskcount, int *tasks); bool istaskenabled(int taskid); diff --git a/zone/task_manager.cpp b/zone/task_manager.cpp index bbd4476ab..0b866af47 100644 --- a/zone/task_manager.cpp +++ b/zone/task_manager.cpp @@ -558,11 +558,10 @@ TaskType TaskManager::GetTaskType(uint32 task_id) return TaskType::Task; } -void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_state, Mob *mob, int task_set_id) +void TaskManager::TaskSetSelector(Client* client, Mob* mob, int task_set_id, bool ignore_cooldown) { - int task_list[MAXCHOOSERENTRIES]; - int task_list_index = 0; int player_level = client->GetLevel(); + ClientTaskState* client_task_state = client->GetTaskState(); LogTasks( "TaskSetSelector called for task_set_id [{}] EnableTaskSize is [{}]", @@ -579,12 +578,12 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s { const auto task_data = GetTaskData(task_id); if (task_data && task_data->type == TaskType::Shared) { - SharedTaskSelector(client, mob, m_task_sets[task_set_id].size(), m_task_sets[task_set_id].data()); + SharedTaskSelector(client, mob, m_task_sets[task_set_id], ignore_cooldown); return; } } - if (client->HasTaskRequestCooldownTimer()) { + if (!ignore_cooldown && client->HasTaskRequestCooldownTimer()) { client->SendTaskRequestCooldownTimerMessage(); return; } @@ -607,7 +606,8 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s ++iterator; } // skip first when all enabled since it's useless data - while (iterator != m_task_sets[task_set_id].end() && task_list_index < MAXCHOOSERENTRIES) { + std::vector task_list; + while (iterator != m_task_sets[task_set_id].end() && task_list.size() < MAXCHOOSERENTRIES) { auto task = *iterator; const auto task_data = GetTaskData(task); @@ -617,14 +617,14 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s !client_task_state->IsTaskActive(task) && client_task_state->HasSlotForTask(task_data) && // this slot checking is a bit silly, but we allow mixing of task types ... (IsTaskRepeatable(task) || !client_task_state->IsTaskCompleted(task))) { - task_list[task_list_index++] = task; + task_list.push_back(task); } ++iterator; } - if (task_list_index > 0) { - SendTaskSelector(client, mob, task_list_index, task_list); + if (!task_list.empty()) { + SendTaskSelector(client, mob, task_list); } else { client->MessageString(Chat::Yellow, NO_TASK_OFFERS, ".", ".", client->GetName()); @@ -633,42 +633,36 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s // 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 *client, - ClientTaskState *client_task_state, - Mob *mob, - int count, - int *tasks -) +void TaskManager::TaskQuestSetSelector(Client* client, Mob* mob, const std::vector& tasks, bool ignore_cooldown) { - int task_list[MAXCHOOSERENTRIES]; - int task_list_index = 0; + std::vector task_list; int player_level = client->GetLevel(); + ClientTaskState* client_task_state = client->GetTaskState(); - LogTasks("[UPDATE] TaskQuestSetSelector called for array size [{}]", count); + LogTasks("[UPDATE] TaskQuestSetSelector called with size [{}]", tasks.size()); - if (count <= 0) { + if (tasks.empty()) { return; } // live prevents mixing selection types (also uses diff opcodes for solo vs shared tasks) // to keep shared task validation live-like (and simple), any shared task will // forward this to shared task validation and non-shared tasks will be dropped - for (int i = 0; i < count; ++i) { + for (int i = 0; i < tasks.size(); ++i) { auto task = tasks[i]; const auto task_data = GetTaskData(task); if (task_data && task_data->type == TaskType::Shared) { - SharedTaskSelector(client, mob, count, tasks); + SharedTaskSelector(client, mob, tasks, ignore_cooldown); return; } } - if (client->HasTaskRequestCooldownTimer()) { + if (!ignore_cooldown && client->HasTaskRequestCooldownTimer()) { client->SendTaskRequestCooldownTimerMessage(); return; } - for (int i = 0; i < count; ++i) { + for (int i = 0; i < tasks.size() && task_list.size() < MAXCHOOSERENTRIES; ++i) { auto task = tasks[i]; const auto task_data = GetTaskData(task); // verify level, we're not currently on it, repeatable status, if it's a (shared) task @@ -677,23 +671,27 @@ void TaskManager::TaskQuestSetSelector( client_task_state->HasSlotForTask(task_data) && // this slot checking is a bit silly, but we allow mixing of task types ... (IsTaskRepeatable(task) || !client_task_state->IsTaskCompleted(task))) { - task_list[task_list_index++] = task; + task_list.push_back(task); } } - if (task_list_index > 0) { - SendTaskSelector(client, mob, task_list_index, task_list); + if (!task_list.empty()) { + SendTaskSelector(client, mob, task_list); } else { client->MessageString(Chat::Yellow, NO_TASK_OFFERS, ".", ".", client->GetName()); } } -void TaskManager::SharedTaskSelector(Client *client, Mob *mob, int count, const int *tasks) +void TaskManager::SharedTaskSelector(Client* client, Mob* mob, const std::vector& tasks, bool ignore_cooldown) { - LogTasks("[UPDATE] SharedTaskSelector called for array size [{}]", count); + LogTasks("[UPDATE] SharedTaskSelector called with size [{}]", tasks.size()); - if (count <= 0 || client->HasTaskRequestCooldownTimer()) { + if (tasks.empty()) { + return; + } + + if (!ignore_cooldown && client->HasTaskRequestCooldownTimer()) { client->SendTaskRequestCooldownTimerMessage(); return; } @@ -736,10 +734,9 @@ void TaskManager::SharedTaskSelector(Client *client, Mob *mob, int count, const if (!validation_failed) { // run type and level filters on task selections - int task_list[MAXCHOOSERENTRIES] = {0}; - int task_list_index = 0; + std::vector task_list; - for (int i = 0; i < count && task_list_index < MAXCHOOSERENTRIES; ++i) { + for (int i = 0; i < tasks.size() && task_list.size() < MAXCHOOSERENTRIES; ++i) { // todo: are there non repeatable shared tasks? (would need to check all group/raid members) auto task = tasks[i]; const auto task_data = GetTaskData(task); @@ -748,13 +745,13 @@ void TaskManager::SharedTaskSelector(Client *client, Mob *mob, int count, const request.lowest_level >= task_data->min_level && (task_data->max_level == 0 || request.highest_level <= task_data->max_level)) { - task_list[task_list_index++] = task; + task_list.push_back(task); } } // check if any tasks are left to offer after filtering - if (task_list_index > 0) { - SendSharedTaskSelector(client, mob, task_list_index, task_list); + if (!task_list.empty()) { + SendSharedTaskSelector(client, mob, task_list); } else { client->MessageString(Chat::Red, TaskStr::NOT_MEET_REQ); @@ -763,14 +760,14 @@ void TaskManager::SharedTaskSelector(Client *client, Mob *mob, int count, const } // sends task selector to client -void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int *task_list) +void TaskManager::SendTaskSelector(Client* client, Mob* mob, const std::vector& task_list) { - LogTasks("TaskSelector for [{}] Tasks", task_count); + LogTasks("TaskSelector for [{}] Tasks", task_list.size()); int player_level = client->GetLevel(); client->GetTaskState()->ClearLastOffers(); int valid_tasks_count = 0; - for (int task_index = 0; task_index < task_count; task_index++) { + for (int task_index = 0; task_index < task_list.size(); task_index++) { if (!ValidateLevel(task_list[task_index], player_level)) { continue; } @@ -796,7 +793,7 @@ void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int // this is also sent in OP_TaskDescription buf.WriteUInt32(mob->GetID()); // TaskGiver - for (int i = 0; i < task_count; i++) { // max 40 + for (int i = 0; i < task_list.size(); i++) { // max 40 if (!ValidateLevel(task_list[i], player_level)) { continue; } @@ -816,9 +813,9 @@ void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int client->QueuePacket(outapp.get()); } -void TaskManager::SendSharedTaskSelector(Client *client, Mob *mob, int task_count, int *task_list) +void TaskManager::SendSharedTaskSelector(Client* client, Mob* mob, const std::vector& task_list) { - LogTasks("SendSharedTaskSelector for [{}] Tasks", task_count); + LogTasks("SendSharedTaskSelector for [{}] Tasks", task_list.size()); // request timer is only set when shared task selection shown (not for failed validations) client->StartTaskRequestCooldownTimer(); @@ -826,12 +823,12 @@ void TaskManager::SendSharedTaskSelector(Client *client, Mob *mob, int task_coun SerializeBuffer buf; - buf.WriteUInt32(task_count); // number of tasks + buf.WriteUInt32(static_cast(task_list.size())); // number of tasks // shared task selection (live doesn't mix types) makes client send shared task specific opcode for accepts buf.WriteUInt32(static_cast(TaskType::Shared)); buf.WriteUInt32(mob->GetID()); // task giver entity id - for (int i = 0; i < task_count; ++i) { + for (int i = 0; i < task_list.size(); ++i) { int task_id = task_list[i]; buf.WriteUInt32(task_id); m_task_data[task_id].SerializeSelector(buf, client->ClientVersion()); diff --git a/zone/task_manager.h b/zone/task_manager.h index cbf723b5d..882e60e47 100644 --- a/zone/task_manager.h +++ b/zone/task_manager.h @@ -24,20 +24,14 @@ public: bool LoadTaskSets(); bool LoadClientState(Client *client, ClientTaskState *client_task_state); bool SaveClientState(Client *client, ClientTaskState *client_task_state); - void SendTaskSelector(Client *client, Mob *mob, int task_count, int *task_list); + void SendTaskSelector(Client* client, Mob* mob, const std::vector& tasks); bool ValidateLevel(int task_id, int player_level); std::string GetTaskName(uint32 task_id); TaskType GetTaskType(uint32 task_id); - void TaskSetSelector(Client *client, ClientTaskState *client_task_state, Mob *mob, int task_set_id); + void TaskSetSelector(Client* client, Mob* mob, int task_set_id, bool ignore_cooldown); // task list provided by QuestManager (perl/lua) - void TaskQuestSetSelector( - Client *client, - ClientTaskState *client_task_state, - Mob *mob, - int count, - int *tasks - ); - void SharedTaskSelector(Client* client, Mob* mob, int count, const int* tasks); + void TaskQuestSetSelector(Client* client, Mob* mob, const std::vector& tasks, bool ignore_cooldown); + void SharedTaskSelector(Client* client, Mob* mob, const std::vector& tasks, bool ignore_cooldown); void SendActiveTasksToClient(Client *client, bool task_complete = false); void SendSingleActiveTaskToClient( Client *client, @@ -92,7 +86,7 @@ private: // shared tasks void SyncClientSharedTaskWithPersistedState(Client *c, ClientTaskState *cts); void SyncClientSharedTaskRemoveLocalIfNotExists(Client *c, ClientTaskState *cts); - void SendSharedTaskSelector(Client* client, Mob* mob, int task_count, int* task_list); + void SendSharedTaskSelector(Client* client, Mob* mob, const std::vector& tasks); void SyncClientSharedTaskStateToLocal(Client *c); };