diff --git a/src/ChunkManager.cpp b/src/ChunkManager.cpp index e130461..a9fcec1 100644 --- a/src/ChunkManager.cpp +++ b/src/ChunkManager.cpp @@ -20,8 +20,6 @@ void ChunkManager::addNPC(int posX, int posY, int32_t id) { Chunk* chunk = chunks[pos]; chunk->NPCs.insert(id); - - NPCManager::addNPC(grabChunks(pos), id); } void ChunkManager::addPlayer(int posX, int posY, CNSocket* sock) { diff --git a/src/MobManager.cpp b/src/MobManager.cpp index 38b6945..22bc83a 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -211,7 +211,7 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { // 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); + NPCManager::destroyNPC(mob->appearanceData.iNPC_ID); return; } } @@ -279,6 +279,8 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->target->plr->x, mob->target->plr->y, speed); + NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ); + INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); pkt.iNPC_ID = mob->appearanceData.iNPC_ID; @@ -330,6 +332,8 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, farX, farY, speed); + NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ); + pkt.iNPC_ID = mob->appearanceData.iNPC_ID; pkt.iSpeed = speed; pkt.iToX = mob->appearanceData.iX = targ.first; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 1a47170..4a58aff 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -38,6 +38,38 @@ void NPCManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_COMBINATION, npcCombineItems); } +void NPCManager::removeNPC(std::vector viewableChunks, int32_t id) { + BaseNPC* npc = NPCs[id]; + + switch (npc->npcClass) { + case NPC_BUS: + INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); + exitBusData.eTT = 3; + exitBusData.iT_ID = id; + + for (Chunk* chunk : viewableChunks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); + } + } + break; + default: + // create struct + INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); + exitData.iNPC_ID = id; + + // remove it from the clients + for (Chunk* chunk : viewableChunks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); + } + } + break; + } +} + void NPCManager::addNPC(std::vector viewableChunks, int32_t id) { BaseNPC* npc = NPCs[id]; @@ -68,7 +100,7 @@ void NPCManager::addNPC(std::vector viewableChunks, int32_t id) { } } -void NPCManager::removeNPC(int32_t id) { +void NPCManager::destroyNPC(int32_t id) { // sanity check if (NPCs.find(id) == NPCs.end()) { std::cout << "npc not found : " << id << std::endl; @@ -77,51 +109,23 @@ void NPCManager::removeNPC(int32_t id) { BaseNPC* entity = NPCs[id]; - std::pair pos = ChunkManager::grabChunk(entity->appearanceData.iX, entity->appearanceData.iY); - // sanity check - if (ChunkManager::chunks.find(pos) == ChunkManager::chunks.end()) { + if (ChunkManager::chunks.find(entity->chunkPos) == ChunkManager::chunks.end()) { std::cout << "chunk not found!" << std::endl; return; } // remove NPC from the chunk - Chunk* chunk = ChunkManager::chunks[pos]; + Chunk* chunk = ChunkManager::chunks[entity->chunkPos]; chunk->NPCs.erase(id); - switch (entity->npcClass) { - case NPC_BUS: - INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); - exitBusData.eTT = 3; - exitBusData.iT_ID = id; + // remove from viewable chunks + removeNPC(entity->currentChunks, id); - for (Chunk* chunk : ChunkManager::grabChunks(pos)) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); - } - } - break; - case NPC_MOB: - // remove from mob list if it's a mob - if (MobManager::Mobs.find(id) != MobManager::Mobs.end()) + // remove from mob manager + if (MobManager::Mobs.find(id) != MobManager::Mobs.end()) MobManager::Mobs.erase(id); - // fall through - default: - // create struct - INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); - exitData.iNPC_ID = id; - - // remove it from the clients - for (Chunk* chunk : ChunkManager::grabChunks(pos)) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); - } - } - break; - } - + // finally, remove it from the map and free it NPCs.erase(id); delete entity; @@ -132,19 +136,31 @@ void NPCManager::removeNPC(int32_t id) { void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z) { BaseNPC* npc = NPCs[id]; - std::pair oldPos = ChunkManager::grabChunk(npc->appearanceData.iX, npc->appearanceData.iY); npc->appearanceData.iX = X; npc->appearanceData.iY = Y; npc->appearanceData.iZ = Z; std::pair newPos = ChunkManager::grabChunk(X, Y); - // nothing to be done - if (newPos == oldPos) + // nothing to be done (but we should also update currentChunks to add/remove stale chunks) + if (newPos == npc->chunkPos) { + npc->currentChunks = ChunkManager::grabChunks(newPos); return; + } - // pull NPC from old chunk and add to new chunk - ChunkManager::removeNPC(oldPos, npc->appearanceData.iNPC_ID); - ChunkManager::addNPC(X, Y, npc->appearanceData.iNPC_ID); + std::vector allChunks = ChunkManager::grabChunks(newPos); + + // send npc exit to stale chunks + removeNPC(ChunkManager::getDeltaChunks(npc->currentChunks, allChunks), id); + + // send npc enter to new chunks + addNPC(ChunkManager::getDeltaChunks(allChunks, npc->currentChunks), id); + + // update chunks + ChunkManager::removeNPC(npc->chunkPos, id); + ChunkManager::addNPC(X, Y, id); + + npc->chunkPos = newPos; + npc->currentChunks = allChunks; } void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t size) { @@ -482,7 +498,7 @@ void NPCManager::npcUnsummonHandler(CNSocket* sock, CNPacketData* data) { return; sP_CL2FE_REQ_NPC_UNSUMMON* req = (sP_CL2FE_REQ_NPC_UNSUMMON*)data->buf; - NPCManager::removeNPC(req->iNPC_ID); + NPCManager::destroyNPC(req->iNPC_ID); } void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { @@ -513,6 +529,7 @@ void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { } else NPCs[resp.NPCAppearanceData.iNPC_ID] = new BaseNPC(plr->x, plr->y, plr->z, req->iNPCType, resp.NPCAppearanceData.iNPC_ID); + updateNPCPosition(resp.NPCAppearanceData.iNPC_ID, plr->x, plr->y, plr->z); ChunkManager::addNPC(plr->x, plr->y, resp.NPCAppearanceData.iNPC_ID); } diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index d23e361..e6ba02a 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -24,8 +24,9 @@ namespace NPCManager { extern int32_t nextId; void init(); - void addNPC(std::vector viewableChunks, int32_t); - void removeNPC(int32_t); + void addNPC(std::vector viewableChunks, int32_t id); + void removeNPC(std::vector viewableChunks, int32_t id); + void destroyNPC(int32_t); void updateNPCPosition(int32_t, int X, int Y, int Z); void sendToViewable(BaseNPC* npc, void* buf, uint32_t type, size_t size); diff --git a/src/TableData.cpp b/src/TableData.cpp index ec1fe3a..7fbae22 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -27,7 +27,7 @@ void TableData::init() { BaseNPC *tmp = new BaseNPC(npc["x"], npc["y"], npc["z"], npc["id"], nextId); NPCManager::NPCs[nextId] = tmp; - ChunkManager::addNPC(npc["x"], npc["y"], nextId); + NPCManager::updateNPCPosition(nextId, npc["x"], npc["y"], npc["z"]); nextId++; if (npc["id"] == 641 || npc["id"] == 642) @@ -172,7 +172,7 @@ void TableData::init() { NPCManager::NPCs[nextId] = tmp; MobManager::Mobs[nextId] = (Mob*)NPCManager::NPCs[nextId]; - ChunkManager::addNPC(npc["iX"], npc["iY"], nextId); + NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"]); nextId++; }