diff --git a/src/MobManager.cpp b/src/MobManager.cpp index e230380..4b8f2c0 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -215,6 +215,13 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { s->sendPacket(&pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); } } + + // if it was summoned, remove it permanently + if (mob->summoned) { + std::cout << "[INFO] Deallocating killed summoned mob" << std::endl; + NPCManager::removeNPC(mob->appearanceData.iNPC_ID); + return; + } } if (mob->killedTime != 0 && currTime - mob->killedTime < mob->regenTime * 100) diff --git a/src/MobManager.hpp b/src/MobManager.hpp index 4562214..72e1844 100644 --- a/src/MobManager.hpp +++ b/src/MobManager.hpp @@ -20,7 +20,7 @@ enum class MobState { struct Mob : public BaseNPC { // general MobState state; - const int maxHealth; + int maxHealth; int spawnX; int spawnY; int spawnZ; @@ -28,6 +28,7 @@ struct Mob : public BaseNPC { // dead time_t killedTime = 0; time_t regenTime; + bool summoned = false; bool despawned = false; // for the sake of death animations // roaming @@ -41,8 +42,8 @@ struct Mob : public BaseNPC { // temporary; until we're sure what's what nlohmann::json data; - Mob(int x, int y, int z, int type, int hp, int angle, nlohmann::json d) - : BaseNPC(x, y, z, type), maxHealth(hp) { + Mob(int x, int y, int z, int type, int hp, int angle, nlohmann::json d, int32_t id) + : BaseNPC(x, y, z, type, id), maxHealth(hp) { state = MobState::ROAMING; data = d; @@ -58,7 +59,17 @@ struct Mob : public BaseNPC { // NOTE: there appear to be discrepancies in the dump appearanceData.iHP = maxHealth; + + npcClass = NPC_MOB; } + + // constructor for /summon + Mob(int x, int y, int z, int type, nlohmann::json d, int32_t id) + : Mob(x, y, z, type, 0, 0, d, id) { + summoned = true; // will be despawned and deallocated when killed + appearanceData.iHP = maxHealth = d["m_iHP"]; + } + ~Mob() {} auto operator[](std::string s) { diff --git a/src/NPC.hpp b/src/NPC.hpp index c2db348..be8b263 100644 --- a/src/NPC.hpp +++ b/src/NPC.hpp @@ -8,7 +8,7 @@ public: NPCClass npcClass; BaseNPC() {}; - BaseNPC(int x, int y, int z, int type) { + BaseNPC(int x, int y, int z, int type, int id) { appearanceData.iX = x; appearanceData.iY = y; appearanceData.iZ = z; @@ -17,11 +17,9 @@ public: appearanceData.iAngle = 0; appearanceData.iConditionBitFlag = 0; appearanceData.iBarkerType = 0; - - // hopefully no collisions happen :eyes: - appearanceData.iNPC_ID = (int32_t)rand(); + appearanceData.iNPC_ID = id; }; - BaseNPC(int x, int y, int z, int type, NPCClass classType) : BaseNPC(x, y, z, type) { + BaseNPC(int x, int y, int z, int type, int id, NPCClass classType) : BaseNPC(x, y, z, type, id) { npcClass = classType; } }; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 45249f1..fd51aeb 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -8,13 +8,21 @@ #include #include #include +#include #include "contrib/JSON.hpp" std::map NPCManager::NPCs; std::map NPCManager::Warps; std::vector NPCManager::RespawnPoints; +nlohmann::json NPCManager::NPCData; +/* + * Initialized at the end of TableData::init(). + * This allows us to summon and kill mobs in arbitrary order without + * NPC ID collisions. + */ +int32_t NPCManager::nextId; void NPCManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler); @@ -481,15 +489,21 @@ void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { if (plr->accountLevel > 30 || req->iNPCType >= 3314) return; - resp.NPCAppearanceData.iNPC_ID = NPCs.size()+1; + int team = NPCData[req->iNPCType]["m_iTeam"]; + + assert(nextId < INT32_MAX); + resp.NPCAppearanceData.iNPC_ID = nextId++; resp.NPCAppearanceData.iNPCType = req->iNPCType; - resp.NPCAppearanceData.iHP = 1000; // TODO: placeholder + resp.NPCAppearanceData.iHP = 1000; resp.NPCAppearanceData.iX = plr->x; resp.NPCAppearanceData.iY = plr->y; resp.NPCAppearanceData.iZ = plr->z; - NPCs[resp.NPCAppearanceData.iNPC_ID] = new BaseNPC(plr->x, plr->y, plr->z, req->iNPCType); - NPCs[resp.NPCAppearanceData.iNPC_ID]->appearanceData.iNPC_ID = resp.NPCAppearanceData.iNPC_ID; + if (team == 2) { + NPCs[resp.NPCAppearanceData.iNPC_ID] = new Mob(plr->x, plr->y, plr->z, req->iNPCType, NPCData[req->iNPCType], resp.NPCAppearanceData.iNPC_ID); + MobManager::Mobs[resp.NPCAppearanceData.iNPC_ID] = (Mob*)NPCs[resp.NPCAppearanceData.iNPC_ID]; + } else + NPCs[resp.NPCAppearanceData.iNPC_ID] = new BaseNPC(plr->x, plr->y, plr->z, req->iNPCType, resp.NPCAppearanceData.iNPC_ID); ChunkManager::addNPC(plr->x, plr->y, resp.NPCAppearanceData.iNPC_ID); } diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index aee6553..c48f687 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -4,6 +4,8 @@ #include "PlayerManager.hpp" #include "NPC.hpp" +#include "contrib/JSON.hpp" + #include #include @@ -18,6 +20,8 @@ namespace NPCManager { extern std::map NPCs; extern std::map Warps; extern std::vector RespawnPoints; + extern nlohmann::json NPCData; + extern int32_t nextId; void init(); void addNPC(std::vector viewableChunks, int32_t); diff --git a/src/TableData.cpp b/src/TableData.cpp index e4cf50b..ec1fe3a 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -12,7 +12,7 @@ #include void TableData::init() { - int i = 0; + int32_t nextId = 0; // load NPCs from NPC.json try { @@ -24,14 +24,11 @@ void TableData::init() { for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) { auto npc = _npc.value(); - BaseNPC *tmp = new BaseNPC(npc["x"], npc["y"], npc["z"], npc["id"]); + BaseNPC *tmp = new BaseNPC(npc["x"], npc["y"], npc["z"], npc["id"], nextId); - // Temporary fix, IDs will be pulled from json later - tmp->appearanceData.iNPC_ID = i; - - NPCManager::NPCs[i] = tmp; - ChunkManager::addNPC(npc["x"], npc["y"], i); - i++; + NPCManager::NPCs[nextId] = tmp; + ChunkManager::addNPC(npc["x"], npc["y"], nextId); + nextId++; if (npc["id"] == 641 || npc["id"] == 642) NPCManager::RespawnPoints.push_back({ npc["x"], npc["y"], ((int)npc["z"]) + RESURRECT_HEIGHT }); @@ -52,6 +49,9 @@ void TableData::init() { // read file into json infile >> xdtData; + // data we'll need for summoned mobs + NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"]; + try { // load warps nlohmann::json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"]; @@ -74,7 +74,7 @@ void TableData::init() { TransportLocation transLoc = { tLoc["m_iNPCID"], tLoc["m_iXpos"], tLoc["m_iYpos"], tLoc["m_iZpos"] }; TransportManager::Locations[tLoc["m_iLocationID"]] = transLoc; } - std::cout << "[INFO] Loaded " << TransportManager::Locations.size() << " S.C.A.M.P.E.R. locations" << std::endl; // TODO: Skyway operates differently + std::cout << "[INFO] Loaded " << TransportManager::Locations.size() << " S.C.A.M.P.E.R. locations" << std::endl; for (nlohmann::json::iterator _tRoute = transRouteData.begin(); _tRoute != transRouteData.end(); _tRoute++) { auto tRoute = _tRoute.value(); @@ -165,21 +165,16 @@ void TableData::init() { // read file into json inFile >> npcData; - nlohmann::json npcTableData = xdtData["m_pNpcTable"]["m_pNpcData"]; - for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) { auto npc = _npc.value(); - auto td = npcTableData[(int)npc["iNPCType"]]; - Mob *tmp = new Mob(npc["iX"], npc["iY"], npc["iZ"], npc["iNPCType"], npc["iHP"], npc["iAngle"], td); + auto td = NPCManager::NPCData[(int)npc["iNPCType"]]; + Mob *tmp = new Mob(npc["iX"], npc["iY"], npc["iZ"], npc["iNPCType"], npc["iHP"], npc["iAngle"], td, nextId); - // Temporary fix, IDs will be pulled from json later - tmp->appearanceData.iNPC_ID = i; + NPCManager::NPCs[nextId] = tmp; + MobManager::Mobs[nextId] = (Mob*)NPCManager::NPCs[nextId]; + ChunkManager::addNPC(npc["iX"], npc["iY"], nextId); - NPCManager::NPCs[i] = tmp; - MobManager::Mobs[i] = (Mob*)NPCManager::NPCs[i]; - ChunkManager::addNPC(npc["iX"], npc["iY"], i); - - i++; + nextId++; } std::cout << "[INFO] Populated " << NPCManager::NPCs.size() << " NPCs" << std::endl; @@ -187,6 +182,8 @@ void TableData::init() { catch (const std::exception& err) { std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl; } + + NPCManager::nextId = nextId; } /* diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 67f97ae..5b3d3fa 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -18,7 +18,7 @@ void TransportManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION, transportRegisterLocationHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION, transportWarpHandler); - BaseNPC* bus = new BaseNPC(220447, 162431, -3650, 1, NPC_BUS); + BaseNPC* bus = new BaseNPC(220447, 162431, -3650, 1, NPCManager::nextId++, NPC_BUS); NPCManager::NPCs[bus->appearanceData.iNPC_ID] = bus; ChunkManager::addNPC(bus->appearanceData.iX, bus->appearanceData.iY, bus->appearanceData.iNPC_ID); std::queue busPoints; diff --git a/suppr.txt b/suppr.txt index 14d8a4e..781647b 100644 --- a/suppr.txt +++ b/suppr.txt @@ -1,4 +1,5 @@ leak:TableData::init leak:ChunkManager::addPlayer leak:ChunkManager::addNPC +leak:NPCManager::npcSummonHandler leak:nlohmann::basic_json