From cee09f634436cbab79628ed3e3718c0dde8d0284 Mon Sep 17 00:00:00 2001 From: dongresource Date: Tue, 16 Mar 2021 22:06:10 +0100 Subject: [PATCH] [refactor] Mark all internal functions static All packet handlers and helper functions that are only used in the source file they're declared in have been taken out of the namespaces in the corresponding header files, have been marked static, and have been reordered to avoid the need for declarations at the top of each source file. Each source file now contains a "using namespace" directive so that the static functions don't need to prefix the source file's symbols with their namespace. All redundant namespace prefixes found have been removed. An unused nano power resetting function in NanoManager has been removed. --- src/Abilities.hpp | 2 + src/BuddyManager.cpp | 78 +-- src/BuddyManager.hpp | 30 +- src/ChatManager.cpp | 70 +-- src/ChatManager.hpp | 13 - src/ChunkManager.cpp | 166 +++--- src/ChunkManager.hpp | 9 - src/Combat.cpp | 234 ++++----- src/Combat.hpp | 17 - src/Email.cpp | 27 +- src/Email.hpp | 18 - src/GroupManager.cpp | 50 +- src/GroupManager.hpp | 5 - src/ItemManager.cpp | 385 +++++++------- src/ItemManager.hpp | 7 - src/MissionManager.cpp | 612 +++++++++++----------- src/MissionManager.hpp | 10 - src/MobAI.cpp | 42 +- src/Monitor.cpp | 108 ++-- src/Monitor.hpp | 1 - src/NPCManager.cpp | 71 +-- src/NPCManager.hpp | 9 - src/NanoManager.cpp | 469 +++++++++-------- src/NanoManager.hpp | 14 - src/PlayerManager.cpp | 150 +++--- src/PlayerManager.hpp | 19 - src/RacingManager.cpp | 23 +- src/RacingManager.hpp | 5 - src/TableData.cpp | 1042 +++++++++++++++++++------------------- src/TableData.hpp | 8 - src/Trading.cpp | 6 +- src/TransportManager.cpp | 34 +- src/TransportManager.hpp | 7 - src/Vendor.cpp | 16 +- src/Vendor.hpp | 7 - src/main.cpp | 1 - 36 files changed, 1808 insertions(+), 1957 deletions(-) diff --git a/src/Abilities.hpp b/src/Abilities.hpp index 439093d..d8e09d2 100644 --- a/src/Abilities.hpp +++ b/src/Abilities.hpp @@ -55,6 +55,8 @@ namespace NanoManager { void nanoUnbuff(CNSocket* sock, std::vector targetData, int32_t bitFlag, int16_t timeBuffID, int16_t amount, bool groupPower); int applyBuff(CNSocket* sock, int skillID, int eTBU, int eTBT, int32_t groupFlags); + + std::vector findTargets(Player* plr, int skillID, CNPacketData* data = nullptr); } namespace Combat { diff --git a/src/BuddyManager.cpp b/src/BuddyManager.cpp index b898242..d6d07b8 100644 --- a/src/BuddyManager.cpp +++ b/src/BuddyManager.cpp @@ -12,18 +12,29 @@ #include #include -void BuddyManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_REQUEST_MAKE_BUDDY, requestBuddy); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIND_NAME_MAKE_BUDDY, reqBuddyByName); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_ACCEPT_MAKE_BUDDY, reqAcceptBuddy); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIND_NAME_ACCEPT_BUDDY, reqFindNameBuddyAccept); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_GET_BUDDY_STATE, reqPktGetBuddyState); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SET_BUDDY_BLOCK, reqBuddyBlock); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SET_PC_BLOCK, reqPlayerBlock); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_REMOVE_BUDDY, reqBuddyDelete); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BUDDY_WARP, reqBuddyWarp); +using namespace BuddyManager; + +#pragma region Helper methods + +static int getAvailableBuddySlot(Player* plr) { + int slot = -1; + for (int i = 0; i < 50; i++) { + if (plr->buddyIDs[i] == 0) + return i; + } + return slot; } +static bool playerHasBuddyWithID(Player* plr, int buddyID) { + for (int i = 0; i < 50; i++) { + if (plr->buddyIDs[i] == buddyID) + return true; + } + return false; +} + +#pragma endregion + // Refresh buddy list void BuddyManager::refreshBuddyList(CNSocket* sock) { Player* plr = PlayerManager::getPlayer(sock); @@ -76,7 +87,7 @@ void BuddyManager::refreshBuddyList(CNSocket* sock) { } // Buddy request -void BuddyManager::requestBuddy(CNSocket* sock, CNPacketData* data) { +static void requestBuddy(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_REQUEST_MAKE_BUDDY)) return; // malformed packet @@ -117,7 +128,7 @@ void BuddyManager::requestBuddy(CNSocket* sock, CNPacketData* data) { } // Sending buddy request by player name -void BuddyManager::reqBuddyByName(CNSocket* sock, CNPacketData* data) { +static void reqBuddyByName(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_FIND_NAME_MAKE_BUDDY)) { return; // malformed packet } @@ -144,7 +155,7 @@ void BuddyManager::reqBuddyByName(CNSocket* sock, CNPacketData* data) { } // Accepting buddy request -void BuddyManager::reqAcceptBuddy(CNSocket* sock, CNPacketData* data) { +static void reqAcceptBuddy(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_ACCEPT_MAKE_BUDDY)) return; // malformed packet @@ -213,7 +224,7 @@ void BuddyManager::reqAcceptBuddy(CNSocket* sock, CNPacketData* data) { } // Accepting buddy request from the find name request -void BuddyManager::reqFindNameBuddyAccept(CNSocket* sock, CNPacketData* data) { +static void reqFindNameBuddyAccept(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_FIND_NAME_ACCEPT_BUDDY)) { return; // malformed packet } @@ -284,7 +295,7 @@ void BuddyManager::reqFindNameBuddyAccept(CNSocket* sock, CNPacketData* data) { } // Getting buddy state -void BuddyManager::reqPktGetBuddyState(CNSocket* sock, CNPacketData* data) { +static void reqPktGetBuddyState(CNSocket* sock, CNPacketData* data) { Player* plr = PlayerManager::getPlayer(sock); /* @@ -307,7 +318,7 @@ void BuddyManager::reqPktGetBuddyState(CNSocket* sock, CNPacketData* data) { } // Blocking the buddy -void BuddyManager::reqBuddyBlock(CNSocket* sock, CNPacketData* data) { +static void reqBuddyBlock(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SET_BUDDY_BLOCK)) return; // malformed packet @@ -353,7 +364,7 @@ void BuddyManager::reqBuddyBlock(CNSocket* sock, CNPacketData* data) { } // block non-buddy -void BuddyManager::reqPlayerBlock(CNSocket* sock, CNPacketData* data) { +static void reqPlayerBlock(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SET_PC_BLOCK)) return; @@ -381,7 +392,7 @@ void BuddyManager::reqPlayerBlock(CNSocket* sock, CNPacketData* data) { } // Deleting the buddy -void BuddyManager::reqBuddyDelete(CNSocket* sock, CNPacketData* data) { +static void reqBuddyDelete(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_REMOVE_BUDDY)) return; // malformed packet @@ -431,7 +442,7 @@ void BuddyManager::reqBuddyDelete(CNSocket* sock, CNPacketData* data) { } // Warping to buddy -void BuddyManager::reqBuddyWarp(CNSocket* sock, CNPacketData* data) { +static void reqBuddyWarp(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_BUDDY_WARP)) return; // malformed packet Player *plr = PlayerManager::getPlayer(sock); @@ -472,23 +483,14 @@ fail: sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BUDDY_WARP_FAIL, sizeof(sP_FE2CL_REP_PC_BUDDY_WARP_FAIL)); } -#pragma region Helper methods - -int BuddyManager::getAvailableBuddySlot(Player* plr) { - int slot = -1; - for (int i = 0; i < 50; i++) { - if (plr->buddyIDs[i] == 0) - return i; - } - return slot; +void BuddyManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_REQUEST_MAKE_BUDDY, requestBuddy); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIND_NAME_MAKE_BUDDY, reqBuddyByName); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_ACCEPT_MAKE_BUDDY, reqAcceptBuddy); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIND_NAME_ACCEPT_BUDDY, reqFindNameBuddyAccept); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_GET_BUDDY_STATE, reqPktGetBuddyState); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SET_BUDDY_BLOCK, reqBuddyBlock); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SET_PC_BLOCK, reqPlayerBlock); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_REMOVE_BUDDY, reqBuddyDelete); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BUDDY_WARP, reqBuddyWarp); } - -bool BuddyManager::playerHasBuddyWithID(Player* plr, int buddyID) { - for (int i = 0; i < 50; i++) { - if (plr->buddyIDs[i] == buddyID) - return true; - } - return false; -} - -#pragma endregion diff --git a/src/BuddyManager.hpp b/src/BuddyManager.hpp index 27b3ea2..76c7570 100644 --- a/src/BuddyManager.hpp +++ b/src/BuddyManager.hpp @@ -3,39 +3,11 @@ #include "Player.hpp" #include "CNProtocol.hpp" #include "CNStructs.hpp" -#include "CNShardServer.hpp" - -#include -#include +#include "CNProtocol.hpp" namespace BuddyManager { void init(); // Buddy list void refreshBuddyList(CNSocket* sock); - - // Buddy requests - void requestBuddy(CNSocket* sock, CNPacketData* data); - void reqBuddyByName(CNSocket* sock, CNPacketData* data); - - // Buddy accepting - void reqAcceptBuddy(CNSocket* sock, CNPacketData* data); - void reqFindNameBuddyAccept(CNSocket* sock, CNPacketData* data); - - // Getting buddy state - void reqPktGetBuddyState(CNSocket* sock, CNPacketData* data); - - // Blocking/removing buddies - void reqBuddyBlock(CNSocket* sock, CNPacketData* data); - void reqPlayerBlock(CNSocket* sock, CNPacketData* data); - void reqBuddyDelete(CNSocket* sock, CNPacketData* data); - - // Buddy warping - void reqBuddyWarp(CNSocket* sock, CNPacketData* data); - - // helper methods - - // Name checks - int getAvailableBuddySlot(Player* plr); - bool playerHasBuddyWithID(Player* plr, int buddyID); } diff --git a/src/ChatManager.cpp b/src/ChatManager.cpp index bd3aefa..049c0d9 100644 --- a/src/ChatManager.cpp +++ b/src/ChatManager.cpp @@ -7,23 +7,9 @@ std::vector ChatManager::dump; -void ChatManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_FREECHAT_MESSAGE, chatHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT, emoteHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE, menuChatHandler); +using namespace ChatManager; - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_BUDDY_FREECHAT_MESSAGE, buddyChatHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_BUDDY_MENUCHAT_MESSAGE, buddyMenuChatHandler); - - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, tradeChatHandler); - - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE, groupChatHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE, groupMenuChatHandler); - - REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_ANNOUNCE, announcementHandler); -} - -void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) { +static void chatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE)) return; // malformed packet @@ -57,7 +43,7 @@ void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) { PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC)); } -void ChatManager::menuChatHandler(CNSocket* sock, CNPacketData* data) { +static void menuChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE)) return; // malformed packet @@ -83,7 +69,7 @@ void ChatManager::menuChatHandler(CNSocket* sock, CNPacketData* data) { PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC)); } -void ChatManager::emoteHandler(CNSocket* sock, CNPacketData* data) { +static void emoteHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT)) return; // ignore the malformed packet @@ -113,7 +99,7 @@ void ChatManager::sendServerMessage(CNSocket* sock, std::string msg) { sock->sendPacket((void*)&motd, P_FE2CL_PC_MOTD_LOGIN, sizeof(sP_FE2CL_PC_MOTD_LOGIN)); } -void ChatManager::announcementHandler(CNSocket* sock, CNPacketData* data) { +static void announcementHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_ANNOUNCE)) return; // ignore malformed packet @@ -151,7 +137,7 @@ void ChatManager::announcementHandler(CNSocket* sock, CNPacketData* data) { } // Buddy freechatting -void ChatManager::buddyChatHandler(CNSocket* sock, CNPacketData* data) { +static void buddyChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_BUDDY_FREECHAT_MESSAGE)) return; // malformed packet @@ -171,7 +157,7 @@ void ChatManager::buddyChatHandler(CNSocket* sock, CNPacketData* data) { resp.iToPCUID = pkt->iBuddyPCUID; resp.iEmoteCode = pkt->iEmoteCode; - std::string fullChat = ChatManager::sanitizeText(U16toU8(pkt->szFreeChat)); + std::string fullChat = sanitizeText(U16toU8(pkt->szFreeChat)); if (fullChat.length() > 1 && fullChat[0] == CMD_PREFIX) { // PREFIX CustomCommands::runCmd(fullChat, sock); @@ -183,7 +169,7 @@ void ChatManager::buddyChatHandler(CNSocket* sock, CNPacketData* data) { std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::cout << logLine << std::endl; - ChatManager::dump.push_back(logLine); + dump.push_back(logLine); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); @@ -192,7 +178,7 @@ void ChatManager::buddyChatHandler(CNSocket* sock, CNPacketData* data) { } // Buddy menuchat -void ChatManager::buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) { +static void buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_BUDDY_MENUCHAT_MESSAGE)) return; // malformed packet @@ -212,11 +198,11 @@ void ChatManager::buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) { resp.iToPCUID = pkt->iBuddyPCUID; resp.iEmoteCode = pkt->iEmoteCode; - std::string fullChat = ChatManager::sanitizeText(U16toU8(pkt->szFreeChat)); + std::string fullChat = sanitizeText(U16toU8(pkt->szFreeChat)); std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::cout << logLine << std::endl; - ChatManager::dump.push_back(logLine); + dump.push_back(logLine); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); @@ -224,7 +210,7 @@ void ChatManager::buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) { otherSock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_SUCC)); // broadcast send to receiver } -void ChatManager::tradeChatHandler(CNSocket* sock, CNPacketData* data) { +static void tradeChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT)) return; // malformed packet sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT* pacdat = (sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT*)data->buf; @@ -245,20 +231,20 @@ void ChatManager::tradeChatHandler(CNSocket* sock, CNPacketData* data) { resp.iID_Request = pacdat->iID_Request; resp.iID_From = pacdat->iID_From; resp.iID_To = pacdat->iID_To; - std::string fullChat = ChatManager::sanitizeText(U16toU8(pacdat->szFreeChat)); + std::string fullChat = sanitizeText(U16toU8(pacdat->szFreeChat)); U8toU16(fullChat, resp.szFreeChat, sizeof(resp.szFreeChat)); std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::cout << logLine << std::endl; - ChatManager::dump.push_back(logLine); + dump.push_back(logLine); resp.iEmoteCode = pacdat->iEmoteCode; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); } -void ChatManager::groupChatHandler(CNSocket* sock, CNPacketData* data) { +static void groupChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE)) return; // malformed packet @@ -269,7 +255,7 @@ void ChatManager::groupChatHandler(CNSocket* sock, CNPacketData* data) { if (otherPlr == nullptr) return; - std::string fullChat = ChatManager::sanitizeText(U16toU8(chat->szFreeChat)); + std::string fullChat = sanitizeText(U16toU8(chat->szFreeChat)); if (fullChat.length() > 1 && fullChat[0] == CMD_PREFIX) { // PREFIX CustomCommands::runCmd(fullChat, sock); @@ -281,7 +267,7 @@ void ChatManager::groupChatHandler(CNSocket* sock, CNPacketData* data) { std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::cout << logLine << std::endl; - ChatManager::dump.push_back(logLine); + dump.push_back(logLine); // send to client INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp); @@ -293,7 +279,7 @@ void ChatManager::groupChatHandler(CNSocket* sock, CNPacketData* data) { GroupManager::sendToGroup(otherPlr, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC)); } -void ChatManager::groupMenuChatHandler(CNSocket* sock, CNPacketData* data) { +static void groupMenuChatHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE)) return; // malformed packet @@ -304,11 +290,11 @@ void ChatManager::groupMenuChatHandler(CNSocket* sock, CNPacketData* data) { if (otherPlr == nullptr) return; - std::string fullChat = ChatManager::sanitizeText(U16toU8(chat->szFreeChat)); + std::string fullChat = sanitizeText(U16toU8(chat->szFreeChat)); std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::cout << logLine << std::endl; - ChatManager::dump.push_back(logLine); + dump.push_back(logLine); // send to client INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp); @@ -343,3 +329,19 @@ std::string ChatManager::sanitizeText(std::string text, bool allowNewlines) { return std::string(buf); } + +void ChatManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_FREECHAT_MESSAGE, chatHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT, emoteHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE, menuChatHandler); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_BUDDY_FREECHAT_MESSAGE, buddyChatHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_BUDDY_MENUCHAT_MESSAGE, buddyMenuChatHandler); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, tradeChatHandler); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE, groupChatHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE, groupMenuChatHandler); + + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_ANNOUNCE, announcementHandler); +} diff --git a/src/ChatManager.hpp b/src/ChatManager.hpp index 145289f..f5b6a3a 100644 --- a/src/ChatManager.hpp +++ b/src/ChatManager.hpp @@ -8,19 +8,6 @@ namespace ChatManager { extern std::vector dump; void init(); - void chatHandler(CNSocket* sock, CNPacketData* data); - void emoteHandler(CNSocket* sock, CNPacketData* data); - void menuChatHandler(CNSocket* sock, CNPacketData* data); - void announcementHandler(CNSocket* sock, CNPacketData* data); - - void buddyChatHandler(CNSocket* sock, CNPacketData* data); - void buddyMenuChatHandler(CNSocket* sock, CNPacketData* data); - - void tradeChatHandler(CNSocket* sock, CNPacketData* data); - - void groupChatHandler(CNSocket* sock, CNPacketData* data); - void groupMenuChatHandler(CNSocket* sock, CNPacketData* data); - void sendServerMessage(CNSocket* sock, std::string msg); // uses MOTD std::string sanitizeText(std::string text, bool allowNewlines=false); } diff --git a/src/ChunkManager.cpp b/src/ChunkManager.cpp index 567064b..188958b 100644 --- a/src/ChunkManager.cpp +++ b/src/ChunkManager.cpp @@ -4,11 +4,11 @@ #include "settings.hpp" #include "Combat.hpp" +using namespace ChunkManager; + std::map ChunkManager::chunks; -void ChunkManager::init() {} // stubbed - -void ChunkManager::newChunk(ChunkPos pos) { +static void newChunk(ChunkPos pos) { if (chunkExists(pos)) { std::cout << "[WARN] Tried to create a chunk that already exists\n"; return; @@ -31,7 +31,7 @@ void ChunkManager::newChunk(ChunkPos pos) { } } -void ChunkManager::deleteChunk(ChunkPos pos) { +static void deleteChunk(ChunkPos pos) { if (!chunkExists(pos)) { std::cout << "[WARN] Tried to delete a chunk that doesn't exist\n"; return; @@ -53,78 +53,6 @@ void ChunkManager::deleteChunk(ChunkPos pos) { delete chunk; // free from memory } -void ChunkManager::updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to) { - Player* plr = PlayerManager::getPlayer(sock); - - // if the new chunk doesn't exist, make it first - if (!ChunkManager::chunkExists(to)) - newChunk(to); - - // move to other chunk's player set - untrackPlayer(from, sock); // this will delete the chunk if it's empty - trackPlayer(to, sock); - - // calculate viewable chunks from both points - std::set oldViewables = getViewableChunks(from); - std::set newViewables = getViewableChunks(to); - std::set toExit, toEnter; - - /* - * Calculate diffs. This is done to prevent phasing on chunk borders. - * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. - * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. - */ - std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), - std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) - std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), - std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) - - // update views - removePlayerFromChunks(toExit, sock); - addPlayerToChunks(toEnter, sock); - - plr->chunkPos = to; // update cached chunk position - // updated cached viewable chunks - plr->viewableChunks->clear(); - plr->viewableChunks->insert(newViewables.begin(), newViewables.end()); -} - -void ChunkManager::updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to) { - BaseNPC* npc = NPCManager::NPCs[id]; - - // if the new chunk doesn't exist, make it first - if (!ChunkManager::chunkExists(to)) - newChunk(to); - - // move to other chunk's player set - untrackNPC(from, id); // this will delete the chunk if it's empty - trackNPC(to, id); - - // calculate viewable chunks from both points - std::set oldViewables = getViewableChunks(from); - std::set newViewables = getViewableChunks(to); - std::set toExit, toEnter; - - /* - * Calculate diffs. This is done to prevent phasing on chunk borders. - * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. - * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. - */ - std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), - std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) - std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), - std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) - - // update views - removeNPCFromChunks(toExit, id); - addNPCToChunks(toEnter, id); - - npc->chunkPos = to; // update cached chunk position - // updated cached viewable chunks - npc->viewableChunks->clear(); - npc->viewableChunks->insert(newViewables.begin(), newViewables.end()); -} - void ChunkManager::trackPlayer(ChunkPos chunkPos, CNSocket* sock) { if (!chunkExists(chunkPos)) return; // shouldn't happen @@ -371,7 +299,7 @@ void ChunkManager::removeNPCFromChunks(std::set chnks, int32_t id) { } } -void ChunkManager::emptyChunk(ChunkPos chunkPos) { +static void emptyChunk(ChunkPos chunkPos) { if (!chunkExists(chunkPos)) { std::cout << "[WARN] Tried to empty chunk that doesn't exist\n"; return; // chunk doesn't exist, we don't need to do anything @@ -392,6 +320,78 @@ void ChunkManager::emptyChunk(ChunkPos chunkPos) { } } +void ChunkManager::updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to) { + Player* plr = PlayerManager::getPlayer(sock); + + // if the new chunk doesn't exist, make it first + if (!chunkExists(to)) + newChunk(to); + + // move to other chunk's player set + untrackPlayer(from, sock); // this will delete the chunk if it's empty + trackPlayer(to, sock); + + // calculate viewable chunks from both points + std::set oldViewables = getViewableChunks(from); + std::set newViewables = getViewableChunks(to); + std::set toExit, toEnter; + + /* + * Calculate diffs. This is done to prevent phasing on chunk borders. + * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. + * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. + */ + std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), + std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) + std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), + std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) + + // update views + removePlayerFromChunks(toExit, sock); + addPlayerToChunks(toEnter, sock); + + plr->chunkPos = to; // update cached chunk position + // updated cached viewable chunks + plr->viewableChunks->clear(); + plr->viewableChunks->insert(newViewables.begin(), newViewables.end()); +} + +void ChunkManager::updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to) { + BaseNPC* npc = NPCManager::NPCs[id]; + + // if the new chunk doesn't exist, make it first + if (!chunkExists(to)) + newChunk(to); + + // move to other chunk's player set + untrackNPC(from, id); // this will delete the chunk if it's empty + trackNPC(to, id); + + // calculate viewable chunks from both points + std::set oldViewables = getViewableChunks(from); + std::set newViewables = getViewableChunks(to); + std::set toExit, toEnter; + + /* + * Calculate diffs. This is done to prevent phasing on chunk borders. + * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. + * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. + */ + std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), + std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) + std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), + std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) + + // update views + removeNPCFromChunks(toExit, id); + addNPCToChunks(toEnter, id); + + npc->chunkPos = to; // update cached chunk position + // updated cached viewable chunks + npc->viewableChunks->clear(); + npc->viewableChunks->insert(newViewables.begin(), newViewables.end()); +} + bool ChunkManager::chunkExists(ChunkPos chunk) { return chunks.find(chunk) != chunks.end(); } @@ -424,10 +424,10 @@ std::set ChunkManager::getViewableChunks(ChunkPos chunk) { /* * inefficient algorithm to get all chunks from a specific instance */ -std::vector ChunkManager::getChunksInMap(uint64_t mapNum) { +static std::vector getChunksInMap(uint64_t mapNum) { std::vector chnks; - for (auto it = ChunkManager::chunks.begin(); it != ChunkManager::chunks.end(); it++) { + for (auto it = chunks.begin(); it != chunks.end(); it++) { if (std::get<2>(it->first) == mapNum) { chnks.push_back(it->first); } @@ -451,8 +451,8 @@ bool ChunkManager::inPopulatedChunks(std::set* chnks) { void ChunkManager::createInstance(uint64_t instanceID) { - std::vector templateChunks = ChunkManager::getChunksInMap(MAPNUM(instanceID)); // base instance chunks - if (ChunkManager::getChunksInMap(instanceID).size() == 0) { // only instantiate if the instance doesn't exist already + std::vector templateChunks = getChunksInMap(MAPNUM(instanceID)); // base instance chunks + if (getChunksInMap(instanceID).size() == 0) { // only instantiate if the instance doesn't exist already std::cout << "Creating instance " << instanceID << std::endl; for (ChunkPos &coords : templateChunks) { for (int npcID : chunks[coords]->NPCs) { @@ -508,9 +508,9 @@ void ChunkManager::createInstance(uint64_t instanceID) { } } -void ChunkManager::destroyInstance(uint64_t instanceID) { +static void destroyInstance(uint64_t instanceID) { - std::vector instanceChunks = ChunkManager::getChunksInMap(instanceID); + std::vector instanceChunks = getChunksInMap(instanceID); std::cout << "Deleting instance " << instanceID << " (" << instanceChunks.size() << " chunks)" << std::endl; for (ChunkPos& coords : instanceChunks) { emptyChunk(coords); diff --git a/src/ChunkManager.hpp b/src/ChunkManager.hpp index 406e98a..51fe172 100644 --- a/src/ChunkManager.hpp +++ b/src/ChunkManager.hpp @@ -22,14 +22,8 @@ enum { }; namespace ChunkManager { - void init(); - void cleanup(); - extern std::map chunks; - void newChunk(ChunkPos pos); - void deleteChunk(ChunkPos pos); - void updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to); void updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to); @@ -44,13 +38,10 @@ namespace ChunkManager { void removeNPCFromChunks(std::set chnks, int32_t id); bool chunkExists(ChunkPos chunk); - void emptyChunk(ChunkPos chunkPos); ChunkPos chunkPosAt(int posX, int posY, uint64_t instanceID); std::set getViewableChunks(ChunkPos chunkPos); - std::vector getChunksInMap(uint64_t mapNum); bool inPopulatedChunks(std::set* chnks); void createInstance(uint64_t); - void destroyInstance(uint64_t); void destroyInstanceIfEmpty(uint64_t); } diff --git a/src/Combat.cpp b/src/Combat.cpp index a222f9c..a11d315 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -11,25 +11,51 @@ #include +using namespace Combat; + /// Player Id -> Bullet Id -> Bullet std::map> Combat::Bullets; -void Combat::init() { - REGISTER_SHARD_TIMER(playerTick, 2000); +static std::pair getDamage(int attackPower, int defensePower, bool shouldCrit, + bool batteryBoost, int attackerStyle, + int defenderStyle, int difficulty) { + std::pair ret = {0, 1}; + if (attackPower + defensePower * 2 == 0) + return ret; - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs); + // base calculation + int damage = attackPower * attackPower / (attackPower + defensePower); + damage = std::max(10 + attackPower / 10, damage - (defensePower - attackPower / 6) * difficulty / 100); + damage = damage * (rand() % 40 + 80) / 100; - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_BEGIN, combatBegin); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_END, combatEnd); - REGISTER_SHARD_PACKET(P_CL2FE_DOT_DAMAGE_ONOFF, dotDamageOnOff); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_CHARs, pcAttackChars); + // Adaptium/Blastons/Cosmix + if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) { + if (attackerStyle - defenderStyle == 2) + defenderStyle += 3; + if (defenderStyle - attackerStyle == 2) + defenderStyle -= 3; + if (attackerStyle < defenderStyle) + damage = damage * 5 / 4; + else + damage = damage * 4 / 5; + } - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GRENADE_STYLE_FIRE, grenadeFire); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ROCKET_STYLE_FIRE, rocketFire); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ROCKET_STYLE_HIT, projectileHit); + // weapon boosts + if (batteryBoost) + damage = damage * 5 / 4; + + ret.first = damage; + ret.second = 1; + + if (shouldCrit && rand() % 20 == 0) { + ret.first *= 2; // critical hit + ret.second = 2; + } + + return ret; } -void Combat::pcAttackNpcs(CNSocket *sock, CNPacketData *data) { +static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) { sP_CL2FE_REQ_PC_ATTACK_NPCs* pkt = (sP_CL2FE_REQ_PC_ATTACK_NPCs*)data->buf; Player *plr = PlayerManager::getPlayer(sock); @@ -271,7 +297,7 @@ void Combat::killMob(CNSocket *sock, Mob *mob) { } } -void Combat::combatBegin(CNSocket *sock, CNPacketData *data) { +static void combatBegin(CNSocket *sock, CNPacketData *data) { Player *plr = PlayerManager::getPlayer(sock); plr->inCombat = true; @@ -286,7 +312,7 @@ void Combat::combatBegin(CNSocket *sock, CNPacketData *data) { PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE)); } -void Combat::combatEnd(CNSocket *sock, CNPacketData *data) { +static void combatEnd(CNSocket *sock, CNPacketData *data) { Player *plr = PlayerManager::getPlayer(sock); if (plr != nullptr) { @@ -295,7 +321,7 @@ void Combat::combatEnd(CNSocket *sock, CNPacketData *data) { } } -void Combat::dotDamageOnOff(CNSocket *sock, CNPacketData *data) { +static void dotDamageOnOff(CNSocket *sock, CNPacketData *data) { sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf; Player *plr = PlayerManager::getPlayer(sock); @@ -312,7 +338,7 @@ void Combat::dotDamageOnOff(CNSocket *sock, CNPacketData *data) { sock->sendPacket((void*)&pkt1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); } -void Combat::dealGooDamage(CNSocket *sock, int amount) { +static void dealGooDamage(CNSocket *sock, int amount) { size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage); assert(resplen < CN_PACKET_BUFFER_SIZE - 8); uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; @@ -358,46 +384,7 @@ void Combat::dealGooDamage(CNSocket *sock, int amount) { PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); } -std::pair Combat::getDamage(int attackPower, int defensePower, bool shouldCrit, - bool batteryBoost, int attackerStyle, - int defenderStyle, int difficulty) { - std::pair ret = {0, 1}; - if (attackPower + defensePower * 2 == 0) - return ret; - - // base calculation - int damage = attackPower * attackPower / (attackPower + defensePower); - damage = std::max(10 + attackPower / 10, damage - (defensePower - attackPower / 6) * difficulty / 100); - damage = damage * (rand() % 40 + 80) / 100; - - // Adaptium/Blastons/Cosmix - if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) { - if (attackerStyle - defenderStyle == 2) - defenderStyle += 3; - if (defenderStyle - attackerStyle == 2) - defenderStyle -= 3; - if (attackerStyle < defenderStyle) - damage = damage * 5 / 4; - else - damage = damage * 4 / 5; - } - - // weapon boosts - if (batteryBoost) - damage = damage * 5 / 4; - - ret.first = damage; - ret.second = 1; - - if (shouldCrit && rand() % 20 == 0) { - ret.first *= 2; // critical hit - ret.second = 2; - } - - return ret; -} - -void Combat::pcAttackChars(CNSocket *sock, CNPacketData *data) { +static void pcAttackChars(CNSocket *sock, CNPacketData *data) { sP_CL2FE_REQ_PC_ATTACK_CHARs* pkt = (sP_CL2FE_REQ_PC_ATTACK_CHARs*)data->buf; Player *plr = PlayerManager::getPlayer(sock); @@ -514,65 +501,7 @@ void Combat::pcAttackChars(CNSocket *sock, CNPacketData *data) { PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_CHARs, resplen); } -void Combat::grenadeFire(CNSocket* sock, CNPacketData* data) { - sP_CL2FE_REQ_PC_GRENADE_STYLE_FIRE* grenade = (sP_CL2FE_REQ_PC_GRENADE_STYLE_FIRE*)data->buf; - Player* plr = PlayerManager::getPlayer(sock); - - INITSTRUCT(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, resp); - resp.iToX = grenade->iToX; - resp.iToY = grenade->iToY; - resp.iToZ = grenade->iToZ; - - resp.iBulletID = addBullet(plr, true); - resp.iBatteryW = plr->batteryW; - - // 1 means grenade - resp.Bullet.iID = 1; - sock->sendPacket(&resp, P_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, sizeof(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC)); - - // send packet to nearby players - INITSTRUCT(sP_FE2CL_PC_GRENADE_STYLE_FIRE, toOthers); - toOthers.iPC_ID = plr->iID; - toOthers.iToX = resp.iToX; - toOthers.iToY = resp.iToY; - toOthers.iToZ = resp.iToZ; - toOthers.iBulletID = resp.iBulletID; - toOthers.Bullet.iID = resp.Bullet.iID; - - PlayerManager::sendToViewable(sock, &toOthers, P_FE2CL_PC_GRENADE_STYLE_FIRE, sizeof(sP_FE2CL_PC_GRENADE_STYLE_FIRE)); -} - -void Combat::rocketFire(CNSocket* sock, CNPacketData* data) { - sP_CL2FE_REQ_PC_ROCKET_STYLE_FIRE* rocket = (sP_CL2FE_REQ_PC_ROCKET_STYLE_FIRE*)data->buf; - Player* plr = PlayerManager::getPlayer(sock); - - // We should be sending back rocket succ packet, but it doesn't work, and this one works - INITSTRUCT(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, resp); - resp.iToX = rocket->iToX; - resp.iToY = rocket->iToY; - // rocket->iToZ is broken, this seems like a good height - resp.iToZ = plr->z + 100; - - resp.iBulletID = addBullet(plr, false); - // we have to send it weapon id - resp.Bullet.iID = plr->Equip[0].iID; - resp.iBatteryW = plr->batteryW; - - sock->sendPacket(&resp, P_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, sizeof(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC)); - - // send packet to nearby players - INITSTRUCT(sP_FE2CL_PC_GRENADE_STYLE_FIRE, toOthers); - toOthers.iPC_ID = plr->iID; - toOthers.iToX = resp.iToX; - toOthers.iToY = resp.iToY; - toOthers.iToZ = resp.iToZ; - toOthers.iBulletID = resp.iBulletID; - toOthers.Bullet.iID = resp.Bullet.iID; - - PlayerManager::sendToViewable(sock, &toOthers, P_FE2CL_PC_GRENADE_STYLE_FIRE, sizeof(sP_FE2CL_PC_GRENADE_STYLE_FIRE)); -} - -int8_t Combat::addBullet(Player* plr, bool isGrenade) { +static int8_t addBullet(Player* plr, bool isGrenade) { int8_t findId = 0; if (Bullets.find(plr->iID) != Bullets.end()) { @@ -605,7 +534,65 @@ int8_t Combat::addBullet(Player* plr, bool isGrenade) { return findId; } -void Combat::projectileHit(CNSocket* sock, CNPacketData* data) { +static void grenadeFire(CNSocket* sock, CNPacketData* data) { + sP_CL2FE_REQ_PC_GRENADE_STYLE_FIRE* grenade = (sP_CL2FE_REQ_PC_GRENADE_STYLE_FIRE*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + INITSTRUCT(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, resp); + resp.iToX = grenade->iToX; + resp.iToY = grenade->iToY; + resp.iToZ = grenade->iToZ; + + resp.iBulletID = addBullet(plr, true); + resp.iBatteryW = plr->batteryW; + + // 1 means grenade + resp.Bullet.iID = 1; + sock->sendPacket(&resp, P_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, sizeof(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC)); + + // send packet to nearby players + INITSTRUCT(sP_FE2CL_PC_GRENADE_STYLE_FIRE, toOthers); + toOthers.iPC_ID = plr->iID; + toOthers.iToX = resp.iToX; + toOthers.iToY = resp.iToY; + toOthers.iToZ = resp.iToZ; + toOthers.iBulletID = resp.iBulletID; + toOthers.Bullet.iID = resp.Bullet.iID; + + PlayerManager::sendToViewable(sock, &toOthers, P_FE2CL_PC_GRENADE_STYLE_FIRE, sizeof(sP_FE2CL_PC_GRENADE_STYLE_FIRE)); +} + +static void rocketFire(CNSocket* sock, CNPacketData* data) { + sP_CL2FE_REQ_PC_ROCKET_STYLE_FIRE* rocket = (sP_CL2FE_REQ_PC_ROCKET_STYLE_FIRE*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + // We should be sending back rocket succ packet, but it doesn't work, and this one works + INITSTRUCT(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, resp); + resp.iToX = rocket->iToX; + resp.iToY = rocket->iToY; + // rocket->iToZ is broken, this seems like a good height + resp.iToZ = plr->z + 100; + + resp.iBulletID = addBullet(plr, false); + // we have to send it weapon id + resp.Bullet.iID = plr->Equip[0].iID; + resp.iBatteryW = plr->batteryW; + + sock->sendPacket(&resp, P_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC, sizeof(sP_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC)); + + // send packet to nearby players + INITSTRUCT(sP_FE2CL_PC_GRENADE_STYLE_FIRE, toOthers); + toOthers.iPC_ID = plr->iID; + toOthers.iToX = resp.iToX; + toOthers.iToY = resp.iToY; + toOthers.iToZ = resp.iToZ; + toOthers.iBulletID = resp.iBulletID; + toOthers.Bullet.iID = resp.Bullet.iID; + + PlayerManager::sendToViewable(sock, &toOthers, P_FE2CL_PC_GRENADE_STYLE_FIRE, sizeof(sP_FE2CL_PC_GRENADE_STYLE_FIRE)); +} + +static void projectileHit(CNSocket* sock, CNPacketData* data) { sP_CL2FE_REQ_PC_ROCKET_STYLE_HIT* pkt = (sP_CL2FE_REQ_PC_ROCKET_STYLE_HIT*)data->buf; Player* plr = PlayerManager::getPlayer(sock); @@ -699,7 +686,7 @@ void Combat::projectileHit(CNSocket* sock, CNPacketData* data) { Bullets[plr->iID].erase(resp->iBulletID); } -void Combat::playerTick(CNServer *serv, time_t currTime) { +static void playerTick(CNServer *serv, time_t currTime) { static time_t lastHealTime = 0; for (auto& pair : PlayerManager::players) { @@ -780,3 +767,18 @@ void Combat::playerTick(CNServer *serv, time_t currTime) { if (currTime - lastHealTime >= 4000) lastHealTime = currTime; } + +void Combat::init() { + REGISTER_SHARD_TIMER(playerTick, 2000); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_BEGIN, combatBegin); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_END, combatEnd); + REGISTER_SHARD_PACKET(P_CL2FE_DOT_DAMAGE_ONOFF, dotDamageOnOff); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_CHARs, pcAttackChars); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GRENADE_STYLE_FIRE, grenadeFire); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ROCKET_STYLE_FIRE, rocketFire); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ROCKET_STYLE_HIT, projectileHit); +} diff --git a/src/Combat.hpp b/src/Combat.hpp index 908e10f..ef4d0ec 100644 --- a/src/Combat.hpp +++ b/src/Combat.hpp @@ -23,25 +23,8 @@ namespace Combat { extern std::map> Bullets; void init(); - void playerTick(CNServer*, time_t); - - void pcAttackNpcs(CNSocket *sock, CNPacketData *data); - void combatBegin(CNSocket *sock, CNPacketData *data); - void combatEnd(CNSocket *sock, CNPacketData *data); - void dotDamageOnOff(CNSocket *sock, CNPacketData *data); - void dealGooDamage(CNSocket *sock, int amount); void npcAttackPc(Mob *mob, time_t currTime); int hitMob(CNSocket *sock, Mob *mob, int damage); void killMob(CNSocket *sock, Mob *mob); - - std::pair lerp(int, int, int, int, int); - std::pair getDamage(int, int, bool, bool, int, int, int); - - void pcAttackChars(CNSocket *sock, CNPacketData *data); - void grenadeFire(CNSocket* sock, CNPacketData* data); - void rocketFire(CNSocket* sock, CNPacketData* data); - void projectileHit(CNSocket* sock, CNPacketData* data); - /// returns bullet id - int8_t addBullet(Player* plr, bool isGrenade); } diff --git a/src/Email.cpp b/src/Email.cpp index 00230a1..a4c3bcb 100644 --- a/src/Email.cpp +++ b/src/Email.cpp @@ -1,7 +1,18 @@ #include "Email.hpp" +#include "CNProtocol.hpp" +#include "CNStructs.hpp" +#include "CNShardServer.hpp" + +#include "db/Database.hpp" +#include "PlayerManager.hpp" +#include "ItemManager.hpp" +#include "ChatManager.hpp" + +using namespace Email; + // New email notification -void Email::emailUpdateCheck(CNSocket* sock, CNPacketData* data) { +static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK)) return; // malformed packet @@ -11,7 +22,7 @@ void Email::emailUpdateCheck(CNSocket* sock, CNPacketData* data) { } // Retrieve page of emails -void Email::emailReceivePageList(CNSocket* sock, CNPacketData* data) { +static void emailReceivePageList(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST)) return; // malformed packet @@ -41,7 +52,7 @@ void Email::emailReceivePageList(CNSocket* sock, CNPacketData* data) { } // Read individual email -void Email::emailRead(CNSocket* sock, CNPacketData* data) { +static void emailRead(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_READ_EMAIL)) return; // malformed packet @@ -66,7 +77,7 @@ void Email::emailRead(CNSocket* sock, CNPacketData* data) { } // Retrieve attached taros from email -void Email::emailReceiveTaros(CNSocket* sock, CNPacketData* data) { +static void emailReceiveTaros(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY)) return; // malformed packet @@ -89,7 +100,7 @@ void Email::emailReceiveTaros(CNSocket* sock, CNPacketData* data) { } // Retrieve individual attached item from email -void Email::emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) { +static void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM)) return; // malformed packet @@ -128,7 +139,7 @@ void Email::emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) { } // Retrieve all attached items from email -void Email::emailReceiveItemAll(CNSocket* sock, CNPacketData* data) { +static void emailReceiveItemAll(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL)) return; // malformed packet @@ -173,7 +184,7 @@ void Email::emailReceiveItemAll(CNSocket* sock, CNPacketData* data) { } // Delete an email -void Email::emailDelete(CNSocket* sock, CNPacketData* data) { +static void emailDelete(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_DELETE_EMAIL)) return; // malformed packet @@ -190,7 +201,7 @@ void Email::emailDelete(CNSocket* sock, CNPacketData* data) { } // Send an email -void Email::emailSend(CNSocket* sock, CNPacketData* data) { +static void emailSend(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_SEND_EMAIL)) return; // malformed packet diff --git a/src/Email.hpp b/src/Email.hpp index 6ee280c..7365b12 100644 --- a/src/Email.hpp +++ b/src/Email.hpp @@ -1,23 +1,5 @@ #pragma once -#include "CNProtocol.hpp" -#include "CNStructs.hpp" -#include "CNShardServer.hpp" - -#include "db/Database.hpp" -#include "PlayerManager.hpp" -#include "ItemManager.hpp" -#include "ChatManager.hpp" - namespace Email { void init(); - - void emailUpdateCheck(CNSocket* sock, CNPacketData* data); - void emailReceivePageList(CNSocket* sock, CNPacketData* data); - void emailRead(CNSocket* sock, CNPacketData* data); - void emailReceiveTaros(CNSocket* sock, CNPacketData* data); - void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data); - void emailReceiveItemAll(CNSocket* sock, CNPacketData* data); - void emailDelete(CNSocket* sock, CNPacketData* data); - void emailSend(CNSocket* sock, CNPacketData* data); } diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index 41d6849..361f088 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -10,14 +10,9 @@ #include #include -void GroupManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE, requestGroup); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE, refuseGroup); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_JOIN, joinGroup); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_LEAVE, leaveGroup); -} +using namespace GroupManager; -void GroupManager::requestGroup(CNSocket* sock, CNPacketData* data) { +static void requestGroup(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_INVITE)) return; // malformed packet @@ -53,7 +48,7 @@ void GroupManager::requestGroup(CNSocket* sock, CNPacketData* data) { otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE, sizeof(sP_FE2CL_PC_GROUP_INVITE)); } -void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) { +static void refuseGroup(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE)) return; // malformed packet @@ -73,7 +68,7 @@ void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) { otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE_REFUSE, sizeof(sP_FE2CL_PC_GROUP_INVITE_REFUSE)); } -void GroupManager::joinGroup(CNSocket* sock, CNPacketData* data) { +static void joinGroup(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_JOIN)) return; // malformed packet @@ -152,7 +147,7 @@ void GroupManager::joinGroup(CNSocket* sock, CNPacketData* data) { sendToGroup(otherPlr, (void*)&respbuf, P_FE2CL_PC_GROUP_JOIN, resplen); } -void GroupManager::leaveGroup(CNSocket* sock, CNPacketData* data) { +static void leaveGroup(CNSocket* sock, CNPacketData* data) { Player* plr = PlayerManager::getPlayer(sock); groupKickPlayer(plr); } @@ -219,6 +214,20 @@ void GroupManager::groupTickInfo(Player* plr) { sendToGroup(plr, (void*)&respbuf, P_FE2CL_PC_GROUP_MEMBER_INFO, resplen); } +static void groupUnbuff(Player* plr) { + for (int i = 0; i < plr->groupCnt; i++) { + for (int n = 0; n < plr->groupCnt; n++) { + if (i == n) + continue; + + Player* otherPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); + CNSocket* sock = PlayerManager::getSockFromID(plr->groupIDs[n]); + + NanoManager::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0); + } + } +} + void GroupManager::groupKickPlayer(Player* plr) { // if you are the group leader, destroy your own group and kick everybody if (plr->iID == plr->iIDGroup) { @@ -304,20 +313,6 @@ void GroupManager::groupKickPlayer(Player* plr) { sock->sendPacket((void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC)); } -void GroupManager::groupUnbuff(Player* plr) { - for (int i = 0; i < plr->groupCnt; i++) { - for (int n = 0; n < plr->groupCnt; n++) { - if (i == n) - continue; - - Player* otherPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); - CNSocket* sock = PlayerManager::getSockFromID(plr->groupIDs[n]); - - NanoManager::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0); - } - } -} - int GroupManager::getGroupFlags(Player* plr) { int bitFlag = 0; @@ -332,3 +327,10 @@ int GroupManager::getGroupFlags(Player* plr) { return bitFlag; } + +void GroupManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE, requestGroup); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE, refuseGroup); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_JOIN, joinGroup); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_LEAVE, leaveGroup); +} diff --git a/src/GroupManager.hpp b/src/GroupManager.hpp index 4197d2d..c91c06a 100644 --- a/src/GroupManager.hpp +++ b/src/GroupManager.hpp @@ -11,13 +11,8 @@ namespace GroupManager { void init(); - void requestGroup(CNSocket* sock, CNPacketData* data); - void refuseGroup(CNSocket* sock, CNPacketData* data); - void joinGroup(CNSocket* sock, CNPacketData* data); - void leaveGroup(CNSocket* sock, CNPacketData* data); void sendToGroup(Player* plr, void* buf, uint32_t type, size_t size); void groupTickInfo(Player* plr); void groupKickPlayer(Player* plr); - void groupUnbuff(Player* plr); int getGroupFlags(Player* plr); } diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index bef57a2..4455e4a 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -11,6 +11,8 @@ #include // for memset() #include +using namespace ItemManager; + std::map, ItemManager::Item> ItemManager::ItemData; std::map ItemManager::CrocPotTable; std::map> ItemManager::RarityRatios; @@ -25,9 +27,9 @@ std::map ItemManager::MobDrops; #ifdef ACADEMY std::map ItemManager::NanoCapsules; // crate id -> nano id -void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) { +static void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) { Player* plr = PlayerManager::getPlayer(sock); - int32_t nanoId = ItemManager::NanoCapsules[chest->iID]; + int32_t nanoId = NanoCapsules[chest->iID]; // chest opening acknowledgement packet INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); @@ -79,7 +81,93 @@ void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) { } #endif -void itemMoveHandler(CNSocket* sock, CNPacketData* data) { +static int getRarity(Crate& crate, int itemSetId) { + // find rarity ratio + if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) { + std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl; + return -1; + } + + std::vector rarityRatio = RarityRatios[crate.rarityRatioId]; + + /* + * First we have to check if specified item set contains items with all specified rarities, + * and if not eliminate them from the draw + * it is simpler to do here than to fix individually in the file + */ + + // remember that rarities start from 1! + for (int i = 0; i < rarityRatio.size(); i++){ + if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end()) + rarityRatio[i] = 0; + } + + int total = 0; + for (int value : rarityRatio) + total += value; + + if (total == 0) { + std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl; + return -1; + } + + // now return a random rarity number + int randomNum = rand() % total; + int rarity = 0; + int sum = 0; + do { + sum += rarityRatio[rarity]; + rarity++; + } while (sum <= randomNum); + + return rarity; +} + +static int getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) { + auto key = std::make_pair(itemSetId, rarity); + + if (CrateItems.find(key) == CrateItems.end()) { + std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl; + return -1; + } + + // only take into account items that have correct gender + std::vector, Item>::iterator> items; + for (auto crateitem : CrateItems[key]) { + int gender = crateitem->second.gender; + // if gender is incorrect, exclude item + if (gender != 0 && gender != playerGender) + continue; + items.push_back(crateitem); + } + + if (items.size() == 0) { + std::cout << "[WARN] Set ID " << itemSetId << " Rarity " << rarity << " contains no valid items" << std::endl; + return -1; + } + + auto item = items[rand() % items.size()]; + + result.iID = item->first.first; + result.iType = item->first.second; + result.iOpt = 1; + + return 0; +} + +static int getItemSetId(Crate& crate, int crateId) { + int itemSetsCount = crate.itemSets.size(); + if (itemSetsCount == 0) { + std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl; + return -1; + } + + // if crate points to multiple itemSets, choose a random one + int itemSetIndex = rand() % itemSetsCount; + return crate.itemSets[itemSetIndex]; +} + +static void itemMoveHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_MOVE)) return; // ignore the malformed packet @@ -100,20 +188,20 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { // get the fromItem sItemBase *fromItem; - switch ((ItemManager::SlotType)itemmove->eFrom) { - case ItemManager::SlotType::EQUIP: + switch ((SlotType)itemmove->eFrom) { + case SlotType::EQUIP: if (itemmove->iFromSlotNum >= AEQUIP_COUNT) return; fromItem = &plr->Equip[itemmove->iFromSlotNum]; break; - case ItemManager::SlotType::INVENTORY: + case SlotType::INVENTORY: if (itemmove->iFromSlotNum >= AINVEN_COUNT) return; fromItem = &plr->Inven[itemmove->iFromSlotNum]; break; - case ItemManager::SlotType::BANK: + case SlotType::BANK: if (itemmove->iFromSlotNum >= ABANK_COUNT) return; @@ -126,20 +214,20 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { // get the toItem sItemBase* toItem; - switch ((ItemManager::SlotType)itemmove->eTo) { - case ItemManager::SlotType::EQUIP: + switch ((SlotType)itemmove->eTo) { + case SlotType::EQUIP: if (itemmove->iToSlotNum >= AEQUIP_COUNT) return; toItem = &plr->Equip[itemmove->iToSlotNum]; break; - case ItemManager::SlotType::INVENTORY: + case SlotType::INVENTORY: if (itemmove->iToSlotNum >= AINVEN_COUNT) return; toItem = &plr->Inven[itemmove->iToSlotNum]; break; - case ItemManager::SlotType::BANK: + case SlotType::BANK: if (itemmove->iToSlotNum >= ABANK_COUNT) return; @@ -151,7 +239,7 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { } // if equipping an item, validate that it's of the correct type for the slot - if ((ItemManager::SlotType)itemmove->eTo == ItemManager::SlotType::EQUIP) { + if ((SlotType)itemmove->eTo == SlotType::EQUIP) { if (fromItem->iType == 10 && itemmove->iToSlotNum != 8) return; // vehicle in wrong slot else if (fromItem->iType != 10 @@ -169,8 +257,8 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { resp.FromSlotItem = *fromItem; // swap/stack items in session - ItemManager::Item* itemDat = ItemManager::getItemData(toItem->iID, toItem->iType); - ItemManager::Item* itemDatFrom = ItemManager::getItemData(fromItem->iID, fromItem->iType); + Item* itemDat = getItemData(toItem->iID, toItem->iType); + Item* itemDatFrom = getItemData(fromItem->iID, fromItem->iType); if (itemDat != nullptr && itemDatFrom != nullptr && itemDat->stackSize > 1 && itemDat == itemDatFrom && fromItem->iOpt < itemDat->stackSize && toItem->iOpt < itemDat->stackSize) { // items are stackable, identical, and not maxed, so run stacking logic @@ -203,11 +291,11 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { } // send equip change to viewable players - if (itemmove->eFrom == (int)ItemManager::SlotType::EQUIP || itemmove->eTo == (int)ItemManager::SlotType::EQUIP) { + if (itemmove->eFrom == (int)SlotType::EQUIP || itemmove->eTo == (int)SlotType::EQUIP) { INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, equipChange); equipChange.iPC_ID = plr->iID; - if (itemmove->eTo == (int)ItemManager::SlotType::EQUIP) { + if (itemmove->eTo == (int)SlotType::EQUIP) { equipChange.iEquipSlotNum = itemmove->iToSlotNum; equipChange.EquipSlotItem = resp.FromSlotItem; } else { @@ -223,14 +311,14 @@ void itemMoveHandler(CNSocket* sock, CNPacketData* data) { PlayerManager::sendToViewable(sock, (void*)&equipChange, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE)); // set equipment stats serverside - ItemManager::setItemStats(plr); + setItemStats(plr); } // send response sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC)); } -void itemDeleteHandler(CNSocket* sock, CNPacketData* data) { +static void itemDeleteHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_DELETE)) return; // ignore the malformed packet @@ -250,7 +338,7 @@ void itemDeleteHandler(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_ITEM_DELETE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC)); } -void itemUseHandler(CNSocket* sock, CNPacketData* data) { +static void itemUseHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_USE)) return; // ignore the malformed packet sP_CL2FE_REQ_ITEM_USE* request = (sP_CL2FE_REQ_ITEM_USE*)data->buf; @@ -333,7 +421,7 @@ void itemUseHandler(CNSocket* sock, CNPacketData* data) { NPCManager::EggBuffs[key] = until; } -void itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { +static void itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_BANK_OPEN)) return; // ignore the malformed packet @@ -348,7 +436,7 @@ void itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BANK_OPEN_SUCC, sizeof(sP_FE2CL_REP_PC_BANK_OPEN_SUCC)); } -void chestOpenHandler(CNSocket *sock, CNPacketData *data) { +static void chestOpenHandler(CNSocket *sock, CNPacketData *data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN)) return; // ignore the malformed packet @@ -370,7 +458,7 @@ void chestOpenHandler(CNSocket *sock, CNPacketData *data) { #ifdef ACADEMY // check if chest isn't a nano capsule - if (ItemManager::NanoCapsules.find(chest->iID) != ItemManager::NanoCapsules.end()) + if (NanoCapsules.find(chest->iID) != NanoCapsules.end()) return nanoCapsuleHandler(sock, pkt->iSlotNum, chest); #endif @@ -406,24 +494,24 @@ void chestOpenHandler(CNSocket *sock, CNPacketData *data) { bool failing = false; // find the crate - if (ItemManager::Crates.find(chest->iID) == ItemManager::Crates.end()) { + if (Crates.find(chest->iID) == Crates.end()) { std::cout << "[WARN] Crate " << chest->iID << " not found!" << std::endl; failing = true; } - Crate& crate = ItemManager::Crates[chest->iID]; + Crate& crate = Crates[chest->iID]; if (!failing) - itemSetId = ItemManager::getItemSetId(crate, chest->iID); + itemSetId = getItemSetId(crate, chest->iID); if (itemSetId == -1) failing = true; if (!failing) - rarity = ItemManager::getRarity(crate, itemSetId); + rarity = getRarity(crate, itemSetId); if (rarity == -1) failing = true; if (!failing) - ret = ItemManager::getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); + ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); if (ret == -1) failing = true; @@ -444,92 +532,6 @@ void chestOpenHandler(CNSocket *sock, CNPacketData *data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC)); } -int ItemManager::getItemSetId(Crate& crate, int crateId) { - int itemSetsCount = crate.itemSets.size(); - if (itemSetsCount == 0) { - std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl; - return -1; - } - - // if crate points to multiple itemSets, choose a random one - int itemSetIndex = rand() % itemSetsCount; - return crate.itemSets[itemSetIndex]; -} - -int ItemManager::getRarity(Crate& crate, int itemSetId) { - // find rarity ratio - if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) { - std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl; - return -1; - } - - std::vector rarityRatio = RarityRatios[crate.rarityRatioId]; - - /* - * First we have to check if specified item set contains items with all specified rarities, - * and if not eliminate them from the draw - * it is simpler to do here than to fix individually in the file - */ - - // remember that rarities start from 1! - for (int i = 0; i < rarityRatio.size(); i++){ - if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end()) - rarityRatio[i] = 0; - } - - int total = 0; - for (int value : rarityRatio) - total += value; - - if (total == 0) { - std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl; - return -1; - } - - // now return a random rarity number - int randomNum = rand() % total; - int rarity = 0; - int sum = 0; - do { - sum += rarityRatio[rarity]; - rarity++; - } while (sum <= randomNum); - - return rarity; -} - -int ItemManager::getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) { - auto key = std::make_pair(itemSetId, rarity); - - if (CrateItems.find(key) == CrateItems.end()) { - std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl; - return -1; - } - - // only take into account items that have correct gender - std::vector, Item>::iterator> items; - for (auto crateitem : CrateItems[key]) { - int gender = crateitem->second.gender; - // if gender is incorrect, exclude item - if (gender != 0 && gender != playerGender) - continue; - items.push_back(crateitem); - } - - if (items.size() == 0) { - std::cout << "[WARN] Set ID " << itemSetId << " Rarity " << rarity << " contains no valid items" << std::endl; - return -1; - } - - auto item = items[rand() % items.size()]; - - result.iID = item->first.first; - result.iType = item->first.second; - result.iOpt = 1; - - return 0; -} - // TODO: use this in cleaned up ItemManager int ItemManager::findFreeSlot(Player *plr) { int i; @@ -542,7 +544,7 @@ int ItemManager::findFreeSlot(Player *plr) { return -1; } -ItemManager::Item* ItemManager::getItemData(int32_t id, int32_t type) { +Item* ItemManager::getItemData(int32_t id, int32_t type) { if(ItemData.find(std::make_pair(id, type)) != ItemData.end()) return &ItemData[std::make_pair(id, type)]; return nullptr; @@ -593,7 +595,7 @@ void ItemManager::setItemStats(Player* plr) { Item* itemStatsDat; for (int i = 0; i < 4; i++) { - itemStatsDat = ItemManager::getItemData(plr->Equip[i].iID, plr->Equip[i].iType); + itemStatsDat = getItemData(plr->Equip[i].iID, plr->Equip[i].iType); if (itemStatsDat == nullptr) { std::cout << "[WARN] setItemStats(): getItemData() returned NULL" << std::endl; continue; @@ -606,6 +608,7 @@ void ItemManager::setItemStats(Player* plr) { } // HACK: work around the invisible weapon bug +// TODO: I don't think this makes a difference at all? Check and remove, if necessary. void ItemManager::updateEquips(CNSocket* sock, Player* plr) { for (int i = 0; i < 4; i++) { INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, resp); @@ -618,6 +621,81 @@ void ItemManager::updateEquips(CNSocket* sock, Player* plr) { } } +static void getMobDrop(sItemBase *reward, MobDrop* drop, MobDropChance* chance, int rolled) { + reward->iType = 9; + reward->iOpt = 1; + + int total = 0; + for (int ratio : chance->cratesRatio) + total += ratio; + + // randomizing a crate + int randomNum = rolled % total; + int i = 0; + int sum = 0; + do { + reward->iID = drop->crateIDs[i]; + sum += chance->cratesRatio[i]; + i++; + } + while (sum<=randomNum); +} + +static void giveEventDrop(CNSocket* sock, Player* player, int rolled) { + // random drop chance + if (rand() % 100 > settings::EVENTCRATECHANCE) + return; + + // no slot = no reward + int slot = findFreeSlot(player); + if (slot == -1) + return; + + const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + + uint8_t respbuf[resplen]; + sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; + sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); + + // don't forget to zero the buffer! + memset(respbuf, 0, resplen); + + // leave everything here as it is + reward->m_iCandy = player->money; + reward->m_iFusionMatter = player->fusionmatter; + reward->m_iBatteryN = player->batteryN; + reward->m_iBatteryW = player->batteryW; + reward->iFatigue = 100; // prevents warning message + reward->iFatigue_Level = 1; + reward->iItemCnt = 1; // remember to update resplen if you change this + + // which crate to drop + int crateId; + switch (settings::EVENTMODE) { + // knishmas + case 1: crateId = 1187; break; + // halloween + case 2: crateId = 1181; break; + // spring + case 3: crateId = 1126; break; + // what + default: + std::cout << "[WARN] Unknown event Id " << settings::EVENTMODE << std::endl; + return; + } + + item->sItem.iType = 9; + item->sItem.iID = crateId; + item->sItem.iOpt = 1; + item->iSlotNum = slot; + item->eIL = 1; // Inventory Location. 1 means player inventory. + + // update player + player->Inven[slot] = item->sItem; + sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); +} + void ItemManager::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int rolledPotions, int rolledCrate, int rolledCrateType, int rolledEvent) { Player *plr = PlayerManager::getPlayer(sock); @@ -687,7 +765,7 @@ void ItemManager::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int ro reward->iFatigue_Level = 1; reward->iItemCnt = 1; // remember to update resplen if you change this - int slot = ItemManager::findFreeSlot(plr); + int slot = findFreeSlot(plr); bool awardDrop = false; MobDropChance *chance = nullptr; @@ -722,81 +800,6 @@ void ItemManager::giveMobDrop(CNSocket *sock, Mob* mob, int rolledBoosts, int ro giveEventDrop(sock, plr, rolledEvent); } -void ItemManager::getMobDrop(sItemBase *reward, MobDrop* drop, MobDropChance* chance, int rolled) { - reward->iType = 9; - reward->iOpt = 1; - - int total = 0; - for (int ratio : chance->cratesRatio) - total += ratio; - - // randomizing a crate - int randomNum = rolled % total; - int i = 0; - int sum = 0; - do { - reward->iID = drop->crateIDs[i]; - sum += chance->cratesRatio[i]; - i++; - } - while (sum<=randomNum); -} - -void ItemManager::giveEventDrop(CNSocket* sock, Player* player, int rolled) { - // random drop chance - if (rand() % 100 > settings::EVENTCRATECHANCE) - return; - - // no slot = no reward - int slot = ItemManager::findFreeSlot(player); - if (slot == -1) - return; - - const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); - assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - - uint8_t respbuf[resplen]; - sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; - sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); - - // don't forget to zero the buffer! - memset(respbuf, 0, resplen); - - // leave everything here as it is - reward->m_iCandy = player->money; - reward->m_iFusionMatter = player->fusionmatter; - reward->m_iBatteryN = player->batteryN; - reward->m_iBatteryW = player->batteryW; - reward->iFatigue = 100; // prevents warning message - reward->iFatigue_Level = 1; - reward->iItemCnt = 1; // remember to update resplen if you change this - - // which crate to drop - int crateId; - switch (settings::EVENTMODE) { - // knishmas - case 1: crateId = 1187; break; - // halloween - case 2: crateId = 1181; break; - // spring - case 3: crateId = 1126; break; - // what - default: - std::cout << "[WARN] Unknown event Id " << settings::EVENTMODE << std::endl; - return; - } - - item->sItem.iType = 9; - item->sItem.iID = crateId; - item->sItem.iOpt = 1; - item->iSlotNum = slot; - item->eIL = 1; // Inventory Location. 1 means player inventory. - - // update player - player->Inven[slot] = item->sItem; - sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); -} - void ItemManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler); diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index 328af14..eba9e35 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -57,15 +57,8 @@ namespace ItemManager { void init(); - // crate opening logic with all helper functions - int getItemSetId(Crate& crate, int crateId); - int getRarity(Crate& crate, int itemSetId); - int getCrateItem(sItemBase& reward, int itemSetId, int rarity, int playerGender); - // mob drops void giveMobDrop(CNSocket *sock, Mob *mob, int rolledBoosts, int rolledPotions, int rolledCrate, int rolledCrateType, int rolledEvent); - void getMobDrop(sItemBase *reward, MobDrop *drop, MobDropChance *chance, int rolled); - void giveEventDrop(CNSocket* sock, Player* player, int rolled); int findFreeSlot(Player *plr); Item* getItemData(int32_t id, int32_t type); diff --git a/src/MissionManager.cpp b/src/MissionManager.cpp index c90a634..73b0ff6 100644 --- a/src/MissionManager.cpp +++ b/src/MissionManager.cpp @@ -7,292 +7,26 @@ #include "string.h" +using namespace MissionManager; + std::map MissionManager::Rewards; std::map MissionManager::Tasks; nlohmann::json MissionManager::AvatarGrowth[37]; -void MissionManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_START, taskStart); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_END, taskEnd); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID, setMission); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_STOP, quitMission); -} - -bool MissionManager::startTask(Player* plr, int TaskID) { - if (MissionManager::Tasks.find(TaskID) == MissionManager::Tasks.end()) { - std::cout << "[WARN] Player submitted unknown task!?" << std::endl; - return false; - } - - TaskData& task = *MissionManager::Tasks[TaskID]; - - // client freaks out if nano mission isn't sent first after relogging, so it's easiest to set it here - if (task["m_iSTNanoID"] != 0 && plr->tasks[0] != 0) { - // lets move task0 to different spot - int moveToSlot = 1; - for (; moveToSlot < ACTIVE_MISSION_COUNT; moveToSlot++) - if (plr->tasks[moveToSlot] == 0) - break; - - plr->tasks[moveToSlot] = plr->tasks[0]; - plr->tasks[0] = 0; - for (int i = 0; i < 3; i++) { - plr->RemainingNPCCount[moveToSlot][i] = plr->RemainingNPCCount[0][i]; - plr->RemainingNPCCount[0][i] = 0; - } - } - - int i; - for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { - if (plr->tasks[i] == 0) { - plr->tasks[i] = TaskID; - for (int j = 0; j < 3; j++) { - plr->RemainingNPCCount[i][j] = (int)task["m_iCSUNumToKill"][j]; - } - break; - } - } - - if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != TaskID) { - std::cout << "[WARN] Player has more than 6 active missions!?" << std::endl; - return false; - } - - return true; -} - -void MissionManager::taskStart(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_START)) - return; // malformed packet - - sP_CL2FE_REQ_PC_TASK_START* missionData = (sP_CL2FE_REQ_PC_TASK_START*)data->buf; - INITSTRUCT(sP_FE2CL_REP_PC_TASK_START_SUCC, response); - Player *plr = PlayerManager::getPlayer(sock); - - if (!startTask(plr, missionData->iTaskNum)) { - // TODO: TASK_FAIL? - response.iTaskNum = missionData->iTaskNum; - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC)); +static void saveMission(Player* player, int missionId) { + // sanity check missionID so we don't get exceptions + if (missionId < 0 || missionId > 1023) { + std::cout << "[WARN] Client submitted invalid missionId: " <iTaskNum]; - - // Give player their delivery items at the start, or reset them to 0 at the start. - for (int i = 0; i < 3; i++) - if (task["m_iSTItemID"][i] != 0) - dropQuestItem(sock, missionData->iTaskNum, task["m_iSTItemNumNeeded"][i], task["m_iSTItemID"][i], 0); - std::cout << "Mission requested task: " << missionData->iTaskNum << std::endl; - response.iTaskNum = missionData->iTaskNum; - response.iRemainTime = task["m_iSTGrantTimer"]; - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC)); - - // HACK: auto-succeed escort task - if (task["m_iHTaskType"] == 6) { - std::cout << "Skipping escort mission" << std::endl; - INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response); - - endTask(sock, missionData->iTaskNum); - response.iTaskNum = missionData->iTaskNum; - - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC)); - } + // Missions are stored in int64_t array + int row = missionId / 64; + int column = missionId % 64; + player->aQuestFlag[row] |= (1ULL << column); } -void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_END)) - return; // malformed packet - - sP_CL2FE_REQ_PC_TASK_END* missionData = (sP_CL2FE_REQ_PC_TASK_END*)data->buf; - - // failed timed missions give an iNPC_ID of 0 - if (missionData->iNPC_ID == 0) { - TaskData* task = MissionManager::Tasks[missionData->iTaskNum]; - if (task->task["m_iSTGrantTimer"] > 0) { // its a timed mission - Player* plr = PlayerManager::getPlayer(sock); - /* - * Enemy killing missions - * this is gross and should be cleaned up later - * once we comb over mission logic more throughly - */ - bool mobsAreKilled = false; - if (task->task["m_iHTaskType"] == 5) { - mobsAreKilled = true; - for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) { - if (plr->tasks[i] == missionData->iTaskNum) { - for (int j = 0; j < 3; j++) { - if (plr->RemainingNPCCount[i][j] > 0) { - mobsAreKilled = false; - break; - } - } - } - } - } - - if (!mobsAreKilled) { - - int failTaskID = task->task["m_iFOutgoingTask"]; - if (failTaskID != 0) { - MissionManager::quitTask(sock, missionData->iTaskNum, false); - - for (int i = 0; i < 6; i++) - if (plr->tasks[i] == missionData->iTaskNum) - plr->tasks[i] = failTaskID; - return; - } - } - } - } - - INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response); - - response.iTaskNum = missionData->iTaskNum; - - if (!endTask(sock, missionData->iTaskNum, missionData->iBox1Choice)) { - return; - } - - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC)); -} - -bool MissionManager::endTask(CNSocket *sock, int32_t taskNum, int choice) { - Player *plr = PlayerManager::getPlayer(sock); - - if (Tasks.find(taskNum) == Tasks.end()) - return false; - - // ugly pointer/reference juggling for the sake of operator overloading... - TaskData& task = *Tasks[taskNum]; - - // update player - int i; - for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { - if (plr->tasks[i] == taskNum) { - plr->tasks[i] = 0; - for (int j = 0; j < 3; j++) { - plr->RemainingNPCCount[i][j] = 0; - } - } - } - if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) { - std::cout << "[WARN] Player completed non-active mission!?" << std::endl; - return false; - } - - // mission rewards - if (Rewards.find(taskNum) != Rewards.end()) { - if (giveMissionReward(sock, taskNum, choice) == -1) - return false; // we don't want to send anything - } - // don't take away quest items if we haven't finished the quest - - /* - * Give (or take away) quest items - * - * Some mission tasks give the player a quest item upon completion. - * This is distinct from quest item mob drops. - * They can be identified by a counter in the task indicator (ie. 1/1 Gravity Decelerator). - * The server is responsible for dropping the correct item. - * Yes, this is pretty stupid. - * - * iSUInstancename is the number of items to give. It is usually negative at the end of - * a mission, to clean up its quest items. - */ - - for (int i = 0; i < 3; i++) - if (task["m_iSUItem"][i] != 0) - dropQuestItem(sock, taskNum, task["m_iSUInstancename"][i], task["m_iSUItem"][i], 0); - - // if it's the last task - if (task["m_iSUOutgoingTask"] == 0) { - // save completed mission on player - saveMission(plr, (int)(task["m_iHMissionID"])-1); - - // if it's a nano mission, reward the nano. - if (task["m_iSTNanoID"] != 0) - NanoManager::addNano(sock, task["m_iSTNanoID"], 0, true); - - // remove current mission - plr->CurrentMissionID = 0; - } - - return true; -} - -void MissionManager::setMission(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID)) - return; // malformed packet - - Player* plr = PlayerManager::getPlayer(sock); - - sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID* missionData = (sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID*)data->buf; - INITSTRUCT(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, response); - response.iCurrentMissionID = missionData->iCurrentMissionID; - plr->CurrentMissionID = missionData->iCurrentMissionID; - - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, sizeof(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID)); -} - -void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_STOP)) - return; // malformed packet - - sP_CL2FE_REQ_PC_TASK_STOP* missionData = (sP_CL2FE_REQ_PC_TASK_STOP*)data->buf; - quitTask(sock, missionData->iTaskNum, true); -} - -void MissionManager::quitTask(CNSocket* sock, int32_t taskNum, bool manual) { - Player* plr = PlayerManager::getPlayer(sock); - - if (Tasks.find(taskNum) == Tasks.end()) - return; // sanity check - - // update player - int i; - for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { - if (plr->tasks[i] == taskNum) { - plr->tasks[i] = 0; - for (int j = 0; j < 3; j++) { - plr->RemainingNPCCount[i][j] = 0; - } - } - } - if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) { - std::cout << "[WARN] Player quit non-active mission!?" << std::endl; - } - // remove current mission - plr->CurrentMissionID = 0; - - TaskData& task = *Tasks[taskNum]; - - // clean up quest items - if (manual) { - for (i = 0; i < 3; i++) { - if (task["m_iSUItem"][i] == 0 && task["m_iCSUItemID"][i] == 0) - continue; - - /* - * It's ok to do this only server-side, because the server decides which - * slot later items will be placed in. - */ - for (int j = 0; j < AQINVEN_COUNT; j++) - if (plr->QInven[j].iID == task["m_iSUItem"][i] || plr->QInven[j].iID == task["m_iCSUItemID"][i] || plr->QInven[j].iID == task["m_iSTItemID"][i]) - memset(&plr->QInven[j], 0, sizeof(sItemBase)); - } - } else { - INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_FAIL, failResp); - failResp.iErrorCode = 1; - failResp.iTaskNum = taskNum; - sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_TASK_END_FAIL, sizeof(sP_FE2CL_REP_PC_TASK_END_FAIL)); - } - - INITSTRUCT(sP_FE2CL_REP_PC_TASK_STOP_SUCC, response); - response.iTaskNum = taskNum; - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_STOP_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_STOP_SUCC)); -} - -int MissionManager::findQSlot(Player *plr, int id) { +static int findQSlot(Player *plr, int id) { int i; // two passes. we mustn't fail to find an existing stack. @@ -309,7 +43,20 @@ int MissionManager::findQSlot(Player *plr, int id) { return -1; } -void MissionManager::dropQuestItem(CNSocket *sock, int task, int count, int id, int mobid) { +static bool isQuestItemFull(CNSocket* sock, int itemId, int itemCount) { + Player* plr = PlayerManager::getPlayer(sock); + + int slot = findQSlot(plr, itemId); + if (slot == -1) { + // this should never happen + std::cout << "[WARN] Player has no room for quest item!?" << std::endl; + return true; + } + + return (itemCount == plr->QInven[slot].iOpt); +} + +static void dropQuestItem(CNSocket *sock, int task, int count, int id, int mobid) { std::cout << "Altered item id " << id << " by " << count << " for task id " << task << std::endl; const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); assert(resplen < CN_PACKET_BUFFER_SIZE); @@ -364,7 +111,7 @@ void MissionManager::dropQuestItem(CNSocket *sock, int task, int count, int id, sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); } -int MissionManager::giveMissionReward(CNSocket *sock, int task, int choice) { +static int giveMissionReward(CNSocket *sock, int task, int choice=0) { Reward *reward = Rewards[task]; Player *plr = PlayerManager::getPlayer(sock); @@ -457,6 +204,280 @@ int MissionManager::giveMissionReward(CNSocket *sock, int task, int choice) { return 0; } +static bool endTask(CNSocket *sock, int32_t taskNum, int choice=0) { + Player *plr = PlayerManager::getPlayer(sock); + + if (Tasks.find(taskNum) == Tasks.end()) + return false; + + // ugly pointer/reference juggling for the sake of operator overloading... + TaskData& task = *Tasks[taskNum]; + + // update player + int i; + for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { + if (plr->tasks[i] == taskNum) { + plr->tasks[i] = 0; + for (int j = 0; j < 3; j++) { + plr->RemainingNPCCount[i][j] = 0; + } + } + } + if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) { + std::cout << "[WARN] Player completed non-active mission!?" << std::endl; + return false; + } + + // mission rewards + if (Rewards.find(taskNum) != Rewards.end()) { + if (giveMissionReward(sock, taskNum, choice) == -1) + return false; // we don't want to send anything + } + // don't take away quest items if we haven't finished the quest + + /* + * Give (or take away) quest items + * + * Some mission tasks give the player a quest item upon completion. + * This is distinct from quest item mob drops. + * They can be identified by a counter in the task indicator (ie. 1/1 Gravity Decelerator). + * The server is responsible for dropping the correct item. + * Yes, this is pretty stupid. + * + * iSUInstancename is the number of items to give. It is usually negative at the end of + * a mission, to clean up its quest items. + */ + + for (int i = 0; i < 3; i++) + if (task["m_iSUItem"][i] != 0) + dropQuestItem(sock, taskNum, task["m_iSUInstancename"][i], task["m_iSUItem"][i], 0); + + // if it's the last task + if (task["m_iSUOutgoingTask"] == 0) { + // save completed mission on player + saveMission(plr, (int)(task["m_iHMissionID"])-1); + + // if it's a nano mission, reward the nano. + if (task["m_iSTNanoID"] != 0) + NanoManager::addNano(sock, task["m_iSTNanoID"], 0, true); + + // remove current mission + plr->CurrentMissionID = 0; + } + + return true; +} + +bool MissionManager::startTask(Player* plr, int TaskID) { + if (MissionManager::Tasks.find(TaskID) == MissionManager::Tasks.end()) { + std::cout << "[WARN] Player submitted unknown task!?" << std::endl; + return false; + } + + TaskData& task = *MissionManager::Tasks[TaskID]; + + // client freaks out if nano mission isn't sent first after relogging, so it's easiest to set it here + if (task["m_iSTNanoID"] != 0 && plr->tasks[0] != 0) { + // lets move task0 to different spot + int moveToSlot = 1; + for (; moveToSlot < ACTIVE_MISSION_COUNT; moveToSlot++) + if (plr->tasks[moveToSlot] == 0) + break; + + plr->tasks[moveToSlot] = plr->tasks[0]; + plr->tasks[0] = 0; + for (int i = 0; i < 3; i++) { + plr->RemainingNPCCount[moveToSlot][i] = plr->RemainingNPCCount[0][i]; + plr->RemainingNPCCount[0][i] = 0; + } + } + + int i; + for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { + if (plr->tasks[i] == 0) { + plr->tasks[i] = TaskID; + for (int j = 0; j < 3; j++) { + plr->RemainingNPCCount[i][j] = (int)task["m_iCSUNumToKill"][j]; + } + break; + } + } + + if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != TaskID) { + std::cout << "[WARN] Player has more than 6 active missions!?" << std::endl; + return false; + } + + return true; +} + +static void taskStart(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_START)) + return; // malformed packet + + sP_CL2FE_REQ_PC_TASK_START* missionData = (sP_CL2FE_REQ_PC_TASK_START*)data->buf; + INITSTRUCT(sP_FE2CL_REP_PC_TASK_START_SUCC, response); + Player *plr = PlayerManager::getPlayer(sock); + + if (!startTask(plr, missionData->iTaskNum)) { + // TODO: TASK_FAIL? + response.iTaskNum = missionData->iTaskNum; + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC)); + return; + } + + TaskData& task = *Tasks[missionData->iTaskNum]; + + // Give player their delivery items at the start, or reset them to 0 at the start. + for (int i = 0; i < 3; i++) + if (task["m_iSTItemID"][i] != 0) + dropQuestItem(sock, missionData->iTaskNum, task["m_iSTItemNumNeeded"][i], task["m_iSTItemID"][i], 0); + std::cout << "Mission requested task: " << missionData->iTaskNum << std::endl; + response.iTaskNum = missionData->iTaskNum; + response.iRemainTime = task["m_iSTGrantTimer"]; + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC)); + + // HACK: auto-succeed escort task + if (task["m_iHTaskType"] == 6) { + std::cout << "Skipping escort mission" << std::endl; + INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response); + + endTask(sock, missionData->iTaskNum); + response.iTaskNum = missionData->iTaskNum; + + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC)); + } +} + +static void taskEnd(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_END)) + return; // malformed packet + + sP_CL2FE_REQ_PC_TASK_END* missionData = (sP_CL2FE_REQ_PC_TASK_END*)data->buf; + + // failed timed missions give an iNPC_ID of 0 + if (missionData->iNPC_ID == 0) { + TaskData* task = MissionManager::Tasks[missionData->iTaskNum]; + if (task->task["m_iSTGrantTimer"] > 0) { // its a timed mission + Player* plr = PlayerManager::getPlayer(sock); + /* + * Enemy killing missions + * this is gross and should be cleaned up later + * once we comb over mission logic more throughly + */ + bool mobsAreKilled = false; + if (task->task["m_iHTaskType"] == 5) { + mobsAreKilled = true; + for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) { + if (plr->tasks[i] == missionData->iTaskNum) { + for (int j = 0; j < 3; j++) { + if (plr->RemainingNPCCount[i][j] > 0) { + mobsAreKilled = false; + break; + } + } + } + } + } + + if (!mobsAreKilled) { + + int failTaskID = task->task["m_iFOutgoingTask"]; + if (failTaskID != 0) { + MissionManager::quitTask(sock, missionData->iTaskNum, false); + + for (int i = 0; i < 6; i++) + if (plr->tasks[i] == missionData->iTaskNum) + plr->tasks[i] = failTaskID; + return; + } + } + } + } + + INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response); + + response.iTaskNum = missionData->iTaskNum; + + if (!endTask(sock, missionData->iTaskNum, missionData->iBox1Choice)) { + return; + } + + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC)); +} + +static void setMission(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID)) + return; // malformed packet + + Player* plr = PlayerManager::getPlayer(sock); + + sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID* missionData = (sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID*)data->buf; + INITSTRUCT(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, response); + response.iCurrentMissionID = missionData->iCurrentMissionID; + plr->CurrentMissionID = missionData->iCurrentMissionID; + + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, sizeof(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID)); +} + +static void quitMission(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_STOP)) + return; // malformed packet + + sP_CL2FE_REQ_PC_TASK_STOP* missionData = (sP_CL2FE_REQ_PC_TASK_STOP*)data->buf; + quitTask(sock, missionData->iTaskNum, true); +} + +void MissionManager::quitTask(CNSocket* sock, int32_t taskNum, bool manual) { + Player* plr = PlayerManager::getPlayer(sock); + + if (Tasks.find(taskNum) == Tasks.end()) + return; // sanity check + + // update player + int i; + for (i = 0; i < ACTIVE_MISSION_COUNT; i++) { + if (plr->tasks[i] == taskNum) { + plr->tasks[i] = 0; + for (int j = 0; j < 3; j++) { + plr->RemainingNPCCount[i][j] = 0; + } + } + } + if (i == ACTIVE_MISSION_COUNT - 1 && plr->tasks[i] != 0) { + std::cout << "[WARN] Player quit non-active mission!?" << std::endl; + } + // remove current mission + plr->CurrentMissionID = 0; + + TaskData& task = *Tasks[taskNum]; + + // clean up quest items + if (manual) { + for (i = 0; i < 3; i++) { + if (task["m_iSUItem"][i] == 0 && task["m_iCSUItemID"][i] == 0) + continue; + + /* + * It's ok to do this only server-side, because the server decides which + * slot later items will be placed in. + */ + for (int j = 0; j < AQINVEN_COUNT; j++) + if (plr->QInven[j].iID == task["m_iSUItem"][i] || plr->QInven[j].iID == task["m_iCSUItemID"][i] || plr->QInven[j].iID == task["m_iSTItemID"][i]) + memset(&plr->QInven[j], 0, sizeof(sItemBase)); + } + } else { + INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_FAIL, failResp); + failResp.iErrorCode = 1; + failResp.iTaskNum = taskNum; + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_TASK_END_FAIL, sizeof(sP_FE2CL_REP_PC_TASK_END_FAIL)); + } + + INITSTRUCT(sP_FE2CL_REP_PC_TASK_STOP_SUCC, response); + response.iTaskNum = taskNum; + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_STOP_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_STOP_SUCC)); +} + void MissionManager::updateFusionMatter(CNSocket* sock, int fusion) { Player *plr = PlayerManager::getPlayer(sock); @@ -566,32 +587,6 @@ void MissionManager::mobKilled(CNSocket *sock, int mobid, int rolledQItem) { } } -void MissionManager::saveMission(Player* player, int missionId) { - // sanity check missionID so we don't get exceptions - if (missionId < 0 || missionId > 1023) { - std::cout << "[WARN] Client submitted invalid missionId: " <aQuestFlag[row] |= (1ULL << column); -} - -bool MissionManager::isQuestItemFull(CNSocket* sock, int itemId, int itemCount) { - Player* plr = PlayerManager::getPlayer(sock); - - int slot = findQSlot(plr, itemId); - if (slot == -1) { - // this should never happen - std::cout << "[WARN] Player has no room for quest item!?" << std::endl; - return true; - } - - return (itemCount == plr->QInven[slot].iOpt); -} - void MissionManager::failInstancedMissions(CNSocket* sock) { // loop through all tasks; if the required instance is being left, "fail" the task Player* plr = PlayerManager::getPlayer(sock); @@ -610,3 +605,10 @@ void MissionManager::failInstancedMissions(CNSocket* sock) { } } } + +void MissionManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_START, taskStart); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_END, taskEnd); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID, setMission); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_STOP, quitMission); +} diff --git a/src/MissionManager.hpp b/src/MissionManager.hpp index 419f71b..c4c3bd3 100644 --- a/src/MissionManager.hpp +++ b/src/MissionManager.hpp @@ -42,22 +42,12 @@ namespace MissionManager { void init(); bool startTask(Player* plr, int TaskID); - void taskStart(CNSocket* sock, CNPacketData* data); - void taskEnd(CNSocket* sock, CNPacketData* data); - void setMission(CNSocket* sock, CNPacketData* data); - void quitMission(CNSocket* sock, CNPacketData* data); - int findQSlot(Player *plr, int id); - void dropQuestItem(CNSocket *sock, int task, int count, int id, int mobid); // checks if player doesn't have n/n quest items - bool isQuestItemFull(CNSocket* sock, int itemId, int itemCount); - int giveMissionReward(CNSocket *sock, int task, int choice=0); void updateFusionMatter(CNSocket* sock, int fusion); void mobKilled(CNSocket *sock, int mobid, int rolledQItem); - bool endTask(CNSocket *sock, int32_t taskNum, int choice=0); - void saveMission(Player* player, int missionId); void quitTask(CNSocket* sock, int32_t taskNum, bool manual); void failInstancedMissions(CNSocket* sock); diff --git a/src/MobAI.cpp b/src/MobAI.cpp index f7f63d7..649fddf 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -9,6 +9,8 @@ #include #include +using namespace MobAI; + std::map MobAI::Mobs; static std::queue RemovalQueue; @@ -259,10 +261,10 @@ static void dealCorruption(Mob *mob, std::vector targetData, int skillID, i if (plr->HP <= 0) { mob->target = nullptr; mob->state = MobState::RETREAT; - if (!MobAI::aggroCheck(mob, getTime())) { - MobAI::clearDebuff(mob); + if (!aggroCheck(mob, getTime())) { + clearDebuff(mob); if (mob->groupLeader != 0) - MobAI::groupRetreat(mob); + groupRetreat(mob); } } } @@ -463,8 +465,8 @@ static void deadStep(Mob *mob, time_t currTime) { // if mob is a group leader/follower, spawn where the group is. if (mob->groupLeader != 0) { - if (MobAI::Mobs.find(mob->groupLeader) != MobAI::Mobs.end()) { - Mob* leaderMob = MobAI::Mobs[mob->groupLeader]; + if (Mobs.find(mob->groupLeader) != Mobs.end()) { + Mob* leaderMob = Mobs[mob->groupLeader]; mob->appearanceData.iX = leaderMob->appearanceData.iX + mob->offsetX; mob->appearanceData.iY = leaderMob->appearanceData.iY + mob->offsetY; mob->appearanceData.iZ = leaderMob->appearanceData.iZ; @@ -488,10 +490,10 @@ static void combatStep(Mob *mob, time_t currTime) { if (PlayerManager::players.find(mob->target) == PlayerManager::players.end()) { mob->target = nullptr; mob->state = MobState::RETREAT; - if (!MobAI::aggroCheck(mob, currTime)) { - MobAI::clearDebuff(mob); + if (!aggroCheck(mob, currTime)) { + clearDebuff(mob); if (mob->groupLeader != 0) - MobAI::groupRetreat(mob); + groupRetreat(mob); } return; } @@ -503,10 +505,10 @@ static void combatStep(Mob *mob, time_t currTime) { || (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)) { mob->target = nullptr; mob->state = MobState::RETREAT; - if (!MobAI::aggroCheck(mob, currTime)) { - MobAI::clearDebuff(mob); + if (!aggroCheck(mob, currTime)) { + clearDebuff(mob); if (mob->groupLeader != 0) - MobAI::groupRetreat(mob); + groupRetreat(mob); } return; } @@ -615,9 +617,9 @@ static void combatStep(Mob *mob, time_t currTime) { if (distance >= mob->data["m_iCombatRange"]) { mob->target = nullptr; mob->state = MobState::RETREAT; - MobAI::clearDebuff(mob); + clearDebuff(mob); if (mob->groupLeader != 0) - MobAI::groupRetreat(mob); + groupRetreat(mob); } } @@ -637,7 +639,7 @@ static void roamingStep(Mob *mob, time_t currTime) { */ if (mob->state != MobState::DEAD && (mob->nextAttack == 0 || currTime >= mob->nextAttack)) { mob->nextAttack = currTime + 500; - if (MobAI::aggroCheck(mob, currTime)) + if (aggroCheck(mob, currTime)) return; } @@ -655,7 +657,7 @@ static void roamingStep(Mob *mob, time_t currTime) { */ if (mob->nextMovement != 0 && currTime < mob->nextMovement) return; - MobAI::incNextMovement(mob, currTime); + incNextMovement(mob, currTime); int xStart = mob->spawnX - mob->idleRange/2; int yStart = mob->spawnY - mob->idleRange/2; @@ -702,13 +704,13 @@ static void roamingStep(Mob *mob, time_t currTime) { if (mob->groupMember[i] == 0) break; - if (MobAI::Mobs.find(mob->groupMember[i]) == MobAI::Mobs.end()) { + if (Mobs.find(mob->groupMember[i]) == Mobs.end()) { std::cout << "[WARN] roamingStep: leader can't find a group member!" << std::endl; continue; } std::queue queue2; - Mob* followerMob = MobAI::Mobs[mob->groupMember[i]]; + Mob* followerMob = Mobs[mob->groupMember[i]]; from = { followerMob->appearanceData.iX, followerMob->appearanceData.iY, followerMob->appearanceData.iZ }; to = { farX + followerMob->offsetX, farY + followerMob->offsetY, followerMob->appearanceData.iZ }; TransportManager::lerp(&queue2, from, to, speed); @@ -758,17 +760,17 @@ static void retreatStep(Mob *mob, time_t currTime) { if (pwr.skillType == NanoManager::SkillTable[110].skillType) pwr.handle(mob, targetData, 110, NanoManager::SkillTable[110].durationTime[0], NanoManager::SkillTable[110].powerIntensity[0]); // clear outlying debuffs - MobAI::clearDebuff(mob); + clearDebuff(mob); } } static void step(CNServer *serv, time_t currTime) { - for (auto& pair : MobAI::Mobs) { + for (auto& pair : Mobs) { if (pair.second->playersInView < 0) std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl; // skip mob movement and combat if disabled or not in view - if ((!MobAI::simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD + if ((!simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD && pair.second->state != MobState::RETREAT) continue; diff --git a/src/Monitor.cpp b/src/Monitor.cpp index fe99182..9c1f8e6 100644 --- a/src/Monitor.cpp +++ b/src/Monitor.cpp @@ -12,59 +12,6 @@ static std::mutex sockLock; // guards socket list static std::list sockets; static sockaddr_in address; -SOCKET Monitor::init() { - listener = socket(AF_INET, SOCK_STREAM, 0); - if (SOCKETERROR(listener)) { - std::cout << "Failed to create monitor socket" << std::endl; - printSocketError("socket"); - exit(1); - } - -#ifdef _WIN32 - const char opt = 1; -#else - int opt = 1; -#endif - if (SOCKETERROR(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))) { - std::cout << "Failed to set SO_REUSEADDR on monitor socket" << std::endl; - printSocketError("setsockopt"); - exit(1); - } - - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(settings::MONITORPORT); - - if (SOCKETERROR(bind(listener, (struct sockaddr*)&address, sizeof(address)))) { - std::cout << "Failed to bind to monitor port" << std::endl; - printSocketError("bind"); - exit(1); - } - - if (SOCKETERROR(listen(listener, SOMAXCONN))) { - std::cout << "Failed to listen on monitor port" << std::endl; - printSocketError("listen"); - exit(1); - } - -#ifdef _WIN32 - unsigned long mode = 1; - if (ioctlsocket(listener, FIONBIO, &mode) != 0) { -#else - if (fcntl(listener, F_SETFL, (fcntl(listener, F_GETFL, 0) | O_NONBLOCK)) != 0) { -#endif - std::cerr << "[FATAL] OpenFusion: fcntl failed" << std::endl; - printSocketError("fcntl"); - exit(EXIT_FAILURE); - } - - std::cout << "Monitor listening on *:" << settings::MONITORPORT << std::endl; - - REGISTER_SHARD_TIMER(tick, settings::MONITORINTERVAL); - - return listener; -} - static bool transmit(std::list::iterator& it, char *buff, int len) { int n = 0; int sock = *it; @@ -92,7 +39,7 @@ static bool transmit(std::list::iterator& it, char *buff, int len) { return true; } -void Monitor::tick(CNServer *serv, time_t delta) { +static void tick(CNServer *serv, time_t delta) { std::lock_guard lock(sockLock); char buff[256]; int n; @@ -165,3 +112,56 @@ bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) { return true; } + +SOCKET Monitor::init() { + listener = socket(AF_INET, SOCK_STREAM, 0); + if (SOCKETERROR(listener)) { + std::cout << "Failed to create monitor socket" << std::endl; + printSocketError("socket"); + exit(1); + } + +#ifdef _WIN32 + const char opt = 1; +#else + int opt = 1; +#endif + if (SOCKETERROR(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))) { + std::cout << "Failed to set SO_REUSEADDR on monitor socket" << std::endl; + printSocketError("setsockopt"); + exit(1); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(settings::MONITORPORT); + + if (SOCKETERROR(bind(listener, (struct sockaddr*)&address, sizeof(address)))) { + std::cout << "Failed to bind to monitor port" << std::endl; + printSocketError("bind"); + exit(1); + } + + if (SOCKETERROR(listen(listener, SOMAXCONN))) { + std::cout << "Failed to listen on monitor port" << std::endl; + printSocketError("listen"); + exit(1); + } + +#ifdef _WIN32 + unsigned long mode = 1; + if (ioctlsocket(listener, FIONBIO, &mode) != 0) { +#else + if (fcntl(listener, F_SETFL, (fcntl(listener, F_GETFL, 0) | O_NONBLOCK)) != 0) { +#endif + std::cerr << "[FATAL] OpenFusion: fcntl failed" << std::endl; + printSocketError("fcntl"); + exit(EXIT_FAILURE); + } + + std::cout << "Monitor listening on *:" << settings::MONITORPORT << std::endl; + + REGISTER_SHARD_TIMER(tick, settings::MONITORINTERVAL); + + return listener; +} diff --git a/src/Monitor.hpp b/src/Monitor.hpp index c168b33..41e3eb9 100644 --- a/src/Monitor.hpp +++ b/src/Monitor.hpp @@ -7,6 +7,5 @@ namespace Monitor { SOCKET init(); - void tick(CNServer *, time_t); bool acceptConnection(SOCKET, uint16_t); }; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index f1875b8..aac73e0 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -21,6 +21,8 @@ #include "JSON.hpp" +using namespace NPCManager; + std::map NPCManager::NPCs; std::map NPCManager::Warps; std::vector NPCManager::RespawnPoints; @@ -30,8 +32,6 @@ std::unordered_map NPCManager::EggTypes; std::unordered_map NPCManager::Eggs; nlohmann::json NPCManager::NPCData; - - /* * Initialized at the end of TableData::init(). * This allows us to summon and kill mobs in arbitrary order without @@ -39,18 +39,6 @@ nlohmann::json NPCManager::NPCData; */ int32_t NPCManager::nextId; -void NPCManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TIME_TO_GO_WARP, npcWarpTimeMachine); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); - - REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_PICKUP, eggPickup); - - REGISTER_SHARD_TIMER(eggStep, 1000); -} - void NPCManager::destroyNPC(int32_t id) { // sanity check if (NPCs.find(id) == NPCs.end()) { @@ -109,7 +97,7 @@ void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t s } } -void NPCManager::npcBarkHandler(CNSocket* sock, CNPacketData* data) { +static void npcBarkHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_BARKER)) return; // malformed packet @@ -132,7 +120,7 @@ void NPCManager::npcBarkHandler(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_BARKER, sizeof(sP_FE2CL_REP_BARKER)); } -void NPCManager::npcUnsummonHandler(CNSocket* sock, CNPacketData* data) { +static void npcUnsummonHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_NPC_UNSUMMON)) return; // malformed packet @@ -169,7 +157,7 @@ BaseNPC *NPCManager::summonNPC(int x, int y, int z, uint64_t instance, int type, return npc; } -void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { +static void npcSummonHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_NPC_SUMMON)) return; // malformed packet @@ -188,22 +176,7 @@ void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { } } -void NPCManager::npcWarpHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_USE_NPC)) - return; // malformed packet - - sP_CL2FE_REQ_PC_WARP_USE_NPC* warpNpc = (sP_CL2FE_REQ_PC_WARP_USE_NPC*)data->buf; - handleWarp(sock, warpNpc->iWarpID); -} - -void NPCManager::npcWarpTimeMachine(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TIME_TO_GO_WARP)) - return; // malformed packet - // this is just a warp request - handleWarp(sock, 28); -} - -void NPCManager::handleWarp(CNSocket* sock, int32_t warpId) { +static void handleWarp(CNSocket* sock, int32_t warpId) { Player* plr = PlayerManager::getPlayer(sock); // sanity check if (Warps.find(warpId) == Warps.end()) @@ -303,6 +276,21 @@ void NPCManager::handleWarp(CNSocket* sock, int32_t warpId) { } } +static void npcWarpHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_USE_NPC)) + return; // malformed packet + + sP_CL2FE_REQ_PC_WARP_USE_NPC* warpNpc = (sP_CL2FE_REQ_PC_WARP_USE_NPC*)data->buf; + handleWarp(sock, warpNpc->iWarpID); +} + +static void npcWarpTimeMachine(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TIME_TO_GO_WARP)) + return; // malformed packet + // this is just a warp request + handleWarp(sock, 28); +} + /* * Helper function to get NPC closest to coordinates in specified chunks */ @@ -395,7 +383,7 @@ int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int durati return 0; } -void NPCManager::eggStep(CNServer* serv, time_t currTime) { +static void eggStep(CNServer* serv, time_t currTime) { // tick buffs time_t timeStamp = currTime; auto it = EggBuffs.begin(); @@ -457,7 +445,7 @@ void NPCManager::npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg->iShiny_ID = npc->iNPC_ID; } -void NPCManager::eggPickup(CNSocket* sock, CNPacketData* data) { +static void eggPickup(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_SHINY_PICKUP)) return; // malformed packet @@ -565,6 +553,7 @@ void NPCManager::eggPickup(CNSocket* sock, CNPacketData* data) { } } +// TODO: Move this to MobAI, possibly #pragma region NPCEvents // summon right arm and stage 2 body @@ -618,3 +607,15 @@ std::vector NPCManager::NPCEvents = { }; #pragma endregion NPCEvents + +void NPCManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TIME_TO_GO_WARP, npcWarpTimeMachine); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_PICKUP, eggPickup); + + REGISTER_SHARD_TIMER(eggStep, 1000); +} diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index e9d0164..4ff2e1f 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -69,20 +69,11 @@ namespace NPCManager { void sendToViewable(BaseNPC* npc, void* buf, uint32_t type, size_t size); - void npcBarkHandler(CNSocket* sock, CNPacketData* data); - void npcSummonHandler(CNSocket* sock, CNPacketData* data); - void npcUnsummonHandler(CNSocket* sock, CNPacketData* data); - void npcWarpHandler(CNSocket* sock, CNPacketData* data); - void npcWarpTimeMachine(CNSocket* sock, CNPacketData* data); - - void handleWarp(CNSocket* sock, int32_t warpId); BaseNPC *summonNPC(int x, int y, int z, uint64_t instance, int type, bool respawn=false, bool baseInstance=false); BaseNPC* getNearestNPC(std::set* chunks, int X, int Y, int Z); /// returns -1 on fail int eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration); - void eggStep(CNServer* serv, time_t currTime); void npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg); - void eggPickup(CNSocket* sock, CNPacketData* data); } diff --git a/src/NanoManager.cpp b/src/NanoManager.cpp index 8c79f34..95030bd 100644 --- a/src/NanoManager.cpp +++ b/src/NanoManager.cpp @@ -10,235 +10,11 @@ #include +using namespace NanoManager; + std::map NanoManager::NanoTable; std::map NanoManager::NanoTunings; -void NanoManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_EQUIP, nanoEquipHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_UNEQUIP, nanoUnEquipHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO, nanoGMGiveHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_TUNE, nanoSkillSetHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO_SKILL, nanoSkillSetGMHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_SKILL_USE, nanoSkillUseHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_RXCOM, nanoRecallRegisterHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_WARP_USE_RECALL, nanoRecallHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_CHARGE_NANO_STAMINA, nanoPotionHandler); -} - -void NanoManager::nanoEquipHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_NANO_EQUIP)) - return; // malformed packet - - sP_CL2FE_REQ_NANO_EQUIP* nano = (sP_CL2FE_REQ_NANO_EQUIP*)data->buf; - INITSTRUCT(sP_FE2CL_REP_NANO_EQUIP_SUCC, resp); - Player *plr = PlayerManager::getPlayer(sock); - - // sanity checks - if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0) - return; - - resp.iNanoID = nano->iNanoID; - resp.iNanoSlotNum = nano->iNanoSlotNum; - - // Update player - plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID; - - // Unbuff gumballs - int value1 = CSB_BIT_STIMPAKSLOT1 << nano->iNanoSlotNum; - if (plr->iConditionBitFlag & value1) { - int value2 = ECSB_STIMPAKSLOT1 + nano->iNanoSlotNum; - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt); - pkt.eCSTB = value2; // eCharStatusTimeBuffID - pkt.eTBU = 2; // eTimeBuffUpdate - pkt.eTBT = 1; // eTimeBuffType 1 means nano - pkt.iConditionBitFlag = plr->iConditionBitFlag &= ~value1; - sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - } - - // unsummon nano if replaced - if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum]) - summonNano(sock, -1); - - sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_EQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_EQUIP_SUCC)); -} - -void NanoManager::nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_NANO_UNEQUIP)) - return; // malformed packet - - sP_CL2FE_REQ_NANO_UNEQUIP* nano = (sP_CL2FE_REQ_NANO_UNEQUIP*)data->buf; - INITSTRUCT(sP_FE2CL_REP_NANO_UNEQUIP_SUCC, resp); - Player *plr = PlayerManager::getPlayer(sock); - - // sanity check - if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0) - return; - - resp.iNanoSlotNum = nano->iNanoSlotNum; - - // unsummon nano if removed - if (plr->equippedNanos[nano->iNanoSlotNum] == plr->activeNano) - summonNano(sock, -1); - - // update player - plr->equippedNanos[nano->iNanoSlotNum] = 0; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_UNEQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_UNEQUIP_SUCC)); -} - -void NanoManager::nanoGMGiveHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO)) - return; // ignore the malformed packet - - // Cmd: /nano - sP_CL2FE_REQ_PC_GIVE_NANO* nano = (sP_CL2FE_REQ_PC_GIVE_NANO*)data->buf; - Player *plr = PlayerManager::getPlayer(sock); - - // Add nano to player - addNano(sock, nano->iNanoID, 0); - - DEBUGLOG( - std::cout << PlayerManager::getPlayerName(plr) << " requested to add nano id: " << nano->iNanoID << std::endl; - ) -} - -void NanoManager::nanoSummonHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_NANO_ACTIVE)) - return; // malformed packet - - sP_CL2FE_REQ_NANO_ACTIVE* pkt = (sP_CL2FE_REQ_NANO_ACTIVE*)data->buf; - Player *plr = PlayerManager::getPlayer(sock); - - summonNano(sock, pkt->iNanoSlotNum); - - DEBUGLOG( - std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl; - ) -} - -void NanoManager::nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) { - Player *plr = PlayerManager::getPlayer(sock); - - int16_t nanoID = plr->activeNano; - int16_t skillID = plr->Nanos[nanoID].iSkillID; - - DEBUGLOG( - std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl; - ) - - std::vector targetData = findTargets(plr, skillID, data); - - int boost = 0; - if (getNanoBoost(plr)) - boost = 1; - - plr->Nanos[plr->activeNano].iStamina -= SkillTable[skillID].batteryUse[boost*3]; - if (plr->Nanos[plr->activeNano].iStamina < 0) - plr->Nanos[plr->activeNano].iStamina = 0; - - for (auto& pwr : NanoPowers) - if (pwr.skillType == SkillTable[skillID].skillType) - pwr.handle(sock, targetData, nanoID, skillID, SkillTable[skillID].durationTime[boost], SkillTable[skillID].powerIntensity[boost]); - - if (plr->Nanos[plr->activeNano].iStamina < 0) - summonNano(sock, -1); -} - -void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_NANO_TUNE)) - return; // malformed packet - - sP_CL2FE_REQ_NANO_TUNE* skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf; - setNanoSkill(sock, skill); -} - -void NanoManager::nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO_SKILL)) - return; // malformed packet - - sP_CL2FE_REQ_NANO_TUNE* skillGM = (sP_CL2FE_REQ_NANO_TUNE*)data->buf; - setNanoSkill(sock, skillGM); -} - -void NanoManager::nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_REGIST_RXCOM)) - return; - - sP_CL2FE_REQ_REGIST_RXCOM* recallData = (sP_CL2FE_REQ_REGIST_RXCOM*)data->buf; - - if (NPCManager::NPCs.find(recallData->iNPCID) == NPCManager::NPCs.end()) - return; - - Player* plr = PlayerManager::getPlayer(sock); - - BaseNPC *npc = NPCManager::NPCs[recallData->iNPCID]; - - INITSTRUCT(sP_FE2CL_REP_REGIST_RXCOM, response); - response.iMapNum = plr->recallInstance = (int32_t)npc->instanceID; // Never going to recall into a Fusion Lair - response.iX = plr->recallX = npc->appearanceData.iX; - response.iY = plr->recallY = npc->appearanceData.iY; - response.iZ = plr->recallZ = npc->appearanceData.iZ; - sock->sendPacket((void*)&response, P_FE2CL_REP_REGIST_RXCOM, sizeof(sP_FE2CL_REP_REGIST_RXCOM)); -} - -void NanoManager::nanoRecallHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_WARP_USE_RECALL)) - return; - - sP_CL2FE_REQ_WARP_USE_RECALL* recallData = (sP_CL2FE_REQ_WARP_USE_RECALL*)data->buf; - - Player* plr = PlayerManager::getPlayer(sock); - Player* otherPlr = PlayerManager::getPlayerFromID(recallData->iGroupMemberID); - if (otherPlr == nullptr) - return; - - // ensure the group member is still in the same IZ - if (otherPlr->instanceID != plr->instanceID) - return; - - // do not allow hypothetical recall points in lairs to mess with the respawn logic - if (PLAYERID(plr->instanceID) != 0) - return; - - if ((int32_t)plr->instanceID == otherPlr->recallInstance) - PlayerManager::sendPlayerTo(sock, otherPlr->recallX, otherPlr->recallY, otherPlr->recallZ, otherPlr->recallInstance); - else { - INITSTRUCT(sP_FE2CL_REP_WARP_USE_RECALL_FAIL, response) - sock->sendPacket((void*)&response, P_FE2CL_REP_WARP_USE_RECALL_FAIL, sizeof(sP_FE2CL_REP_WARP_USE_RECALL_FAIL)); - } -} - -void NanoManager::nanoPotionHandler(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_CHARGE_NANO_STAMINA)) - return; - - Player* player = PlayerManager::getPlayer(sock); - - // sanity checks - if (player->activeNano == -1 || player->batteryN == 0) - return; - - sNano nano = player->Nanos[player->activeNano]; - int difference = 150 - nano.iStamina; - if (player->batteryN < difference) - difference = player->batteryN; - - if (difference == 0) - return; - - INITSTRUCT(sP_FE2CL_REP_CHARGE_NANO_STAMINA, response); - response.iNanoID = nano.iID; - response.iNanoStamina = nano.iStamina + difference; - response.iBatteryN = player->batteryN - difference; - - sock->sendPacket((void*)&response, P_FE2CL_REP_CHARGE_NANO_STAMINA, sizeof(sP_FE2CL_REP_CHARGE_NANO_STAMINA)); - // now update serverside - player->batteryN -= difference; - player->Nanos[nano.iID].iStamina += difference; - -} - #pragma region Helper methods void NanoManager::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm) { if (nanoID <= 0 || nanoID >= NANO_COUNT) @@ -353,7 +129,7 @@ void NanoManager::summonNano(CNSocket *sock, int slot, bool silent) { PlayerManager::sendToViewable(sock, (void*)&pkt1, P_FE2CL_NANO_ACTIVE, sizeof(sP_FE2CL_NANO_ACTIVE)); } -void NanoManager::setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) { +static void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) { if (skill->iNanoID >= NANO_COUNT) return; @@ -426,19 +202,6 @@ void NanoManager::setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) { ) } -void NanoManager::resetNanoSkill(CNSocket* sock, int16_t nanoID) { - if (nanoID >= NANO_COUNT) - return; - - Player *plr = PlayerManager::getPlayer(sock); - - sNano nano = plr->Nanos[nanoID]; - - // 0 is reset - nano.iSkillID = 0; - plr->Nanos[nanoID] = nano; -} - // 0=A 1=B 2=C -1=Not found int NanoManager::nanoStyle(int nanoID) { if (nanoID < 1 || nanoID >= (int)NanoTable.size()) @@ -454,3 +217,229 @@ bool NanoManager::getNanoBoost(Player* plr) { return false; } #pragma endregion + +static void nanoEquipHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_NANO_EQUIP)) + return; // malformed packet + + sP_CL2FE_REQ_NANO_EQUIP* nano = (sP_CL2FE_REQ_NANO_EQUIP*)data->buf; + INITSTRUCT(sP_FE2CL_REP_NANO_EQUIP_SUCC, resp); + Player *plr = PlayerManager::getPlayer(sock); + + // sanity checks + if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0) + return; + + resp.iNanoID = nano->iNanoID; + resp.iNanoSlotNum = nano->iNanoSlotNum; + + // Update player + plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID; + + // Unbuff gumballs + int value1 = CSB_BIT_STIMPAKSLOT1 << nano->iNanoSlotNum; + if (plr->iConditionBitFlag & value1) { + int value2 = ECSB_STIMPAKSLOT1 + nano->iNanoSlotNum; + INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt); + pkt.eCSTB = value2; // eCharStatusTimeBuffID + pkt.eTBU = 2; // eTimeBuffUpdate + pkt.eTBT = 1; // eTimeBuffType 1 means nano + pkt.iConditionBitFlag = plr->iConditionBitFlag &= ~value1; + sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); + } + + // unsummon nano if replaced + if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum]) + summonNano(sock, -1); + + sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_EQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_EQUIP_SUCC)); +} + +static void nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_NANO_UNEQUIP)) + return; // malformed packet + + sP_CL2FE_REQ_NANO_UNEQUIP* nano = (sP_CL2FE_REQ_NANO_UNEQUIP*)data->buf; + INITSTRUCT(sP_FE2CL_REP_NANO_UNEQUIP_SUCC, resp); + Player *plr = PlayerManager::getPlayer(sock); + + // sanity check + if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0) + return; + + resp.iNanoSlotNum = nano->iNanoSlotNum; + + // unsummon nano if removed + if (plr->equippedNanos[nano->iNanoSlotNum] == plr->activeNano) + summonNano(sock, -1); + + // update player + plr->equippedNanos[nano->iNanoSlotNum] = 0; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_UNEQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_UNEQUIP_SUCC)); +} + +static void nanoGMGiveHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO)) + return; // ignore the malformed packet + + // Cmd: /nano + sP_CL2FE_REQ_PC_GIVE_NANO* nano = (sP_CL2FE_REQ_PC_GIVE_NANO*)data->buf; + Player *plr = PlayerManager::getPlayer(sock); + + // Add nano to player + addNano(sock, nano->iNanoID, 0); + + DEBUGLOG( + std::cout << PlayerManager::getPlayerName(plr) << " requested to add nano id: " << nano->iNanoID << std::endl; + ) +} + +static void nanoSummonHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_NANO_ACTIVE)) + return; // malformed packet + + sP_CL2FE_REQ_NANO_ACTIVE* pkt = (sP_CL2FE_REQ_NANO_ACTIVE*)data->buf; + Player *plr = PlayerManager::getPlayer(sock); + + summonNano(sock, pkt->iNanoSlotNum); + + DEBUGLOG( + std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl; + ) +} + +static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) { + Player *plr = PlayerManager::getPlayer(sock); + + int16_t nanoID = plr->activeNano; + int16_t skillID = plr->Nanos[nanoID].iSkillID; + + DEBUGLOG( + std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl; + ) + + std::vector targetData = findTargets(plr, skillID, data); + + int boost = 0; + if (getNanoBoost(plr)) + boost = 1; + + plr->Nanos[plr->activeNano].iStamina -= SkillTable[skillID].batteryUse[boost*3]; + if (plr->Nanos[plr->activeNano].iStamina < 0) + plr->Nanos[plr->activeNano].iStamina = 0; + + for (auto& pwr : NanoPowers) + if (pwr.skillType == SkillTable[skillID].skillType) + pwr.handle(sock, targetData, nanoID, skillID, SkillTable[skillID].durationTime[boost], SkillTable[skillID].powerIntensity[boost]); + + if (plr->Nanos[plr->activeNano].iStamina < 0) + summonNano(sock, -1); +} + +static void nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_NANO_TUNE)) + return; // malformed packet + + sP_CL2FE_REQ_NANO_TUNE* skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf; + setNanoSkill(sock, skill); +} + +static void nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO_SKILL)) + return; // malformed packet + + sP_CL2FE_REQ_NANO_TUNE* skillGM = (sP_CL2FE_REQ_NANO_TUNE*)data->buf; + setNanoSkill(sock, skillGM); +} + +static void nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_REGIST_RXCOM)) + return; + + sP_CL2FE_REQ_REGIST_RXCOM* recallData = (sP_CL2FE_REQ_REGIST_RXCOM*)data->buf; + + if (NPCManager::NPCs.find(recallData->iNPCID) == NPCManager::NPCs.end()) + return; + + Player* plr = PlayerManager::getPlayer(sock); + + BaseNPC *npc = NPCManager::NPCs[recallData->iNPCID]; + + INITSTRUCT(sP_FE2CL_REP_REGIST_RXCOM, response); + response.iMapNum = plr->recallInstance = (int32_t)npc->instanceID; // Never going to recall into a Fusion Lair + response.iX = plr->recallX = npc->appearanceData.iX; + response.iY = plr->recallY = npc->appearanceData.iY; + response.iZ = plr->recallZ = npc->appearanceData.iZ; + sock->sendPacket((void*)&response, P_FE2CL_REP_REGIST_RXCOM, sizeof(sP_FE2CL_REP_REGIST_RXCOM)); +} + +static void nanoRecallHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_WARP_USE_RECALL)) + return; + + sP_CL2FE_REQ_WARP_USE_RECALL* recallData = (sP_CL2FE_REQ_WARP_USE_RECALL*)data->buf; + + Player* plr = PlayerManager::getPlayer(sock); + Player* otherPlr = PlayerManager::getPlayerFromID(recallData->iGroupMemberID); + if (otherPlr == nullptr) + return; + + // ensure the group member is still in the same IZ + if (otherPlr->instanceID != plr->instanceID) + return; + + // do not allow hypothetical recall points in lairs to mess with the respawn logic + if (PLAYERID(plr->instanceID) != 0) + return; + + if ((int32_t)plr->instanceID == otherPlr->recallInstance) + PlayerManager::sendPlayerTo(sock, otherPlr->recallX, otherPlr->recallY, otherPlr->recallZ, otherPlr->recallInstance); + else { + INITSTRUCT(sP_FE2CL_REP_WARP_USE_RECALL_FAIL, response) + sock->sendPacket((void*)&response, P_FE2CL_REP_WARP_USE_RECALL_FAIL, sizeof(sP_FE2CL_REP_WARP_USE_RECALL_FAIL)); + } +} + +static void nanoPotionHandler(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_CHARGE_NANO_STAMINA)) + return; + + Player* player = PlayerManager::getPlayer(sock); + + // sanity checks + if (player->activeNano == -1 || player->batteryN == 0) + return; + + sNano nano = player->Nanos[player->activeNano]; + int difference = 150 - nano.iStamina; + if (player->batteryN < difference) + difference = player->batteryN; + + if (difference == 0) + return; + + INITSTRUCT(sP_FE2CL_REP_CHARGE_NANO_STAMINA, response); + response.iNanoID = nano.iID; + response.iNanoStamina = nano.iStamina + difference; + response.iBatteryN = player->batteryN - difference; + + sock->sendPacket((void*)&response, P_FE2CL_REP_CHARGE_NANO_STAMINA, sizeof(sP_FE2CL_REP_CHARGE_NANO_STAMINA)); + // now update serverside + player->batteryN -= difference; + player->Nanos[nano.iID].iStamina += difference; + +} + +void NanoManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_EQUIP, nanoEquipHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_UNEQUIP, nanoUnEquipHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO, nanoGMGiveHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_TUNE, nanoSkillSetHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO_SKILL, nanoSkillSetGMHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_SKILL_USE, nanoSkillUseHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_RXCOM, nanoRecallRegisterHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_WARP_USE_RECALL, nanoRecallHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_CHARGE_NANO_STAMINA, nanoPotionHandler); +} diff --git a/src/NanoManager.hpp b/src/NanoManager.hpp index 9fc2eef..d75d00e 100644 --- a/src/NanoManager.hpp +++ b/src/NanoManager.hpp @@ -20,23 +20,9 @@ namespace NanoManager { extern std::map NanoTunings; void init(); - void nanoSummonHandler(CNSocket* sock, CNPacketData* data); - void nanoEquipHandler(CNSocket* sock, CNPacketData* data); - void nanoUnEquipHandler(CNSocket* sock, CNPacketData* data); - void nanoGMGiveHandler(CNSocket* sock, CNPacketData* data); - void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data); - void nanoSkillSetHandler(CNSocket* sock, CNPacketData* data); - void nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data); - void nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data); - void nanoRecallHandler(CNSocket* sock, CNPacketData* data); - void nanoPotionHandler(CNSocket* sock, CNPacketData* data); - // Helper methods void addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm=false); void summonNano(CNSocket* sock, int slot, bool silent = false); - void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill); - void resetNanoSkill(CNSocket* sock, int16_t nanoID); int nanoStyle(int nanoID); - std::vector findTargets(Player* plr, int skillID, CNPacketData* data = nullptr); bool getNanoBoost(Player* plr); } diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 4597eb1..079618a 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -21,23 +21,11 @@ #include #include +using namespace PlayerManager; + std::map PlayerManager::players; -void PlayerManager::init() { - // register packet types - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ENTER, enterPlayer); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_LOADING_COMPLETE, loadPlayer); - REGISTER_SHARD_PACKET(P_CL2FE_REP_LIVE_CHECK, heartbeatPlayer); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_REGEN, revivePlayer); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EXIT, exitGame); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, setSpecialSwitchPlayer); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, enterPlayerVehicle); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, exitPlayerVehicle); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag); -} - -void PlayerManager::addPlayer(CNSocket* key, Player plr) { +static void addPlayer(CNSocket* key, Player plr) { Player *p = new Player(); memcpy(p, &plr, sizeof(Player)); @@ -174,7 +162,36 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z) { sendPlayerTo(sock, X, Y, Z, getPlayer(sock)->instanceID); } -void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { +/* + * Sends all nanos, from 0 to 58 (the contents of the Nanos array in PC_ENTER_SUCC are totally irrelevant). + * The first Nano in the in-game nanobook is the Unstable Nano, which is Van Kleiss. + * 0 (in plr->Nanos) is the null nano entry. + * 58 is a "Coming Soon" duplicate entry for an actual Van Kleiss nano, identical to the Unstable Nano. + * Nanos the player hasn't unlocked will (and should) be greyed out. Thus, all nanos should be accounted + * for in these packets, even if the player hasn't unlocked them. + */ +static void sendNanoBookSubset(CNSocket *sock) { +#ifdef ACADEMY + Player *plr = getPlayer(sock); + + int16_t id = 0; + INITSTRUCT(sP_FE2CL_REP_NANO_BOOK_SUBSET, pkt); + + pkt.PCUID = plr->iID; + pkt.bookSize = NANO_COUNT; + + while (id < NANO_COUNT) { + pkt.elementOffset = id; + + for (int i = id - pkt.elementOffset; id < NANO_COUNT && i < 10; id++, i = id - pkt.elementOffset) + pkt.element[i] = plr->Nanos[id]; + + sock->sendPacket((void*)&pkt, P_FE2CL_REP_NANO_BOOK_SUBSET, sizeof(sP_FE2CL_REP_NANO_BOOK_SUBSET)); + } +#endif +} + +static void enterPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_ENTER)) return; // ignore the malformed packet @@ -306,40 +323,11 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { // initial buddy sync BuddyManager::refreshBuddyList(sock); - for (auto& pair : PlayerManager::players) + for (auto& pair : players) if (pair.second->notify) ChatManager::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(&plr) + " has joined."); } -/* - * Sends all nanos, from 0 to 58 (the contents of the Nanos array in PC_ENTER_SUCC are totally irrelevant). - * The first Nano in the in-game nanobook is the Unstable Nano, which is Van Kleiss. - * 0 (in plr->Nanos) is the null nano entry. - * 58 is a "Coming Soon" duplicate entry for an actual Van Kleiss nano, identical to the Unstable Nano. - * Nanos the player hasn't unlocked will (and should) be greyed out. Thus, all nanos should be accounted - * for in these packets, even if the player hasn't unlocked them. - */ -void PlayerManager::sendNanoBookSubset(CNSocket *sock) { -#ifdef ACADEMY - Player *plr = getPlayer(sock); - - int16_t id = 0; - INITSTRUCT(sP_FE2CL_REP_NANO_BOOK_SUBSET, pkt); - - pkt.PCUID = plr->iID; - pkt.bookSize = NANO_COUNT; - - while (id < NANO_COUNT) { - pkt.elementOffset = id; - - for (int i = id - pkt.elementOffset; id < NANO_COUNT && i < 10; id++, i = id - pkt.elementOffset) - pkt.element[i] = plr->Nanos[id]; - - sock->sendPacket((void*)&pkt, P_FE2CL_REP_NANO_BOOK_SUBSET, sizeof(sP_FE2CL_REP_NANO_BOOK_SUBSET)); - } -#endif -} - void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) { Player* plr = getPlayer(sock); for (auto it = plr->viewableChunks->begin(); it != plr->viewableChunks->end(); it++) { @@ -353,7 +341,7 @@ void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, siz } } -void PlayerManager::loadPlayer(CNSocket* sock, CNPacketData* data) { +static void loadPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_LOADING_COMPLETE)) return; // ignore the malformed packet @@ -373,11 +361,11 @@ void PlayerManager::loadPlayer(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&response, P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC)); } -void PlayerManager::heartbeatPlayer(CNSocket* sock, CNPacketData* data) { +static void heartbeatPlayer(CNSocket* sock, CNPacketData* data) { getPlayer(sock)->lastHeartbeat = getTime(); } -void PlayerManager::exitGame(CNSocket* sock, CNPacketData* data) { +static void exitGame(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_EXIT)) return; @@ -390,12 +378,27 @@ void PlayerManager::exitGame(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&response, P_FE2CL_REP_PC_EXIT_SUCC, sizeof(sP_FE2CL_REP_PC_EXIT_SUCC)); } -void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) { +static WarpLocation* getRespawnPoint(Player *plr) { + WarpLocation* best = nullptr; + uint32_t curDist, bestDist = UINT32_MAX; + + for (auto& targ : NPCManager::RespawnPoints) { + curDist = sqrt(pow(plr->x - targ.x, 2) + pow(plr->y - targ.y, 2)); + if (curDist < bestDist && targ.instanceID == MAPNUM(plr->instanceID)) { // only mapNum needs to match + best = &targ; + bestDist = curDist; + } + } + + return best; +} + +static void revivePlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_REGEN)) return; - Player *plr = PlayerManager::getPlayer(sock); - WarpLocation* target = PlayerManager::getRespawnPoint(plr); + Player *plr = getPlayer(sock); + WarpLocation* target = getRespawnPoint(plr); sP_CL2FE_REQ_PC_REGEN* reviveData = (sP_CL2FE_REQ_PC_REGEN*)data->buf; INITSTRUCT(sP_FE2CL_REP_PC_REGEN_SUCC, response); @@ -466,7 +469,7 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) { resp2.PCRegenDataForOtherPC.iHP = plr->HP; resp2.PCRegenDataForOtherPC.iAngle = plr->angle; - Player *otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); + Player *otherPlr = getPlayerFromID(plr->iIDGroup); if (otherPlr != nullptr) { int bitFlag = GroupManager::getGroupFlags(otherPlr); resp2.PCRegenDataForOtherPC.iConditionBitFlag = plr->iConditionBitFlag = plr->iSelfConditionBitFlag | bitFlag; @@ -485,7 +488,7 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) { updatePlayerPosition(sock, x, y, z, plr->instanceID, plr->angle); } -void PlayerManager::enterPlayerVehicle(CNSocket* sock, CNPacketData* data) { +static void enterPlayerVehicle(CNSocket* sock, CNPacketData* data) { Player* plr = getPlayer(sock); bool expired = plr->Equip[8].iTimeLimit < getTimestamp() && plr->Equip[8].iTimeLimit != 0; @@ -514,7 +517,7 @@ void PlayerManager::enterPlayerVehicle(CNSocket* sock, CNPacketData* data) { } } -void PlayerManager::exitPlayerVehicle(CNSocket* sock, CNPacketData* data) { +static void exitPlayerVehicle(CNSocket* sock, CNPacketData* data) { Player* plr = getPlayer(sock); if (plr->iPCState & 8) { @@ -531,11 +534,11 @@ void PlayerManager::exitPlayerVehicle(CNSocket* sock, CNPacketData* data) { } } -void PlayerManager::setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data) { +static void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data) { BuiltinCommands::setSpecialState(sock, data); } -void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) { +static void changePlayerGuide(CNSocket *sock, CNPacketData *data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_CHANGE_MENTOR)) return; @@ -565,7 +568,7 @@ void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) { plr->mentor = pkt->iMentor; } -void PlayerManager::setFirstUseFlag(CNSocket* sock, CNPacketData* data) { +static void setFirstUseFlag(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_FIRST_USE_FLAG_SET)) return; @@ -609,24 +612,9 @@ std::string PlayerManager::getPlayerName(Player *plr, bool id) { return ret; } -WarpLocation* PlayerManager::getRespawnPoint(Player *plr) { - WarpLocation* best = nullptr; - uint32_t curDist, bestDist = UINT32_MAX; - - for (auto& targ : NPCManager::RespawnPoints) { - curDist = sqrt(pow(plr->x - targ.x, 2) + pow(plr->y - targ.y, 2)); - if (curDist < bestDist && targ.instanceID == MAPNUM(plr->instanceID)) { // only mapNum needs to match - best = &targ; - bestDist = curDist; - } - } - - return best; -} - bool PlayerManager::isAccountInUse(int accountId) { std::map::iterator it; - for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) { + for (it = players.begin(); it != players.end(); it++) { if (it->second->accountId == accountId) return true; } @@ -698,3 +686,17 @@ CNSocket *PlayerManager::getSockFromAny(int by, int id, int uid, std::string fir } #pragma endregion + +void PlayerManager::init() { + // register packet types + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ENTER, enterPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_LOADING_COMPLETE, loadPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REP_LIVE_CHECK, heartbeatPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_REGEN, revivePlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EXIT, exitGame); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, setSpecialSwitchPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, enterPlayerVehicle); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, exitPlayerVehicle); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag); +} diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index a99f7cc..9701729 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -16,7 +16,6 @@ namespace PlayerManager { extern std::map players; void init(); - void addPlayer(CNSocket* key, Player plr); void removePlayer(CNSocket* key); void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, uint64_t I, int angle); @@ -26,31 +25,13 @@ namespace PlayerManager { void sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size); - void enterPlayer(CNSocket* sock, CNPacketData* data); - void loadPlayer(CNSocket* sock, CNPacketData* data); - void heartbeatPlayer(CNSocket* sock, CNPacketData* data); - void revivePlayer(CNSocket* sock, CNPacketData* data); - void exitGame(CNSocket* sock, CNPacketData* data); - - void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data); - void changePlayerGuide(CNSocket *sock, CNPacketData *data); - - void enterPlayerVehicle(CNSocket* sock, CNPacketData* data); - void exitPlayerVehicle(CNSocket* sock, CNPacketData* data); - - void setFirstUseFlag(CNSocket* sock, CNPacketData* data); - Player *getPlayer(CNSocket* key); std::string getPlayerName(Player *plr, bool id=true); - WarpLocation* getRespawnPoint(Player *plr); bool isAccountInUse(int accountId); void exitDuplicate(int accountId); - void setSpecialState(CNSocket* sock, CNPacketData* data); Player *getPlayerFromID(int32_t iID); CNSocket *getSockFromID(int32_t iID); CNSocket *getSockFromName(std::string firstname, std::string lastname); CNSocket *getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname); - - void sendNanoBookSubset(CNSocket *sock); } diff --git a/src/RacingManager.cpp b/src/RacingManager.cpp index a77f1c7..0b9d057 100644 --- a/src/RacingManager.cpp +++ b/src/RacingManager.cpp @@ -7,18 +7,13 @@ #include "db/Database.hpp" #include "NPCManager.hpp" +using namespace RacingManager; + std::map RacingManager::EPData; std::map RacingManager::EPRaces; std::map, std::vector>> RacingManager::EPRewards; -void RacingManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_START, racingStart); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_GET_RING, racingGetPod); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_CANCEL, racingCancel); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_END, racingEnd); -} - -void RacingManager::racingStart(CNSocket* sock, CNPacketData* data) { +static void racingStart(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_EP_RACE_START)) return; // malformed packet @@ -42,7 +37,7 @@ void RacingManager::racingStart(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_EP_RACE_START_SUCC, sizeof(sP_FE2CL_REP_EP_RACE_START_SUCC)); } -void RacingManager::racingGetPod(CNSocket* sock, CNPacketData* data) { +static void racingGetPod(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_EP_GET_RING)) return; // malformed packet @@ -62,7 +57,7 @@ void RacingManager::racingGetPod(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_EP_GET_RING_SUCC, sizeof(sP_FE2CL_REP_EP_GET_RING_SUCC)); } -void RacingManager::racingCancel(CNSocket* sock, CNPacketData* data) { +static void racingCancel(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_EP_RACE_CANCEL)) return; // malformed packet @@ -75,7 +70,7 @@ void RacingManager::racingCancel(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_EP_RACE_CANCEL_SUCC, sizeof(sP_FE2CL_REP_EP_RACE_CANCEL_SUCC)); } -void RacingManager::racingEnd(CNSocket* sock, CNPacketData* data) { +static void racingEnd(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_EP_RACE_END)) return; // malformed packet @@ -165,3 +160,9 @@ void RacingManager::racingEnd(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_EP_RACE_END_SUCC, sizeof(sP_FE2CL_REP_EP_RACE_END_SUCC)); } +void RacingManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_START, racingStart); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_GET_RING, racingGetPod); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_CANCEL, racingCancel); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_EP_RACE_END, racingEnd); +} diff --git a/src/RacingManager.hpp b/src/RacingManager.hpp index c012d4e..81127ff 100644 --- a/src/RacingManager.hpp +++ b/src/RacingManager.hpp @@ -17,9 +17,4 @@ namespace RacingManager { extern std::map, std::vector>> EPRewards; void init(); - - void racingStart(CNSocket* sock, CNPacketData* data); - void racingGetPod(CNSocket* sock, CNPacketData* data); - void racingCancel(CNSocket* sock, CNPacketData* data); - void racingEnd(CNSocket* sock, CNPacketData* data); } diff --git a/src/TableData.cpp b/src/TableData.cpp index 5d242cc..a8f01c7 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -15,6 +15,8 @@ #include #include +using namespace TableData; + std::map> TableData::RunningSkywayRoutes; std::map TableData::RunningNPCRotations; std::map TableData::RunningNPCMapNumbers; @@ -31,6 +33,526 @@ public: const char *what() const throw() { return msg.c_str(); } }; +/* + * Create a full and properly-paced path by interpolating between keyframes. + */ +static void constructPathSkyway(nlohmann::json::iterator _pathData) { + auto pathData = _pathData.value(); + // Interpolate + nlohmann::json pathPoints = pathData["points"]; + std::queue points; + nlohmann::json::iterator _point = pathPoints.begin(); + auto point = _point.value(); + WarpLocation last = { point["iX"] , point["iY"] , point["iZ"] }; // start pos + // use some for loop trickery; start position should not be a point + for (_point++; _point != pathPoints.end(); _point++) { + point = _point.value(); + WarpLocation coords = { point["iX"] , point["iY"] , point["iZ"] }; + TransportManager::lerp(&points, last, coords, pathData["iMonkeySpeed"]); + points.push(coords); // add keyframe to the queue + last = coords; // update start pos + } + TransportManager::SkywayPaths[pathData["iRouteID"]] = points; +} + +static void constructPathNPC(nlohmann::json::iterator _pathData, int32_t id=0) { + auto pathData = _pathData.value(); + // Interpolate + nlohmann::json pathPoints = pathData["points"]; + std::queue points; + nlohmann::json::iterator _point = pathPoints.begin(); + auto point = _point.value(); + WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords + int stopTime = point["stop"]; + for (_point++; _point != pathPoints.end(); _point++) { // loop through all point Bs + point = _point.value(); + for(int i = 0; i < stopTime + 1; i++) // repeat point if it's a stop + points.push(from); // add point A to the queue + WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords + TransportManager::lerp(&points, from, to, pathData["iBaseSpeed"]); // lerp from A to B + from = to; // update point A + stopTime = point["stop"]; + } + + if (id == 0) + id = pathData["iNPCID"]; + + TransportManager::NPCQueues[id] = points; +} + +/* + * Load paths from paths JSON. + */ +static void loadPaths(int* nextId) { + try { + std::ifstream inFile(settings::PATHJSON); + nlohmann::json pathData; + + // read file into json + inFile >> pathData; + + // skyway paths + nlohmann::json pathDataSkyway = pathData["skyway"]; + for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) { + constructPathSkyway(skywayPath); + } + std::cout << "[INFO] Loaded " << TransportManager::SkywayPaths.size() << " skyway paths" << std::endl; + + // slider circuit + nlohmann::json pathDataSlider = pathData["slider"]; + // lerp between keyframes + std::queue route; + // initial point + nlohmann::json::iterator _point = pathDataSlider.begin(); // iterator + auto point = _point.value(); + WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords + int stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // arbitrary stop length + // remaining points + for (_point++; _point != pathDataSlider.end(); _point++) { // loop through all point Bs + point = _point.value(); + for (int i = 0; i < stopTime + 1; i++) { // repeat point if it's a stop + route.push(from); // add point A to the queue + } + WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords + // we may need to change this later; right now, the speed is cut before and after stops (no accel) + float curve = 1; + if (stopTime > 0) { // point A is a stop + curve = 0.375f;//2.0f; + } else if (point["stop"]) { // point B is a stop + curve = 0.375f;//0.35f; + } + TransportManager::lerp(&route, from, to, SLIDER_SPEED * curve, 1); // lerp from A to B (arbitrary speed) + from = to; // update point A + stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // set stop ticks for next point A + } + // Uniform distance calculation + int passedDistance = 0; + // initial point + int pos = 0; + WarpLocation lastPoint = route.front(); + route.pop(); + route.push(lastPoint); + for (pos = 1; pos < route.size(); pos++) { + WarpLocation point = route.front(); + passedDistance += hypot(point.x - lastPoint.x, point.y - lastPoint.y); + if (passedDistance >= SLIDER_GAP_SIZE) { // space them out uniformaly + passedDistance -= SLIDER_GAP_SIZE; // step down + // spawn a slider + BaseNPC* slider = new BaseNPC(point.x, point.y, point.z, 0, INSTANCE_OVERWORLD, 1, (*nextId)++, NPC_BUS); + NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider; + NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->appearanceData.iX, slider->appearanceData.iY, slider->appearanceData.iZ, INSTANCE_OVERWORLD, 0); + TransportManager::NPCQueues[slider->appearanceData.iNPC_ID] = route; + } + // rotate + route.pop(); + route.push(point); + lastPoint = point; + } + + // npc paths + nlohmann::json pathDataNPC = pathData["npc"]; + /* + for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) { + constructPathNPC(npcPath); + } + */ + + // mob paths + pathDataNPC = pathData["mob"]; + for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) { + for (auto& pair : MobAI::Mobs) { + if (pair.second->appearanceData.iNPCType == npcPath.value()["iNPCType"]) { + std::cout << "[INFO] Using static path for mob " << pair.second->appearanceData.iNPCType << " with ID " << pair.first << std::endl; + + auto firstPoint = npcPath.value()["points"][0]; + if (firstPoint["iX"] != pair.second->spawnX || firstPoint["iY"] != pair.second->spawnY) { + std::cout << "[FATAL] The first point of the route for mob " << pair.first << + " (type " << pair.second->appearanceData.iNPCType << ") does not correspond with its spawn point." << std::endl; + exit(1); + } + + constructPathNPC(npcPath, pair.first); + pair.second->staticPath = true; + break; // only one NPC per path + } + } + } + std::cout << "[INFO] Loaded " << TransportManager::NPCQueues.size() << " NPC paths" << std::endl; + } + catch (const std::exception& err) { + std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl; + exit(1); + } +} + +/* + * Load drops data from JSON. + * This has to be called after reading xdt because it reffers to ItemData!!! + */ +static void loadDrops() { + try { + std::ifstream inFile(settings::DROPSJSON); + nlohmann::json dropData; + + // read file into json + inFile >> dropData; + + // MobDropChances + nlohmann::json mobDropChances = dropData["MobDropChances"]; + for (nlohmann::json::iterator _dropChance = mobDropChances.begin(); _dropChance != mobDropChances.end(); _dropChance++) { + auto dropChance = _dropChance.value(); + MobDropChance toAdd = {}; + toAdd.dropChance = (int)dropChance["DropChance"]; + for (nlohmann::json::iterator _cratesRatio = dropChance["CratesRatio"].begin(); _cratesRatio != dropChance["CratesRatio"].end(); _cratesRatio++) { + toAdd.cratesRatio.push_back((int)_cratesRatio.value()); + } + ItemManager::MobDropChances[(int)dropChance["Type"]] = toAdd; + } + + // MobDrops + nlohmann::json mobDrops = dropData["MobDrops"]; + for (nlohmann::json::iterator _drop = mobDrops.begin(); _drop != mobDrops.end(); _drop++) { + auto drop = _drop.value(); + MobDrop toAdd = {}; + for (nlohmann::json::iterator _crates = drop["CrateIDs"].begin(); _crates != drop["CrateIDs"].end(); _crates++) { + toAdd.crateIDs.push_back((int)_crates.value()); + } + + toAdd.dropChanceType = (int)drop["DropChance"]; + // Check if DropChance exists + if (ItemManager::MobDropChances.find(toAdd.dropChanceType) == ItemManager::MobDropChances.end()) { + throw TableException(" MobDropChance not found: " + std::to_string((toAdd.dropChanceType))); + } + // Check if number of crates is correct + if (!(ItemManager::MobDropChances[(int)drop["DropChance"]].cratesRatio.size() == toAdd.crateIDs.size())) { + throw TableException(" DropType " + std::to_string((int)drop["DropType"]) + " contains invalid number of crates"); + } + + toAdd.taros = (int)drop["Taros"]; + toAdd.fm = (int)drop["FM"]; + toAdd.boosts = (int)drop["Boosts"]; + ItemManager::MobDrops[(int)drop["DropType"]] = toAdd; + } + + std::cout << "[INFO] Loaded " << ItemManager::MobDrops.size() << " Mob Drop Types"<< std::endl; + + // Rarity Ratios + nlohmann::json rarities = dropData["RarityRatios"]; + for (nlohmann::json::iterator _rarity = rarities.begin(); _rarity != rarities.end(); _rarity++) { + auto rarity = _rarity.value(); + std::vector toAdd; + for (nlohmann::json::iterator _ratio = rarity["Ratio"].begin(); _ratio != rarity["Ratio"].end(); _ratio++){ + toAdd.push_back((int)_ratio.value()); + } + ItemManager::RarityRatios[(int)rarity["Type"]] = toAdd; + } + + // Crates + nlohmann::json crates = dropData["Crates"]; + for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) { + auto crate = _crate.value(); + Crate toAdd; + toAdd.rarityRatioId = (int)crate["RarityRatio"]; + for (nlohmann::json::iterator _itemSet = crate["ItemSets"].begin(); _itemSet != crate["ItemSets"].end(); _itemSet++) { + toAdd.itemSets.push_back((int)_itemSet.value()); + } + ItemManager::Crates[(int)crate["Id"]] = toAdd; + } + + // Crate Items + nlohmann::json items = dropData["Items"]; + int itemCount = 0; + for (nlohmann::json::iterator _item = items.begin(); _item != items.end(); _item++) { + auto item = _item.value(); + std::pair itemSetkey = std::make_pair((int)item["ItemSet"], (int)item["Rarity"]); + std::pair itemDataKey = std::make_pair((int)item["Id"], (int)item["Type"]); + + if (ItemManager::ItemData.find(itemDataKey) == ItemManager::ItemData.end()) { + char buff[255]; + sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]); + throw TableException(std::string(buff)); + } + + std::map, ItemManager::Item>::iterator toAdd = ItemManager::ItemData.find(itemDataKey); + + // if item collection doesn't exist, start a new one + if (ItemManager::CrateItems.find(itemSetkey) == ItemManager::CrateItems.end()) { + std::vector, ItemManager::Item>::iterator> vector; + vector.push_back(toAdd); + ItemManager::CrateItems[itemSetkey] = vector; + } else // else add a new element to existing collection + ItemManager::CrateItems[itemSetkey].push_back(toAdd); + + itemCount++; + } + +#ifdef ACADEMY + nlohmann::json capsules = dropData["NanoCapsules"]; + + for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) { + auto capsule = _capsule.value(); + ItemManager::NanoCapsules[(int)capsule["Crate"]] = (int)capsule["Nano"]; + } +#endif + nlohmann::json codes = dropData["CodeItems"]; + for (nlohmann::json::iterator _code = codes.begin(); _code != codes.end(); _code++) { + auto code = _code.value(); + std::string codeStr = code["Code"]; + std::pair item = std::make_pair((int)code["Id"], (int)code["Type"]); + + if (ItemManager::CodeItems.find(codeStr) == ItemManager::CodeItems.end()) + ItemManager::CodeItems[codeStr] = std::vector>(); + + ItemManager::CodeItems[codeStr].push_back(item); + } + + std::cout << "[INFO] Loaded " << ItemManager::Crates.size() << " Crates containing " + << itemCount << " items" << std::endl; + + // Racing rewards + nlohmann::json racing = dropData["Racing"]; + for (nlohmann::json::iterator _race = racing.begin(); _race != racing.end(); _race++) { + auto race = _race.value(); + int raceEPID = race["EPID"]; + + // find the instance data corresponding to the EPID + int EPMap = -1; + for (auto it = RacingManager::EPData.begin(); it != RacingManager::EPData.end(); it++) { + if (it->second.EPID == raceEPID) { + EPMap = it->first; + } + } + + if (EPMap == -1) { // not found + std::cout << "[WARN] EP with ID " << raceEPID << " not found, skipping" << std::endl; + continue; + } + + // time limit isn't stored in the XDT, so we include it in the reward table instead + RacingManager::EPData[EPMap].maxTime = race["TimeLimit"]; + + // score cutoffs + std::vector rankScores; + for (nlohmann::json::iterator _rankScore = race["RankScores"].begin(); _rankScore != race["RankScores"].end(); _rankScore++) { + rankScores.push_back((int)_rankScore.value()); + } + + // reward IDs for each rank + std::vector rankRewards; + for (nlohmann::json::iterator _rankReward = race["Rewards"].begin(); _rankReward != race["Rewards"].end(); _rankReward++) { + rankRewards.push_back((int)_rankReward.value()); + } + + if (rankScores.size() != 5 || rankScores.size() != rankRewards.size()) { + char buff[255]; + sprintf(buff, "Race in EP %d doesn't have exactly 5 score/reward pairs", raceEPID); + throw TableException(std::string(buff)); + } + + RacingManager::EPRewards[raceEPID] = std::make_pair(rankScores, rankRewards); + } + + std::cout << "[INFO] Loaded rewards for " << RacingManager::EPRewards.size() << " IZ races" << std::endl; + + } + catch (const std::exception& err) { + std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl; + exit(1); + } +} + +static void loadEggs(int32_t* nextId) { + try { + std::ifstream inFile(settings::EGGSJSON); + nlohmann::json eggData; + + // read file into json + inFile >> eggData; + + // EggTypes + nlohmann::json eggTypes = eggData["EggTypes"]; + for (nlohmann::json::iterator _eggType = eggTypes.begin(); _eggType != eggTypes.end(); _eggType++) { + auto eggType = _eggType.value(); + EggType toAdd = {}; + toAdd.dropCrateId = (int)eggType["DropCrateId"]; + toAdd.effectId = (int)eggType["EffectId"]; + toAdd.duration = (int)eggType["Duration"]; + toAdd.regen= (int)eggType["Regen"]; + NPCManager::EggTypes[(int)eggType["Id"]] = toAdd; + } + + // Egg instances + auto eggs = eggData["Eggs"]; + for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) { + auto egg = _egg.value(); + int id = (*nextId)++; + uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"]; + + Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); + NPCManager::NPCs[id] = addEgg; + NPCManager::Eggs[id] = addEgg; + NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); + } + + std::cout << "[INFO] Loaded " <> gruntwork; + + // skyway paths + auto skyway = gruntwork["skyway"]; + for (auto _route = skyway.begin(); _route != skyway.end(); _route++) { + auto route = _route.value(); + std::vector points; + + for (auto _point = route["points"].begin(); _point != route["points"].end(); _point++) { + auto point = _point.value(); + points.push_back(WarpLocation{point["x"], point["y"], point["z"]}); + } + + RunningSkywayRoutes[(int)route["iRouteID"]] = points; + } + + // npc rotations + auto npcRot = gruntwork["rotations"]; + for (auto _rot = npcRot.begin(); _rot != npcRot.end(); _rot++) { + int32_t npcID = _rot.value()["iNPCID"]; + int angle = _rot.value()["iAngle"]; + if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) + continue; // NPC not found + BaseNPC* npc = NPCManager::NPCs[npcID]; + npc->appearanceData.iAngle = angle; + + RunningNPCRotations[npcID] = angle; + } + + // npc map numbers + auto npcMap = gruntwork["instances"]; + for (auto _map = npcMap.begin(); _map != npcMap.end(); _map++) { + int32_t npcID = _map.value()["iNPCID"]; + uint64_t instanceID = _map.value()["iMapNum"]; + if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) + continue; // NPC not found + BaseNPC* npc = NPCManager::NPCs[npcID]; + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, + npc->appearanceData.iZ, instanceID, npc->appearanceData.iAngle); + + RunningNPCMapNumbers[npcID] = instanceID; + } + + // mobs + auto mobs = gruntwork["mobs"]; + for (auto _mob = mobs.begin(); _mob != mobs.end(); _mob++) { + auto mob = _mob.value(); + BaseNPC *npc; + int id = (*nextId)++; + uint64_t instanceID = mob.find("iMapNum") == mob.end() ? INSTANCE_OVERWORLD : (int)mob["iMapNum"]; + + if (NPCManager::NPCData[(int)mob["iNPCType"]]["m_iTeam"] == 2) { + npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"], + NPCManager::NPCData[(int)mob["iNPCType"]], id); + + // re-enable respawning + ((Mob*)npc)->summoned = false; + + MobAI::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc; + } else { + npc = new BaseNPC(mob["iX"], mob["iY"], mob["iZ"], mob["iAngle"], instanceID, mob["iNPCType"], id); + } + + NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; + RunningMobs[npc->appearanceData.iNPC_ID] = npc; + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]); + } + + // mob groups + auto groups = gruntwork["groups"]; + for (auto _group = groups.begin(); _group != groups.end(); _group++) { + auto leader = _group.value(); + auto td = NPCManager::NPCData[(int)leader["iNPCType"]]; + uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"]; + + Mob* tmp = new Mob(leader["iX"], leader["iY"], leader["iZ"], leader["iAngle"], instanceID, leader["iNPCType"], td, *nextId); + + // re-enable respawning + ((Mob*)tmp)->summoned = false; + + NPCManager::NPCs[*nextId] = tmp; + MobAI::Mobs[*nextId] = (Mob*)NPCManager::NPCs[*nextId]; + NPCManager::updateNPCPosition(*nextId, leader["iX"], leader["iY"], leader["iZ"], instanceID, leader["iAngle"]); + + tmp->groupLeader = *nextId; + + (*nextId)++; + + auto followers = leader["aFollowers"]; + if (followers.size() < 5) { + int followerCount = 0; + for (nlohmann::json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) { + auto follower = _fol.value(); + auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]]; + Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, *nextId); + + // re-enable respawning + ((Mob*)tmp)->summoned = false; + + NPCManager::NPCs[*nextId] = tmpFol; + MobAI::Mobs[*nextId] = (Mob*)NPCManager::NPCs[*nextId]; + NPCManager::updateNPCPosition(*nextId, (int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], instanceID, leader["iAngle"]); + + tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"]; + tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"]; + tmpFol->groupLeader = tmp->appearanceData.iNPC_ID; + tmp->groupMember[followerCount++] = *nextId; + + (*nextId)++; + } + } + else { + std::cout << "[WARN] Mob group leader with ID " << *nextId << " has too many followers (" << followers.size() << ")\n"; + } + + RunningGroups[tmp->appearanceData.iNPC_ID] = tmp; // store as running + } + + auto eggs = gruntwork["eggs"]; + for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) { + auto egg = _egg.value(); + int id = (*nextId)++; + uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"]; + + Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); + NPCManager::NPCs[id] = addEgg; + NPCManager::Eggs[id] = addEgg; + NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); + RunningEggs[id] = addEgg; + } + + + std::cout << "[INFO] Loaded gruntwork.json" << std::endl; + } + catch (const std::exception& err) { + std::cerr << "[FATAL] Malformed gruntwork.json file! Reason:" << err.what() << std::endl; + exit(1); + } +} + void TableData::init() { int32_t nextId = 0; @@ -391,526 +913,6 @@ void TableData::init() { NPCManager::nextId = nextId; } -/* - * Load paths from paths JSON. - */ -void TableData::loadPaths(int* nextId) { - try { - std::ifstream inFile(settings::PATHJSON); - nlohmann::json pathData; - - // read file into json - inFile >> pathData; - - // skyway paths - nlohmann::json pathDataSkyway = pathData["skyway"]; - for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) { - constructPathSkyway(skywayPath); - } - std::cout << "[INFO] Loaded " << TransportManager::SkywayPaths.size() << " skyway paths" << std::endl; - - // slider circuit - nlohmann::json pathDataSlider = pathData["slider"]; - // lerp between keyframes - std::queue route; - // initial point - nlohmann::json::iterator _point = pathDataSlider.begin(); // iterator - auto point = _point.value(); - WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords - int stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // arbitrary stop length - // remaining points - for (_point++; _point != pathDataSlider.end(); _point++) { // loop through all point Bs - point = _point.value(); - for (int i = 0; i < stopTime + 1; i++) { // repeat point if it's a stop - route.push(from); // add point A to the queue - } - WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords - // we may need to change this later; right now, the speed is cut before and after stops (no accel) - float curve = 1; - if (stopTime > 0) { // point A is a stop - curve = 0.375f;//2.0f; - } else if (point["stop"]) { // point B is a stop - curve = 0.375f;//0.35f; - } - TransportManager::lerp(&route, from, to, SLIDER_SPEED * curve, 1); // lerp from A to B (arbitrary speed) - from = to; // update point A - stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0; // set stop ticks for next point A - } - // Uniform distance calculation - int passedDistance = 0; - // initial point - int pos = 0; - WarpLocation lastPoint = route.front(); - route.pop(); - route.push(lastPoint); - for (pos = 1; pos < route.size(); pos++) { - WarpLocation point = route.front(); - passedDistance += hypot(point.x - lastPoint.x, point.y - lastPoint.y); - if (passedDistance >= SLIDER_GAP_SIZE) { // space them out uniformaly - passedDistance -= SLIDER_GAP_SIZE; // step down - // spawn a slider - BaseNPC* slider = new BaseNPC(point.x, point.y, point.z, 0, INSTANCE_OVERWORLD, 1, (*nextId)++, NPC_BUS); - NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider; - NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->appearanceData.iX, slider->appearanceData.iY, slider->appearanceData.iZ, INSTANCE_OVERWORLD, 0); - TransportManager::NPCQueues[slider->appearanceData.iNPC_ID] = route; - } - // rotate - route.pop(); - route.push(point); - lastPoint = point; - } - - // npc paths - nlohmann::json pathDataNPC = pathData["npc"]; - /* - for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) { - constructPathNPC(npcPath); - } - */ - - // mob paths - pathDataNPC = pathData["mob"]; - for (nlohmann::json::iterator npcPath = pathDataNPC.begin(); npcPath != pathDataNPC.end(); npcPath++) { - for (auto& pair : MobAI::Mobs) { - if (pair.second->appearanceData.iNPCType == npcPath.value()["iNPCType"]) { - std::cout << "[INFO] Using static path for mob " << pair.second->appearanceData.iNPCType << " with ID " << pair.first << std::endl; - - auto firstPoint = npcPath.value()["points"][0]; - if (firstPoint["iX"] != pair.second->spawnX || firstPoint["iY"] != pair.second->spawnY) { - std::cout << "[FATAL] The first point of the route for mob " << pair.first << - " (type " << pair.second->appearanceData.iNPCType << ") does not correspond with its spawn point." << std::endl; - exit(1); - } - - constructPathNPC(npcPath, pair.first); - pair.second->staticPath = true; - break; // only one NPC per path - } - } - } - std::cout << "[INFO] Loaded " << TransportManager::NPCQueues.size() << " NPC paths" << std::endl; - } - catch (const std::exception& err) { - std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl; - exit(1); - } -} - -/* - * Load drops data from JSON. - * This has to be called after reading xdt because it reffers to ItemData!!! - */ -void TableData::loadDrops() { - try { - std::ifstream inFile(settings::DROPSJSON); - nlohmann::json dropData; - - // read file into json - inFile >> dropData; - - // MobDropChances - nlohmann::json mobDropChances = dropData["MobDropChances"]; - for (nlohmann::json::iterator _dropChance = mobDropChances.begin(); _dropChance != mobDropChances.end(); _dropChance++) { - auto dropChance = _dropChance.value(); - MobDropChance toAdd = {}; - toAdd.dropChance = (int)dropChance["DropChance"]; - for (nlohmann::json::iterator _cratesRatio = dropChance["CratesRatio"].begin(); _cratesRatio != dropChance["CratesRatio"].end(); _cratesRatio++) { - toAdd.cratesRatio.push_back((int)_cratesRatio.value()); - } - ItemManager::MobDropChances[(int)dropChance["Type"]] = toAdd; - } - - // MobDrops - nlohmann::json mobDrops = dropData["MobDrops"]; - for (nlohmann::json::iterator _drop = mobDrops.begin(); _drop != mobDrops.end(); _drop++) { - auto drop = _drop.value(); - MobDrop toAdd = {}; - for (nlohmann::json::iterator _crates = drop["CrateIDs"].begin(); _crates != drop["CrateIDs"].end(); _crates++) { - toAdd.crateIDs.push_back((int)_crates.value()); - } - - toAdd.dropChanceType = (int)drop["DropChance"]; - // Check if DropChance exists - if (ItemManager::MobDropChances.find(toAdd.dropChanceType) == ItemManager::MobDropChances.end()) { - throw TableException(" MobDropChance not found: " + std::to_string((toAdd.dropChanceType))); - } - // Check if number of crates is correct - if (!(ItemManager::MobDropChances[(int)drop["DropChance"]].cratesRatio.size() == toAdd.crateIDs.size())) { - throw TableException(" DropType " + std::to_string((int)drop["DropType"]) + " contains invalid number of crates"); - } - - toAdd.taros = (int)drop["Taros"]; - toAdd.fm = (int)drop["FM"]; - toAdd.boosts = (int)drop["Boosts"]; - ItemManager::MobDrops[(int)drop["DropType"]] = toAdd; - } - - std::cout << "[INFO] Loaded " << ItemManager::MobDrops.size() << " Mob Drop Types"<< std::endl; - - // Rarity Ratios - nlohmann::json rarities = dropData["RarityRatios"]; - for (nlohmann::json::iterator _rarity = rarities.begin(); _rarity != rarities.end(); _rarity++) { - auto rarity = _rarity.value(); - std::vector toAdd; - for (nlohmann::json::iterator _ratio = rarity["Ratio"].begin(); _ratio != rarity["Ratio"].end(); _ratio++){ - toAdd.push_back((int)_ratio.value()); - } - ItemManager::RarityRatios[(int)rarity["Type"]] = toAdd; - } - - // Crates - nlohmann::json crates = dropData["Crates"]; - for (nlohmann::json::iterator _crate = crates.begin(); _crate != crates.end(); _crate++) { - auto crate = _crate.value(); - Crate toAdd; - toAdd.rarityRatioId = (int)crate["RarityRatio"]; - for (nlohmann::json::iterator _itemSet = crate["ItemSets"].begin(); _itemSet != crate["ItemSets"].end(); _itemSet++) { - toAdd.itemSets.push_back((int)_itemSet.value()); - } - ItemManager::Crates[(int)crate["Id"]] = toAdd; - } - - // Crate Items - nlohmann::json items = dropData["Items"]; - int itemCount = 0; - for (nlohmann::json::iterator _item = items.begin(); _item != items.end(); _item++) { - auto item = _item.value(); - std::pair itemSetkey = std::make_pair((int)item["ItemSet"], (int)item["Rarity"]); - std::pair itemDataKey = std::make_pair((int)item["Id"], (int)item["Type"]); - - if (ItemManager::ItemData.find(itemDataKey) == ItemManager::ItemData.end()) { - char buff[255]; - sprintf(buff, "Unknown item with Id %d and Type %d", (int)item["Id"], (int)item["Type"]); - throw TableException(std::string(buff)); - } - - std::map, ItemManager::Item>::iterator toAdd = ItemManager::ItemData.find(itemDataKey); - - // if item collection doesn't exist, start a new one - if (ItemManager::CrateItems.find(itemSetkey) == ItemManager::CrateItems.end()) { - std::vector, ItemManager::Item>::iterator> vector; - vector.push_back(toAdd); - ItemManager::CrateItems[itemSetkey] = vector; - } else // else add a new element to existing collection - ItemManager::CrateItems[itemSetkey].push_back(toAdd); - - itemCount++; - } - -#ifdef ACADEMY - nlohmann::json capsules = dropData["NanoCapsules"]; - - for (nlohmann::json::iterator _capsule = capsules.begin(); _capsule != capsules.end(); _capsule++) { - auto capsule = _capsule.value(); - ItemManager::NanoCapsules[(int)capsule["Crate"]] = (int)capsule["Nano"]; - } -#endif - nlohmann::json codes = dropData["CodeItems"]; - for (nlohmann::json::iterator _code = codes.begin(); _code != codes.end(); _code++) { - auto code = _code.value(); - std::string codeStr = code["Code"]; - std::pair item = std::make_pair((int)code["Id"], (int)code["Type"]); - - if (ItemManager::CodeItems.find(codeStr) == ItemManager::CodeItems.end()) - ItemManager::CodeItems[codeStr] = std::vector>(); - - ItemManager::CodeItems[codeStr].push_back(item); - } - - std::cout << "[INFO] Loaded " << ItemManager::Crates.size() << " Crates containing " - << itemCount << " items" << std::endl; - - // Racing rewards - nlohmann::json racing = dropData["Racing"]; - for (nlohmann::json::iterator _race = racing.begin(); _race != racing.end(); _race++) { - auto race = _race.value(); - int raceEPID = race["EPID"]; - - // find the instance data corresponding to the EPID - int EPMap = -1; - for (auto it = RacingManager::EPData.begin(); it != RacingManager::EPData.end(); it++) { - if (it->second.EPID == raceEPID) { - EPMap = it->first; - } - } - - if (EPMap == -1) { // not found - std::cout << "[WARN] EP with ID " << raceEPID << " not found, skipping" << std::endl; - continue; - } - - // time limit isn't stored in the XDT, so we include it in the reward table instead - RacingManager::EPData[EPMap].maxTime = race["TimeLimit"]; - - // score cutoffs - std::vector rankScores; - for (nlohmann::json::iterator _rankScore = race["RankScores"].begin(); _rankScore != race["RankScores"].end(); _rankScore++) { - rankScores.push_back((int)_rankScore.value()); - } - - // reward IDs for each rank - std::vector rankRewards; - for (nlohmann::json::iterator _rankReward = race["Rewards"].begin(); _rankReward != race["Rewards"].end(); _rankReward++) { - rankRewards.push_back((int)_rankReward.value()); - } - - if (rankScores.size() != 5 || rankScores.size() != rankRewards.size()) { - char buff[255]; - sprintf(buff, "Race in EP %d doesn't have exactly 5 score/reward pairs", raceEPID); - throw TableException(std::string(buff)); - } - - RacingManager::EPRewards[raceEPID] = std::make_pair(rankScores, rankRewards); - } - - std::cout << "[INFO] Loaded rewards for " << RacingManager::EPRewards.size() << " IZ races" << std::endl; - - } - catch (const std::exception& err) { - std::cerr << "[FATAL] Malformed drops.json file! Reason:" << err.what() << std::endl; - exit(1); - } -} - -void TableData::loadEggs(int32_t* nextId) { - try { - std::ifstream inFile(settings::EGGSJSON); - nlohmann::json eggData; - - // read file into json - inFile >> eggData; - - // EggTypes - nlohmann::json eggTypes = eggData["EggTypes"]; - for (nlohmann::json::iterator _eggType = eggTypes.begin(); _eggType != eggTypes.end(); _eggType++) { - auto eggType = _eggType.value(); - EggType toAdd = {}; - toAdd.dropCrateId = (int)eggType["DropCrateId"]; - toAdd.effectId = (int)eggType["EffectId"]; - toAdd.duration = (int)eggType["Duration"]; - toAdd.regen= (int)eggType["Regen"]; - NPCManager::EggTypes[(int)eggType["Id"]] = toAdd; - } - - // Egg instances - auto eggs = eggData["Eggs"]; - for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) { - auto egg = _egg.value(); - int id = (*nextId)++; - uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"]; - - Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); - NPCManager::NPCs[id] = addEgg; - NPCManager::Eggs[id] = addEgg; - NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); - } - - std::cout << "[INFO] Loaded " < points; - nlohmann::json::iterator _point = pathPoints.begin(); - auto point = _point.value(); - WarpLocation last = { point["iX"] , point["iY"] , point["iZ"] }; // start pos - // use some for loop trickery; start position should not be a point - for (_point++; _point != pathPoints.end(); _point++) { - point = _point.value(); - WarpLocation coords = { point["iX"] , point["iY"] , point["iZ"] }; - TransportManager::lerp(&points, last, coords, pathData["iMonkeySpeed"]); - points.push(coords); // add keyframe to the queue - last = coords; // update start pos - } - TransportManager::SkywayPaths[pathData["iRouteID"]] = points; -} - -void TableData::constructPathNPC(nlohmann::json::iterator _pathData, int32_t id) { - auto pathData = _pathData.value(); - // Interpolate - nlohmann::json pathPoints = pathData["points"]; - std::queue points; - nlohmann::json::iterator _point = pathPoints.begin(); - auto point = _point.value(); - WarpLocation from = { point["iX"] , point["iY"] , point["iZ"] }; // point A coords - int stopTime = point["stop"]; - for (_point++; _point != pathPoints.end(); _point++) { // loop through all point Bs - point = _point.value(); - for(int i = 0; i < stopTime + 1; i++) // repeat point if it's a stop - points.push(from); // add point A to the queue - WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords - TransportManager::lerp(&points, from, to, pathData["iBaseSpeed"]); // lerp from A to B - from = to; // update point A - stopTime = point["stop"]; - } - - if (id == 0) - id = pathData["iNPCID"]; - - TransportManager::NPCQueues[id] = points; -} - -// load gruntwork output; if it exists -void TableData::loadGruntwork(int32_t *nextId) { - try { - std::ifstream inFile(settings::GRUNTWORKJSON); - nlohmann::json gruntwork; - - // skip if there's no gruntwork to load - if (inFile.fail()) - return; - - inFile >> gruntwork; - - // skyway paths - auto skyway = gruntwork["skyway"]; - for (auto _route = skyway.begin(); _route != skyway.end(); _route++) { - auto route = _route.value(); - std::vector points; - - for (auto _point = route["points"].begin(); _point != route["points"].end(); _point++) { - auto point = _point.value(); - points.push_back(WarpLocation{point["x"], point["y"], point["z"]}); - } - - RunningSkywayRoutes[(int)route["iRouteID"]] = points; - } - - // npc rotations - auto npcRot = gruntwork["rotations"]; - for (auto _rot = npcRot.begin(); _rot != npcRot.end(); _rot++) { - int32_t npcID = _rot.value()["iNPCID"]; - int angle = _rot.value()["iAngle"]; - if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) - continue; // NPC not found - BaseNPC* npc = NPCManager::NPCs[npcID]; - npc->appearanceData.iAngle = angle; - - RunningNPCRotations[npcID] = angle; - } - - // npc map numbers - auto npcMap = gruntwork["instances"]; - for (auto _map = npcMap.begin(); _map != npcMap.end(); _map++) { - int32_t npcID = _map.value()["iNPCID"]; - uint64_t instanceID = _map.value()["iMapNum"]; - if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) - continue; // NPC not found - BaseNPC* npc = NPCManager::NPCs[npcID]; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, - npc->appearanceData.iZ, instanceID, npc->appearanceData.iAngle); - - RunningNPCMapNumbers[npcID] = instanceID; - } - - // mobs - auto mobs = gruntwork["mobs"]; - for (auto _mob = mobs.begin(); _mob != mobs.end(); _mob++) { - auto mob = _mob.value(); - BaseNPC *npc; - int id = (*nextId)++; - uint64_t instanceID = mob.find("iMapNum") == mob.end() ? INSTANCE_OVERWORLD : (int)mob["iMapNum"]; - - if (NPCManager::NPCData[(int)mob["iNPCType"]]["m_iTeam"] == 2) { - npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"], - NPCManager::NPCData[(int)mob["iNPCType"]], id); - - // re-enable respawning - ((Mob*)npc)->summoned = false; - - MobAI::Mobs[npc->appearanceData.iNPC_ID] = (Mob*)npc; - } else { - npc = new BaseNPC(mob["iX"], mob["iY"], mob["iZ"], mob["iAngle"], instanceID, mob["iNPCType"], id); - } - - NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; - TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]); - } - - // mob groups - auto groups = gruntwork["groups"]; - for (auto _group = groups.begin(); _group != groups.end(); _group++) { - auto leader = _group.value(); - auto td = NPCManager::NPCData[(int)leader["iNPCType"]]; - uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"]; - - Mob* tmp = new Mob(leader["iX"], leader["iY"], leader["iZ"], leader["iAngle"], instanceID, leader["iNPCType"], td, *nextId); - - // re-enable respawning - ((Mob*)tmp)->summoned = false; - - NPCManager::NPCs[*nextId] = tmp; - MobAI::Mobs[*nextId] = (Mob*)NPCManager::NPCs[*nextId]; - NPCManager::updateNPCPosition(*nextId, leader["iX"], leader["iY"], leader["iZ"], instanceID, leader["iAngle"]); - - tmp->groupLeader = *nextId; - - (*nextId)++; - - auto followers = leader["aFollowers"]; - if (followers.size() < 5) { - int followerCount = 0; - for (nlohmann::json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) { - auto follower = _fol.value(); - auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]]; - Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, *nextId); - - // re-enable respawning - ((Mob*)tmp)->summoned = false; - - NPCManager::NPCs[*nextId] = tmpFol; - MobAI::Mobs[*nextId] = (Mob*)NPCManager::NPCs[*nextId]; - NPCManager::updateNPCPosition(*nextId, (int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], instanceID, leader["iAngle"]); - - tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"]; - tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"]; - tmpFol->groupLeader = tmp->appearanceData.iNPC_ID; - tmp->groupMember[followerCount++] = *nextId; - - (*nextId)++; - } - } - else { - std::cout << "[WARN] Mob group leader with ID " << *nextId << " has too many followers (" << followers.size() << ")\n"; - } - - TableData::RunningGroups[tmp->appearanceData.iNPC_ID] = tmp; // store as running - } - - auto eggs = gruntwork["eggs"]; - for (auto _egg = eggs.begin(); _egg != eggs.end(); _egg++) { - auto egg = _egg.value(); - int id = (*nextId)++; - uint64_t instanceID = egg.find("iMapNum") == egg.end() ? INSTANCE_OVERWORLD : (int)egg["iMapNum"]; - - Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); - NPCManager::NPCs[id] = addEgg; - NPCManager::Eggs[id] = addEgg; - NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); - TableData::RunningEggs[id] = addEgg; - } - - - std::cout << "[INFO] Loaded gruntwork.json" << std::endl; - } - catch (const std::exception& err) { - std::cerr << "[FATAL] Malformed gruntwork.json file! Reason:" << err.what() << std::endl; - exit(1); - } -} - // write gruntwork output to file void TableData::flush() { std::ofstream file(settings::GRUNTWORKJSON); diff --git a/src/TableData.hpp b/src/TableData.hpp index 12b7f62..61bb858 100644 --- a/src/TableData.hpp +++ b/src/TableData.hpp @@ -13,13 +13,5 @@ namespace TableData { extern std::map RunningEggs; void init(); - void cleanup(); - void loadGruntwork(int32_t*); void flush(); - - void loadPaths(int*); - void loadDrops(); - void loadEggs(int32_t* nextId); - void constructPathSkyway(nlohmann::json::iterator); - void constructPathNPC(nlohmann::json::iterator, int id=0); } diff --git a/src/Trading.cpp b/src/Trading.cpp index 12bdccf..98d639f 100644 --- a/src/Trading.cpp +++ b/src/Trading.cpp @@ -1,7 +1,9 @@ #include "Trading.hpp" #include "PlayerManager.hpp" -bool doTrade(Player* plr, Player* plr2) { +using namespace Trading; + +static bool doTrade(Player* plr, Player* plr2) { // init dummy inventories sItemBase plrInven[AINVEN_COUNT]; sItemBase plr2Inven[AINVEN_COUNT]; @@ -429,4 +431,4 @@ void Trading::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, tradeRegisterItem); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, tradeUnregisterItem); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, tradeRegisterCash); -} \ No newline at end of file +} diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 031f95d..b953b4d 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -10,20 +10,15 @@ #include #include +using namespace TransportManager; + std::map TransportManager::Routes; std::map TransportManager::Locations; std::map> TransportManager::SkywayPaths; std::unordered_map> TransportManager::SkywayQueues; std::unordered_map> TransportManager::NPCQueues; -void TransportManager::init() { - REGISTER_SHARD_TIMER(tickTransportationSystem, 1000); - - REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION, transportRegisterLocationHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION, transportWarpHandler); -} - -void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data) { +static void transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION)) return; // malformed packet @@ -99,7 +94,7 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, sizeof(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC)); } -void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) { +static void transportWarpHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION)) return; // malformed packet @@ -196,16 +191,11 @@ void TransportManager::testMssRoute(CNSocket *sock, std::vector* r SkywayQueues[sock] = path; } -void TransportManager::tickTransportationSystem(CNServer* serv, time_t currTime) { - stepNPCPathing(); - stepSkywaySystem(); -} - /* * Go through every socket that has broomstick points queued up, and advance to the next point. * If the player has disconnected or finished the route, clean up and remove them from the queue. */ -void TransportManager::stepSkywaySystem() { +static void stepSkywaySystem() { // using an unordered map so we can remove finished players in one iteration std::unordered_map>::iterator it = SkywayQueues.begin(); @@ -254,7 +244,7 @@ void TransportManager::stepSkywaySystem() { } } -void TransportManager::stepNPCPathing() { +static void stepNPCPathing() { // all NPC pathing queues std::unordered_map>::iterator it = NPCQueues.begin(); @@ -334,6 +324,11 @@ void TransportManager::stepNPCPathing() { } } +static void tickTransportationSystem(CNServer* serv, time_t currTime) { + stepNPCPathing(); + stepSkywaySystem(); +} + /* * Linearly interpolate between two points and insert the results into a queue. */ @@ -355,3 +350,10 @@ void TransportManager::lerp(std::queue* queue, WarpLocation start, void TransportManager::lerp(std::queue* queue, WarpLocation start, WarpLocation end, int gapSize) { lerp(queue, start, end, gapSize, 1); } + +void TransportManager::init() { + REGISTER_SHARD_TIMER(tickTransportationSystem, 1000); + + REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION, transportRegisterLocationHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION, transportWarpHandler); +} diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index c274542..24e8c2b 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -28,15 +28,8 @@ namespace TransportManager { void init(); - void transportRegisterLocationHandler(CNSocket*, CNPacketData*); - void transportWarpHandler(CNSocket*, CNPacketData*); - void testMssRoute(CNSocket *sock, std::vector* route); - void tickTransportationSystem(CNServer*, time_t); - void stepNPCPathing(); - void stepSkywaySystem(); - void lerp(std::queue*, WarpLocation, WarpLocation, int, float); void lerp(std::queue*, WarpLocation, WarpLocation, int); } diff --git a/src/Vendor.cpp b/src/Vendor.cpp index d561368..681f702 100644 --- a/src/Vendor.cpp +++ b/src/Vendor.cpp @@ -1,8 +1,10 @@ #include "Vendor.hpp" +using namespace Vendor; + std::map> Vendor::VendorTables; -void Vendor::vendorBuy(CNSocket* sock, CNPacketData* data) { +static void vendorBuy(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY)) return; // malformed packet @@ -57,7 +59,7 @@ void Vendor::vendorBuy(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_SUCC)); } -void Vendor::vendorSell(CNSocket* sock, CNPacketData* data) { +static void vendorSell(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_ITEM_SELL)) return; // malformed packet @@ -123,7 +125,7 @@ void Vendor::vendorSell(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC)); } -void Vendor::vendorBuyback(CNSocket* sock, CNPacketData* data) { +static void vendorBuyback(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY)) return; // malformed packet @@ -202,7 +204,7 @@ void Vendor::vendorBuyback(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC)); } -void Vendor::vendorTable(CNSocket* sock, CNPacketData* data) { +static void vendorTable(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE)) return; // malformed packet @@ -234,7 +236,7 @@ void Vendor::vendorTable(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC)); } -void Vendor::vendorStart(CNSocket* sock, CNPacketData* data) { +static void vendorStart(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_START)) return; // malformed packet @@ -247,7 +249,7 @@ void Vendor::vendorStart(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_START_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_START_SUCC)); } -void Vendor::vendorBuyBattery(CNSocket* sock, CNPacketData* data) { +static void vendorBuyBattery(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_BATTERY_BUY)) return; // malformed packet @@ -284,7 +286,7 @@ void Vendor::vendorBuyBattery(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC)); } -void vendorCombineItems(CNSocket* sock, CNPacketData* data) { +static void vendorCombineItems(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_COMBINATION)) return; // malformed packet diff --git a/src/Vendor.hpp b/src/Vendor.hpp index f6a7b1d..09576ac 100644 --- a/src/Vendor.hpp +++ b/src/Vendor.hpp @@ -15,11 +15,4 @@ namespace Vendor { extern std::map> VendorTables; void init(); - - void vendorStart(CNSocket* sock, CNPacketData* data); - void vendorTable(CNSocket* sock, CNPacketData* data); - void vendorBuy(CNSocket* sock, CNPacketData* data); - void vendorSell(CNSocket* sock, CNPacketData* data); - void vendorBuyback(CNSocket* sock, CNPacketData* data); - void vendorBuyBattery(CNSocket* sock, CNPacketData* data); } diff --git a/src/main.cpp b/src/main.cpp index 0028896..6d19cf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,6 @@ #include "BuddyManager.hpp" #include "db/Database.hpp" #include "TableData.hpp" -#include "ChunkManager.hpp" #include "GroupManager.hpp" #include "Monitor.hpp" #include "RacingManager.hpp"