diff --git a/src/Player.hpp b/src/Player.hpp index 04c8e45..6bda172 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -25,6 +25,8 @@ struct Player { int equippedNanos[3]; int activeNano; // active nano (index into Nanos) int8_t iPCState; + int32_t iWarpLocationFlag; + int64_t aSkywayLocationFlag[2]; int x, y, z, angle; sItemBase Equip[AEQUIP_COUNT]; diff --git a/src/TableData.cpp b/src/TableData.cpp index 3991c70..8486dee 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -1,5 +1,6 @@ #include "TableData.hpp" #include "NPCManager.hpp" +#include "TransportManager.hpp" #include "settings.hpp" #include "MissionManager.hpp" @@ -81,6 +82,23 @@ void TableData::init() { std::cout << "[INFO] Populated " << NPCManager::Warps.size() << " Warps" << std::endl; + // load transport routes and locations + nlohmann::json transRouteData = xdtData["m_pTransportationTable"]["m_pTransportationData"]; + nlohmann::json transLocData = xdtData["m_pTransportationTable"]["m_pTransportationWarpLocation"]; + + for (nlohmann::json::iterator tLoc = transLocData.begin(); tLoc != transLocData.end(); tLoc++) { + TransportLocation transLoc = { tLoc.value()["m_iNPCID"], tLoc.value()["m_iXpos"], tLoc.value()["m_iYpos"], tLoc.value()["m_iZpos"] }; + TransportManager::Locations[tLoc.value()["m_iLocationID"]] = transLoc; + } + std::cout << "[INFO] Loaded " << TransportManager::Locations.size() << " S.C.A.M.P.E.R. locations" << std::endl; // TODO: Skyway operates differently + + for (nlohmann::json::iterator tRoute = transRouteData.begin(); tRoute != transRouteData.end(); tRoute++) { + TransportRoute transRoute = { tRoute.value()["m_iMoveType"], tRoute.value()["m_iStartLocation"], tRoute.value()["m_iEndLocation"], + tRoute.value()["m_iCost"] , tRoute.value()["m_iSpeed"], tRoute.value()["m_iRouteNum"] }; + TransportManager::Routes[tRoute.value()["m_iVehicleID"]] = transRoute; + } + std::cout << "[INFO] Loaded " << TransportManager::Routes.size() << " transportation routes" << std::endl; + // load mission-related data nlohmann::json tasks = xdtData["m_pMissionTable"]["m_pMissionData"]; diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 135aa7d..a183bac 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -3,16 +3,142 @@ #include "PlayerManager.hpp" #include "TransportManager.hpp" + +std::map TransportManager::Routes; +std::map TransportManager::Locations; + void TransportManager::init() { 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) { + if (data->size != sizeof(sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION)) + return; // malformed packet + sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION* transport = (sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + std::cout << "request to register transport, eTT " << transport->eTT << ", locID " << transport->iLocationID << ", npc " << transport->iNPC_ID << std::endl; + if (transport->eTT == 1) { // S.C.A.M.P.E.R. + if (transport->iLocationID < 1 || transport->iLocationID > 31) { // sanity check + std::cout << "[WARN] S.C.A.M.P.E.R. location ID " << transport->iLocationID << " is out of bounds" << std::endl; + INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, failResp); + + failResp.eTT = transport->eTT; + failResp.iErrorCode = 0; // TODO: review what error code to use here + failResp.iLocationID = transport->iLocationID; + + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, sizeof(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL)); + return; + } + + // update registration bitfield using bit shifting + bitwise or + plr->iWarpLocationFlag |= (1 << (transport->iLocationID - 1)); + } else if (transport->eTT == 2) { // Monkey Skyway System + if (transport->iLocationID < 1 || transport->iLocationID > 127) { // sanity check + std::cout << "[WARN] Skyway location ID " << transport->iLocationID << " is out of bounds" << std::endl; + INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, failResp); + + failResp.eTT = transport->eTT; + failResp.iErrorCode = 0; // TODO: review what error code to use here + failResp.iLocationID = transport->iLocationID; + + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, sizeof(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL)); + return; + } + + /* + * assuming the two bitfields are just stuck together to make a longer one... do a similar operation, but on the respective integer + * this approach seems to work with initial testing, but we have yet to see a monkey ID greater than 63. + */ + plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1 << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1)); + } else { + std::cout << "[WARN] Unknown mode of transport; eTT = " << transport->eTT << std::endl; + INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, failResp); + + failResp.eTT = transport->eTT; + failResp.iErrorCode = 0; // TODO: review what error code to use here + failResp.iLocationID = transport->iLocationID; + + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, sizeof(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL)); + return; + } INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, resp); + + // response parameters resp.eTT = transport->eTT; resp.iLocationID = transport->iLocationID; + resp.iWarpLocationFlag = plr->iWarpLocationFlag; + resp.aWyvernLocationFlag[0] = plr->aSkywayLocationFlag[0]; + resp.aWyvernLocationFlag[1] = plr->aSkywayLocationFlag[1]; 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) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION)) + return; // malformed packet + + sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION* req = (sP_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + /* + * req: + * eIL -- inventory type + * iNPC_ID -- the ID of the NPC who is warping you + * iTransporationID -- iVehicleID + * iSlotNum -- inventory slot number + */ + + if (Routes.find(req->iTransporationID) == Routes.end() || Locations.find(Routes[req->iTransporationID].end) == Locations.end() + || Routes[req->iTransporationID].cost > plr->money) { // sanity check + INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL, failResp); + + failResp.iErrorCode = 0; // TODO: error code + failResp.iTransportationID = req->iTransporationID; + + sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL)); + return; + } + + TransportRoute route = Routes[req->iTransporationID]; + + plr->money -= route.cost; + + TransportLocation target = Locations[route.end]; + switch (route.type) + { + case 1: // S.C.A.M.P.E.R. + plr->x = target.x; + plr->y = target.y; + plr->z = target.z; + break; + case 2: // Monkey Skyway + // TODO: implement + break; + default: + std::cout << "[WARN] Unknown tranportation type " << route.type << std::endl; + break; + } + + INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, resp); + + // response parameters + resp.eTT = route.type; + resp.iCandy = plr->money; + resp.iX = plr->x; + resp.iY = plr->y; + resp.iZ = plr->z; + + /* + * Not strictly necessary since there isn't a valid SCAMPER that puts you in the + * same map tile you were already in, but we might as well force an NPC reload. + */ + PlayerView& plrv = PlayerManager::players[sock]; + plrv.viewable.clear(); + plrv.viewableNPCs.clear(); + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC)); +} diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index 963925d..7ad34b0 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -2,8 +2,20 @@ #include "CNShardServer.hpp" +struct TransportRoute { + int type, start, end, cost, mssSpeed, mssRouteNum; +}; + +struct TransportLocation { + int npcID, x, y, z; +}; + namespace TransportManager { + extern std::map Routes; + extern std::map Locations; + void init(); void transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data); + void transportWarpHandler(CNSocket* sock, CNPacketData* data); }