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"