diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index de46a8bb0..f1a179ffc 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1393,11 +1393,13 @@ sub fetch_latest_windows_binaries { } sub fetch_latest_windows_binaries_bots { - print "[Update] Fetching Latest Windows Binaries with Bots...\n"; - get_remote_file($install_repository_request_url . "master_windows_build_bots.zip", "updates_staged/master_windows_build_bots.zip", 1); + print "[Update] Fetching Latest Windows Binaries (unstable) with Bots...\n"; + get_remote_file("https://ci.appveyor.com/api/projects/KimLS/server/artifacts/eqemu-x86-bots.zip", "updates_staged/eqemu-x86-bots.zip", 1); + #::: old repository kept for reference until no issues reported + #::: get_remote_file($install_repository_request_url . "master_windows_build_bots.zip", "updates_staged/master_windows_build_bots.zip", 1); print "[Update] Fetched Latest Windows Binaries with Bots...\n"; print "[Update] Extracting...\n"; - unzip('updates_staged/master_windows_build_bots.zip', 'updates_staged/binaries/'); + unzip('updates_staged/eqemu-x86-bots.zip', 'updates_staged/binaries/'); my @files; my $start_dir = "updates_staged/binaries"; find( diff --git a/utils/sql/git/required/sharedtasks.sql b/utils/sql/git/required/sharedtasks.sql index 7a12f05a6..7985371c4 100644 --- a/utils/sql/git/required/sharedtasks.sql +++ b/utils/sql/git/required/sharedtasks.sql @@ -21,30 +21,30 @@ CREATE TABLE `task_replay_groups` ( PRIMARY KEY(`id`) ); CREATE TABLE `character_task_lockouts` ( - `charid` INT NOT NULL, + `character_id` INT NOT NULL, `replay_group` INT NOT NULL, `original_id` INT NOT NULL, `timestamp` INT NOT NULL, - PRIMARY KEY(`charid`, `replay_group`) + PRIMARY KEY(`character_id`, `replay_group`) ); CREATE TABLE `shared_task_state` ( `id` INT NOT NULL, - `taskid` INT NOT NULL, - `acceptedtime` INT NOT NULL, - `locked` TINYINT NOT NULL DEFAULT '0', + `task_id` INT NOT NULL, + `accepted_time` INT NOT NULL, + `is_locked` TINYINT NOT NULL DEFAULT '0', PRIMARY KEY(`id`) ); CREATE TABLE `shared_task_activities` ( - `shared_id` INT NOT NULL, + `shared_task_id` INT NOT NULL, `activity_id` INT NOT NULL, `done_count` INT NOT NULL, `completed` TINYINT, - PRIMARY KEY(`shared_id`, `activity_id`) + PRIMARY KEY(`shared_task_id`, `activity_id`) ); CREATE TABLE `shared_task_members` ( - `shared_id` INT NOT NULL, - `charid` INT NOT NULL, - `name` VARCHAR(64) NOT NULL, - `leader` TINYINT, - PRIMARY KEY(`charid`) + `shared_task_id` INT NOT NULL, + `character_id` INT NOT NULL, + `character_name` VARCHAR(64) NOT NULL, + `is_leader` TINYINT DEFAULT 0, + PRIMARY KEY(shared_task_id, character_id) ); diff --git a/utils/sql/system_tables.txt b/utils/sql/system_tables.txt index a6bcfe81b..f2d9f7529 100644 --- a/utils/sql/system_tables.txt +++ b/utils/sql/system_tables.txt @@ -23,6 +23,7 @@ data_buckets db_str doors eqtime +faction_base_data faction_list faction_list_mod fear_hints diff --git a/world/cliententry.cpp b/world/cliententry.cpp index c081a8541..d964cc355 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -373,7 +373,7 @@ int ClientListEntry::GetTaskLockoutTimeLeft(int id) const [id](const TaskTimer &a) { return a.ID == id; }); if (it != m_task_replay_timers.end()) - return it->expires - Timer::GetCurrentTime(); + return it->expires - time(nullptr); return 0; } @@ -384,7 +384,7 @@ int ClientListEntry::GetTaskLockoutTimeLeft(int id) const bool ClientListEntry::CleanExpiredTaskLockouts() const { std::string query = - StringFormat("DELETE FROM `character_task_lockouts` WHERE `charid` = %i AND `timestamp` > %i", pcharid, + StringFormat("DELETE FROM `character_task_lockouts` WHERE `character_id` = %i AND `timestamp` > %i", pcharid, Timer::GetCurrentTime()); auto results = database.QueryDatabase(query); return results.Success(); @@ -397,7 +397,7 @@ bool ClientListEntry::LoadTaskLockouts() { CleanExpiredTaskLockouts(); std::string query = StringFormat( - "SELECT `replay_group`, `original_id`, `timestamp` FROM `character_task_lockouts` WHERE `charid` = %i", + "SELECT `replay_group`, `original_id`, `timestamp` FROM `character_task_lockouts` WHERE `character_id` = %i", pcharid); auto results = database.QueryDatabase(query); if (!results.Success()) diff --git a/world/shared_tasks.cpp b/world/shared_tasks.cpp index 4610da789..a89b1b7e4 100644 --- a/world/shared_tasks.cpp +++ b/world/shared_tasks.cpp @@ -82,7 +82,7 @@ void SharedTaskManager::HandleTaskRequest(ServerPacket *pack) } auto &task = ret.first->second; - task.AddMember(leader_name, cle_leader, true); + task.AddMember(leader_name, cle_leader, cle_leader->CharID(), true); if (players.empty()) { // send instant success to leader @@ -133,7 +133,7 @@ void SharedTaskManager::HandleTaskRequest(ServerPacket *pack) // check our lock out timer int expires = cle->GetTaskLockoutExpire(task_id); - if ((expires - Timer::GetCurrentTime()) >= 0) { + if ((expires - time(nullptr)) >= 0) { // failure TODO: appropriate message, we need to send the timestamp here auto pack = new ServerPacket(ServerOP_TaskReject, leader_name.size() + 1 + 8); pack->WriteUInt32(0); // string ID or just generic fail message @@ -146,7 +146,7 @@ void SharedTaskManager::HandleTaskRequest(ServerPacket *pack) } // we're good, add to task - task.AddMember(name, cle); + task.AddMember(name, cle, cle->CharID()); } } @@ -374,7 +374,7 @@ bool SharedTaskManager::LoadSharedTaskState() { // one may think we should clean up expired tasks, but we don't just in case world is booting back up after a crash // we will clean them up in the normal process loop so zones get told to clean up - std::string query = "SELECT `id`, `taskid`, `acceptedtime`, `locked` FROM `shared_task_state`"; + std::string query = "SELECT `id`, `task_id`, `accepted_time`, `is_locked` FROM `shared_task_state`"; auto results = database.QueryDatabase(query); if (results.Success() && results.RowCount() > 0) { @@ -389,14 +389,14 @@ bool SharedTaskManager::LoadSharedTaskState() } } - query = "SELECT `shared_id`, `charid`, `name`, `leader` FROM `shared_task_members` ORDER BY shared_id ASC"; + query = "SELECT `shared_task_id`, `character_id`, `character_name`, `is_leader` FROM `shared_task_members` ORDER BY shared_task_id ASC"; results = database.QueryDatabase(query); if (results.Success() && results.RowCount() > 0) { for (auto row = results.begin(); row != results.end(); ++row) { int task_id = atoi(row[0]); // hmm not sure best way to do this, fine for now if (tasks.count(task_id) == 1) - tasks[task_id].AddMember(row[2], nullptr, atoi(row[3]) != 0); + tasks[task_id].AddMember(row[2], nullptr, atoi(row[1]), atoi(row[3]) != 0); } } @@ -404,7 +404,7 @@ bool SharedTaskManager::LoadSharedTaskState() // But the crash case may actually dictate we should :P // set next_id to highest used ID - query = "SELECT MAX(id) FROM shared_task_state"; + query = "SELECT IFNULL(MAX(id), 0) FROM shared_task_state"; results = database.QueryDatabase(query); if (results.Success() && results.RowCount() == 1) { auto row = results.begin(); diff --git a/world/shared_tasks.h b/world/shared_tasks.h index 79c51eea4..a56974628 100644 --- a/world/shared_tasks.h +++ b/world/shared_tasks.h @@ -6,16 +6,21 @@ #include "../common/servertalk.h" #include "../common/global_tasks.h" +#include "cliententry.h" class ClientListEntry; struct SharedTaskMember { std::string name; ClientListEntry *cle; + int char_id; bool leader; // TODO: monster mission stuff - SharedTaskMember() : cle(nullptr), leader(false) {} - SharedTaskMember(std::string name, ClientListEntry *cle, bool leader) : name(name), cle(cle), leader(leader) {} + SharedTaskMember() : cle(nullptr), char_id(0), leader(false) {} + SharedTaskMember(std::string name, ClientListEntry *cle, int char_id, bool leader) + : name(name), cle(cle), char_id(char_id), leader(leader) + { + } }; class SharedTask { @@ -24,11 +29,16 @@ public: SharedTask(int id, int task_id) : id(id), task_id(task_id), locked(false) {} ~SharedTask() {} - void AddMember(std::string name, ClientListEntry *cle = nullptr, bool leader = false) + void AddMember(std::string name, ClientListEntry *cle = nullptr, int char_id = 0, bool leader = false) { - members.push_back({name, cle, leader}); + members.push_back({name, cle, char_id, leader}); if (leader) leader_name = name; + if (char_id == 0) + return; + auto it = std::find(char_ids.begin(), char_ids.end(), char_id); + if (it == char_ids.end()) + char_ids.push_back(char_id); } void MemberLeftGame(ClientListEntry *cle); inline const std::string &GetLeaderName() const { return leader_name; } @@ -59,6 +69,7 @@ private: bool locked; std::string leader_name; std::vector members; + std::vector char_ids; // every char id of someone to be locked out, different in case they leave/removed ClientTaskInformation task_state; // book keeping friend class SharedTaskManager; diff --git a/zone/attack.cpp b/zone/attack.cpp index a51cd3163..ef0d4c81b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1877,6 +1877,10 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk if (r) r->MemberZoned(this); + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); + dead_timer.Start(5000, true); m_pp.zone_id = m_pp.binds[0].zoneId; m_pp.zoneInstance = m_pp.binds[0].instance_id; diff --git a/zone/bot.cpp b/zone/bot.cpp index b221620e2..143520076 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -439,6 +439,7 @@ NPCType *Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::st bot_npc_type->skip_global_loot = true; //bot_npc_type->rare_spawn = false; bot_npc_type->stuck_behavior = Ground; + bot_npc_type->skip_auto_scale = true; return bot_npc_type; } diff --git a/zone/client.cpp b/zone/client.cpp index 2d126598f..46ae726d6 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3396,6 +3396,11 @@ void Client::LinkDead() if(raid){ raid->MemberZoned(this); } + + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); + // save_timer.Start(2500); linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); SendAppearancePacket(AT_Linkdead, 1); diff --git a/zone/client.h b/zone/client.h index 158f03d54..a790dfbe0 100644 --- a/zone/client.h +++ b/zone/client.h @@ -791,7 +791,9 @@ public: uint32 GetCharMaxLevelFromQGlobal(); uint32 GetCharMaxLevelFromBucket(); + inline bool IsStanding() const {return (playeraction == 0);} inline bool IsSitting() const {return (playeraction == 1);} + inline bool IsCrouching() const {return (playeraction == 2);} inline bool IsBecomeNPC() const { return npcflag; } inline uint8 GetBecomeNPCLevel() const { return npclevel; } inline void SetBecomeNPC(bool flag) { npcflag = flag; } @@ -1027,6 +1029,7 @@ public: inline void ProcessTaskProximities(float X, float Y, float Z) { if(taskstate) taskstate->ProcessTaskProximities(this, X, Y, Z); } inline void AssignTask(int TaskID, int NPCID, bool enforce_level_requirement = false) { if (taskstate) taskstate->AcceptNewTask(this, TaskID, NPCID, enforce_level_requirement); } inline void AssignSharedTask(int TaskID, int NPCID, int id, int accepted_time, std::vector &members) { if (taskstate) taskstate->AcceptNewSharedTask(this, TaskID, NPCID, id, accepted_time, members); } + inline void AddToSharedTask(int TaskID) { if (taskstate) taskstate->AddToSharedTask(this, TaskID); } inline int ActiveSpeakTask(int NPCID) { if(taskstate) return taskstate->ActiveSpeakTask(NPCID); else return 0; } inline int ActiveSpeakActivity(int NPCID, int TaskID) { if(taskstate) return taskstate->ActiveSpeakActivity(NPCID, TaskID); else return 0; } inline void FailTask(int TaskID) { if(taskstate) taskstate->FailTask(this, TaskID); } @@ -1045,6 +1048,7 @@ public: inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); } inline int GetTaskLockoutExpire(int id) { return 0; } // stub inline int GetTaskLockoutTimeLeft(int id) { return 0; } // stub + inline SharedTaskState *GetSharedTask() { return taskstate ? taskstate->GetSharedTask() : nullptr; } inline const EQEmu::versions::ClientVersion ClientVersion() const { return m_ClientVersion; } inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 691c8f077..3143a196d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1799,7 +1799,7 @@ void Client::Handle_OP_AcceptNewSharedTask(const EQApplicationPacket *app) 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); + taskstate->RequestSharedTask(this, ant->task_id, ant->task_master_id); } void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 5c750f03a..d50c34488 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -149,6 +149,9 @@ bool Client::Process() { { myraid->MemberZoned(this); } + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); return(false); } @@ -171,6 +174,9 @@ bool Client::Process() { if (myraid) { myraid->MemberZoned(this); } + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); return false; //delete client } @@ -653,6 +659,9 @@ bool Client::Process() { myraid->MemberZoned(this); } } + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); OnDisconnect(false); return false; } @@ -694,6 +703,10 @@ void Client::OnDisconnect(bool hard_disconnect) { if (MyRaid) MyRaid->MemberZoned(this); + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); + parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); /* QS: PlayerLogConnectDisconnect */ @@ -2101,6 +2114,10 @@ void Client::HandleRespawnFromHover(uint32 Option) if(r) r->MemberZoned(this); + auto shared_task = GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(this); + m_pp.zone_id = chosen->zone_id; m_pp.zoneInstance = chosen->instance_id; database.MoveCharacterToZone(CharacterID(), database.GetZoneName(chosen->zone_id)); diff --git a/zone/entity.cpp b/zone/entity.cpp index ad8d1fb1b..4b4052175 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -537,6 +537,9 @@ void EntityList::MobProcess() Log(Logs::General, Logs::Error, "About to delete a client still in a raid."); r->MemberZoned(mob->CastToClient()); } + auto shared_task = mob->CastToClient()->GetSharedTask(); + if (shared_task) + shared_task->MemberZoned(mob); entity_list.RemoveClient(id); } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 9c802b5a3..e86610697 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -972,8 +972,8 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd safe_delete(outapp); } else { - outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); - MoveItem_Struct* delitem = (MoveItem_Struct*)outapp->pBuffer; + outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer; delitem->from_slot = slot_id; delitem->to_slot = 0xFFFFFFFF; delitem->number_in_stack = 0xFFFFFFFF; diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a8991f97a..fc89db7d9 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -615,11 +615,21 @@ void Lua_Client::UntrainDiscAll(bool update_client) { self->UntrainDiscAll(update_client); } +bool Lua_Client::IsStanding() { + Lua_Safe_Call_Bool(); + return self->IsStanding(); +} + bool Lua_Client::IsSitting() { Lua_Safe_Call_Bool(); return self->IsSitting(); } +bool Lua_Client::IsCrouching() { + Lua_Safe_Call_Bool(); + return self->IsCrouching(); +} + void Lua_Client::SetFeigned(bool v) { Lua_Safe_Call_Void(); self->SetFeigned(v); @@ -1621,7 +1631,9 @@ luabind::scope lua_register_client() { .def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc) .def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll) .def("UntrainDiscAll", (void(Lua_Client::*)(bool))&Lua_Client::UntrainDiscAll) + .def("IsStanding", (bool(Lua_Client::*)(void))&Lua_Client::IsStanding) .def("IsSitting", (bool(Lua_Client::*)(void))&Lua_Client::IsSitting) + .def("IsCrouching", (bool(Lua_Client::*)(void))&Lua_Client::IsCrouching) .def("SetFeigned", (void(Lua_Client::*)(bool))&Lua_Client::SetFeigned) .def("GetFeigned", (bool(Lua_Client::*)(void))&Lua_Client::GetFeigned) .def("AutoSplitEnabled", (bool(Lua_Client::*)(void))&Lua_Client::AutoSplitEnabled) diff --git a/zone/lua_client.h b/zone/lua_client.h index 715ea141d..95d6a0f55 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -148,7 +148,9 @@ public: void UntrainDisc(int slot, bool update_client); void UntrainDiscAll(); void UntrainDiscAll(bool update_client); + bool IsStanding(); bool IsSitting(); + bool IsCrouching(); void SetFeigned(bool v); bool GetFeigned(); bool AutoSplitEnabled(); diff --git a/zone/npc.cpp b/zone/npc.cpp index ac8825774..355196d2e 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -239,6 +239,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi p_depop = false; loottable_id = npc_type_data->loottable_id; skip_global_loot = npc_type_data->skip_global_loot; + skip_auto_scale = npc_type_data->skip_auto_scale; rare_spawn = npc_type_data->rare_spawn; no_target_hotkey = npc_type_data->no_target_hotkey; primary_faction = 0; diff --git a/zone/npc.h b/zone/npc.h index b9e0458e6..1957b8e63 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -468,6 +468,7 @@ public: virtual int GetStuckBehavior() const { return NPCTypedata_ours ? NPCTypedata_ours->stuck_behavior : NPCTypedata->stuck_behavior; } + inline bool IsSkipAutoScale() const { return skip_auto_scale; } protected: @@ -612,6 +613,7 @@ protected: private: uint32 loottable_id; bool skip_global_loot; + bool skip_auto_scale; bool p_depop; }; diff --git a/zone/npc_scale_manager.cpp b/zone/npc_scale_manager.cpp index dd1b9734d..c17619482 100644 --- a/zone/npc_scale_manager.cpp +++ b/zone/npc_scale_manager.cpp @@ -26,6 +26,9 @@ */ void NpcScaleManager::ScaleNPC(NPC * npc) { + if (npc->IsSkipAutoScale()) + return; + int8 npc_type = GetNPCScalingType(npc); int npc_level = npc->GetLevel(); bool is_auto_scaled = IsAutoScaled(npc); @@ -621,4 +624,4 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc) auto results = database.QueryDatabase(query); return results.Success(); -} \ No newline at end of file +} diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index fcdeca352..9c0ae4200 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2596,6 +2596,32 @@ XS(XS_Client_UntrainDiscAll) { XSRETURN_EMPTY; } +XS(XS_Client_IsStanding); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsStanding) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsStanding(THIS)"); + { + Client * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->IsStanding(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_Client_IsSitting); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_IsSitting) { dXSARGS; @@ -2620,6 +2646,32 @@ XS(XS_Client_IsSitting) { XSRETURN(1); } +XS(XS_Client_IsCrouching); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsCrouching) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsCrouching(THIS)"); + { + Client * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->IsCrouching(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_Client_IsBecomeNPC); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_IsBecomeNPC) { dXSARGS; @@ -6338,7 +6390,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "IsLD"), XS_Client_IsLD, file, "$"); newXSproto(strcpy(buf, "IsMedding"), XS_Client_IsMedding, file, "$"); newXSproto(strcpy(buf, "IsRaidGrouped"), XS_Client_IsRaidGrouped, file, "$"); + newXSproto(strcpy(buf, "IsStanding"), XS_Client_IsStanding, file, "$"); newXSproto(strcpy(buf, "IsSitting"), XS_Client_IsSitting, file, "$"); + newXSproto(strcpy(buf, "IsCrouching"), XS_Client_IsCrouching, file, "$"); newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$"); newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$"); newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$"); diff --git a/zone/tasks.cpp b/zone/tasks.cpp index d450320fc..a92d7151c 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -3084,6 +3084,21 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, ClientTaskInf safe_delete(outapp); } +// TODO: stub +SharedTaskState *TaskManager::LoadSharedTask(int id) +{ + return nullptr; +} + +SharedTaskState *TaskManager::GetSharedTask(int id) +{ + auto it = SharedTasks.find(id); + if (it == SharedTasks.end()) + return nullptr; + + return &(it->second); +} + bool ClientTaskState::IsTaskActivityCompleted(TaskType type, int index, int ActivityID) { switch (type) { @@ -3387,10 +3402,13 @@ 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) +/* + * This function is a proxy for OP_AccetNewSharedTask since it has to fire to + * world. We do a handful of checks on the leader, then build a packet with a + * list of all the members in our group/raid if applicable. The verification for + * the other members is done in world. + */ +void ClientTaskState::RequestSharedTask(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); @@ -3550,11 +3568,47 @@ void ClientTaskState::AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int worldserver.SendPacket(pack); delete pack; + ActiveSharedTask = task_state; + return; // there are a few issues we need to solve with this } +/* + * This function is called when world sends ServerOP_TaskZoneCreated to trigger + * members to join. If the task doesn't already exist, we need to load it from + * the DB. + * + * This is also called in LoadClientTaskState() when they notice they have a + * shared task they need to join. (Called from first OP_ZoneEntry) + */ +void ClientTaskState::AddToSharedTask(Client *c, int TaskID) +{ + auto task = taskmanager->GetSharedTask(TaskID); + if (!task) + task = taskmanager->LoadSharedTask(TaskID); + + if (!task) {// FUCK + return; + } + + task->MemberEnterZone(c); + + // send packets + auto task_activity = task->GetActivity(); + taskmanager->SendSingleActiveTaskToClient(c, *task_activity, false, true); + task->SendMembersList(c); + + // So normally getting a task we would send EVENT_TASK_ACCEPTED here, but + // this isn't an accept step. I guess we should add another event in case + // they need the same thing TODO + + ActiveSharedTask = task; + + return; +} + void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) { float LastX = c->ProximityX(); @@ -3780,7 +3834,7 @@ void SharedTaskState::LockTask() void SharedTaskState::MemberZoned(Mob *player) { auto it = std::find_if(members.begin(), members.end(), - [&player](const SharedTaskMember &a) { return a.name == player->GetName(); }); + [&player](const SharedTaskMember &a) { return a.entity == player; }); if (it == members.end()) // guess they weren't in this group, w/e return; diff --git a/zone/tasks.h b/zone/tasks.h index 3d1cf628b..c06ddfddb 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -146,8 +146,6 @@ 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, int id, int accepted_time, std::vector &members); - 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); @@ -180,9 +178,15 @@ public: int ActiveTasksInSet(int TaskSetID); int CompletedTasksInSet(int TaskSetID); bool HasSlotForTask(TaskInformation *task); + // shared task related functions + void AcceptNewSharedTask(Client *c, int TaskID, int NPCID, int id, int accepted_time, std::vector &members); + void AddToSharedTask(Client *c, int TaskID); + void RequestSharedTask(Client *c, int TaskID, int NPCID, bool enforce_level_requirement = false); inline bool HasFreeTaskSlot() { return ActiveTask.TaskID == TASKSLOTEMPTY; } + inline SharedTaskState *GetSharedTask() { return ActiveSharedTask ; } + friend class TaskManager; private: @@ -266,8 +270,9 @@ public: bool IsTaskRepeatable(int TaskID); friend class ClientTaskState; - void LoadSharedTask(int id); // loads the shared task state + SharedTaskState *LoadSharedTask(int id); // loads the shared task state SharedTaskState *CreateSharedTask(int id, int task_id); + SharedTaskState *GetSharedTask(int id); private: TaskGoalListManager GoalListManager; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 524f9e9b1..755ba037a 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1991,6 +1991,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_TaskZoneCreated: + { + int task_id = pack->ReadUInt32(); + char name[64] = { 0 }; + pack->ReadString(name); + + auto client = entity_list.GetClientByName(name); + if (client) + client->AddToSharedTask(task_id); + break; + } default: { std::cout << " Unknown ZSopcode:" << (int)pack->opcode; std::cout << " size:" << pack->size << std::endl; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 093e1e30c..76e3a4c5c 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2675,6 +2675,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load temp_npctype_data->rare_spawn = atoi(row[108]) != 0; temp_npctype_data->stuck_behavior = atoi(row[109]); temp_npctype_data->use_model = atoi(row[110]); + temp_npctype_data->skip_auto_scale = false; // hardcoded here for now // If NPC with duplicate NPC id already in table, // free item we attempted to add. @@ -2877,6 +2878,8 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client tmpNPCType->scalerate = atoi(row[43]); tmpNPCType->spellscale = atoi(row[44]); tmpNPCType->healscale = atoi(row[45]); + tmpNPCType->skip_global_loot = true; + tmpNPCType->skip_auto_scale = true; // If Merc with duplicate NPC id already in table, // free item we attempted to add. diff --git a/zone/zonedump.h b/zone/zonedump.h index be978a1be..71f530171 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -143,6 +143,7 @@ struct NPCType bool untargetable; bool skip_global_loot; bool rare_spawn; + bool skip_auto_scale; // just so it doesn't mess up bots or mercs, probably should add to DB too just in case int8 stuck_behavior; uint16 use_model; };