diff --git a/src/Chunking.cpp b/src/Chunking.cpp index d2d0cdb..2feb443 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -240,59 +240,67 @@ bool Chunking::inPopulatedChunks(std::set* chnks) { void Chunking::createInstance(uint64_t instanceID) { 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) { - // make a copy of each NPC in the template chunks and put them in the new instance - BaseNPC* baseNPC = NPCManager::NPCs[npcID]; - if (baseNPC->npcClass == NPC_MOB) { - if (((Mob*)baseNPC)->groupLeader != 0 && ((Mob*)baseNPC)->groupLeader != npcID) - continue; // follower; don't copy individually - Mob* newMob = new Mob(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, baseNPC->appearanceData.iAngle, - instanceID, baseNPC->appearanceData.iNPCType, NPCManager::NPCData[baseNPC->appearanceData.iNPCType], NPCManager::nextId++); - NPCManager::NPCs[newMob->appearanceData.iNPC_ID] = newMob; - MobAI::Mobs[newMob->appearanceData.iNPC_ID] = newMob; + // only instantiate if the instance doesn't exist already + if (getChunksInMap(instanceID).size() != 0) { + std::cout << "Instance " << instanceID << " already exists" << std::endl; + return; + } - // if in a group, copy over group members as well - if (((Mob*)baseNPC)->groupLeader != 0) { - newMob->groupLeader = newMob->appearanceData.iNPC_ID; // set leader ID for new leader - Mob* mobData = (Mob*)baseNPC; - for (int i = 0; i < 4; i++) { - if (mobData->groupMember[i] != 0) { - int followerID = NPCManager::nextId++; // id for follower - BaseNPC* baseFollower = NPCManager::NPCs[mobData->groupMember[i]]; // follower from template - // new follower instance - Mob* newMobFollower = new Mob(baseFollower->appearanceData.iX, baseFollower->appearanceData.iY, baseFollower->appearanceData.iZ, baseFollower->appearanceData.iAngle, - instanceID, baseFollower->appearanceData.iNPCType, NPCManager::NPCData[baseFollower->appearanceData.iNPCType], followerID); - // add follower to NPC maps - NPCManager::NPCs[followerID] = newMobFollower; - MobAI::Mobs[followerID] = newMobFollower; - // set follower-specific properties - newMobFollower->groupLeader = newMob->appearanceData.iNPC_ID; - newMobFollower->offsetX = ((Mob*)baseFollower)->offsetX; - newMobFollower->offsetY = ((Mob*)baseFollower)->offsetY; - // add follower copy to leader copy - newMob->groupMember[i] = followerID; - NPCManager::updateNPCPosition(followerID, baseFollower->appearanceData.iX, baseFollower->appearanceData.iY, baseFollower->appearanceData.iZ, - instanceID, baseFollower->appearanceData.iAngle); - } + std::cout << "Creating instance " << instanceID << std::endl; + for (ChunkPos &coords : templateChunks) { + for (const EntityRef& ref : chunks[coords]->entities) { + if (ref.type == EntityType::PLAYER) + continue; + + int npcID = ref.id; + BaseNPC* baseNPC = (BaseNPC*)ref.getEntity(); + + // make a copy of each NPC in the template chunks and put them in the new instance + if (baseNPC->type == EntityType::MOB) { + if (((Mob*)baseNPC)->groupLeader != 0 && ((Mob*)baseNPC)->groupLeader != npcID) + continue; // follower; don't copy individually + + Mob* newMob = new Mob(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, baseNPC->appearanceData.iAngle, + instanceID, baseNPC->appearanceData.iNPCType, NPCManager::NPCData[baseNPC->appearanceData.iNPCType], NPCManager::nextId++); + NPCManager::NPCs[newMob->appearanceData.iNPC_ID] = newMob; + MobAI::Mobs[newMob->appearanceData.iNPC_ID] = newMob; + + // if in a group, copy over group members as well + if (((Mob*)baseNPC)->groupLeader != 0) { + newMob->groupLeader = newMob->appearanceData.iNPC_ID; // set leader ID for new leader + Mob* mobData = (Mob*)baseNPC; + for (int i = 0; i < 4; i++) { + if (mobData->groupMember[i] != 0) { + int followerID = NPCManager::nextId++; // id for follower + BaseNPC* baseFollower = NPCManager::NPCs[mobData->groupMember[i]]; // follower from template + // new follower instance + Mob* newMobFollower = new Mob(baseFollower->appearanceData.iX, baseFollower->appearanceData.iY, baseFollower->appearanceData.iZ, baseFollower->appearanceData.iAngle, + instanceID, baseFollower->appearanceData.iNPCType, NPCManager::NPCData[baseFollower->appearanceData.iNPCType], followerID); + // add follower to NPC maps + NPCManager::NPCs[followerID] = newMobFollower; + MobAI::Mobs[followerID] = newMobFollower; + // set follower-specific properties + newMobFollower->groupLeader = newMob->appearanceData.iNPC_ID; + newMobFollower->offsetX = ((Mob*)baseFollower)->offsetX; + newMobFollower->offsetY = ((Mob*)baseFollower)->offsetY; + // add follower copy to leader copy + newMob->groupMember[i] = followerID; + NPCManager::updateNPCPosition(followerID, baseFollower->appearanceData.iX, baseFollower->appearanceData.iY, baseFollower->appearanceData.iZ, + instanceID, baseFollower->appearanceData.iAngle); } } - NPCManager::updateNPCPosition(newMob->appearanceData.iNPC_ID, baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, - instanceID, baseNPC->appearanceData.iAngle); - } else { - BaseNPC* newNPC = new BaseNPC(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, baseNPC->appearanceData.iAngle, - instanceID, baseNPC->appearanceData.iNPCType, NPCManager::nextId++); - NPCManager::NPCs[newNPC->appearanceData.iNPC_ID] = newNPC; - NPCManager::updateNPCPosition(newNPC->appearanceData.iNPC_ID, baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, - instanceID, baseNPC->appearanceData.iAngle); } + NPCManager::updateNPCPosition(newMob->appearanceData.iNPC_ID, baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, + instanceID, baseNPC->appearanceData.iAngle); + } else { + BaseNPC* newNPC = new BaseNPC(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, baseNPC->appearanceData.iAngle, + instanceID, baseNPC->appearanceData.iNPCType, NPCManager::nextId++); + NPCManager::NPCs[newNPC->appearanceData.iNPC_ID] = newNPC; + NPCManager::updateNPCPosition(newNPC->appearanceData.iNPC_ID, baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, + instanceID, baseNPC->appearanceData.iAngle); } } - } else { - std::cout << "Instance " << instanceID << " already exists" << std::endl; } } diff --git a/src/Chunking.hpp b/src/Chunking.hpp index b63c536..8efc637 100644 --- a/src/Chunking.hpp +++ b/src/Chunking.hpp @@ -11,8 +11,8 @@ class Chunk { public: - std::set players; - std::set NPCs; + //std::set players; + //std::set NPCs; std::set entities; int nplayers = 0; }; @@ -41,18 +41,4 @@ namespace Chunking { bool inPopulatedChunks(std::set* chnks); void createInstance(uint64_t); void destroyInstanceIfEmpty(uint64_t); - - // death row below this point - //void updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to); - //void updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to); - - //void trackPlayer(ChunkPos chunkPos, CNSocket* sock); - //void trackNPC(ChunkPos chunkPos, int32_t id); - //void untrackPlayer(ChunkPos chunkPos, CNSocket* sock); - //void untrackNPC(ChunkPos chunkPos, int32_t id); - - //void addPlayerToChunks(std::set chnks, CNSocket* sock); - //void addNPCToChunks(std::set chnks, int32_t id); - //void removePlayerFromChunks(std::set chnks, CNSocket* sock); - //void removeNPCFromChunks(std::set chnks, int32_t id); } diff --git a/src/CustomCommands.cpp b/src/CustomCommands.cpp index 825b968..be0a746 100644 --- a/src/CustomCommands.cpp +++ b/src/CustomCommands.cpp @@ -662,7 +662,7 @@ static void whoisCommand(std::string full, std::vector& args, CNSoc Chat::sendServerMessage(sock, "[WHOIS] Type: " + std::to_string(npc->appearanceData.iNPCType)); Chat::sendServerMessage(sock, "[WHOIS] HP: " + std::to_string(npc->appearanceData.iHP)); Chat::sendServerMessage(sock, "[WHOIS] CBF: " + std::to_string(npc->appearanceData.iConditionBitFlag)); - Chat::sendServerMessage(sock, "[WHOIS] Class: " + std::to_string(npc->npcClass)); + Chat::sendServerMessage(sock, "[WHOIS] EntityType: " + std::to_string((int)npc->type)); Chat::sendServerMessage(sock, "[WHOIS] X: " + std::to_string(npc->appearanceData.iX)); Chat::sendServerMessage(sock, "[WHOIS] Y: " + std::to_string(npc->appearanceData.iY)); Chat::sendServerMessage(sock, "[WHOIS] Z: " + std::to_string(npc->appearanceData.iZ)); @@ -682,7 +682,11 @@ static void lairUnlockCommand(std::string full, std::vector& args, int taskID = -1; int missionID = -1; int found = 0; - for (int32_t id : chnk->NPCs) { + for (const EntityRef& ref : chnk->entities) { + if (ref.type == EntityType::PLAYER) + continue; + + int32_t id = ref.id; if (NPCManager::NPCs.find(id) == NPCManager::NPCs.end()) continue; diff --git a/src/Entities.hpp b/src/Entities.hpp index 0f0c465..a4fe3ed 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -9,6 +9,7 @@ enum class EntityType { PLAYER, SIMPLE_NPC, COMBAT_NPC, + MOB, EGG, BUS }; @@ -68,34 +69,37 @@ struct EntityRef { } }; +/* + * Subclasses + */ class BaseNPC : public Entity { public: sNPCAppearanceData appearanceData; - NPCClass npcClass; + //NPCClass npcClass; int playersInView; BaseNPC() {}; - BaseNPC(int x, int y, int z, int angle, uint64_t iID, int type, int id) { // XXX + BaseNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id) { // XXX appearanceData.iX = x; appearanceData.iY = y; appearanceData.iZ = z; - appearanceData.iNPCType = type; + appearanceData.iNPCType = t; appearanceData.iHP = 400; appearanceData.iAngle = angle; appearanceData.iConditionBitFlag = 0; appearanceData.iBarkerType = 0; appearanceData.iNPC_ID = id; - npcClass = NPCClass::NPC_BASE; + type = EntityType::SIMPLE_NPC; instanceID = iID; chunkPos = std::make_tuple(0, 0, 0); playersInView = 0; }; - BaseNPC(int x, int y, int z, int angle, uint64_t iID, int type, int id, NPCClass classType) : BaseNPC(x, y, z, angle, iID, type, id) { - npcClass = classType; + BaseNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, EntityType entityType) : BaseNPC(x, y, z, angle, iID, t, id) { + type = entityType; } // XXX: move to CombatNPC, probably @@ -111,10 +115,10 @@ struct Egg : public BaseNPC { bool dead = false; time_t deadUntil; - Egg(int x, int y, int z, uint64_t iID, int type, int32_t id, bool summon) - : BaseNPC(x, y, z, 0, iID, type, id) { + Egg(int x, int y, int z, uint64_t iID, int t, int32_t id, bool summon) + : BaseNPC(x, y, z, 0, iID, t, id) { summoned = summon; - npcClass = NPCClass::NPC_EGG; + type = EntityType::EGG; } virtual bool isAlive() override { return !dead; } diff --git a/src/MobAI.cpp b/src/MobAI.cpp index 19895bd..a3ad09c 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -121,7 +121,12 @@ bool MobAI::aggroCheck(Mob *mob, time_t currTime) { for (auto it = mob->viewableChunks.begin(); it != mob->viewableChunks.end(); it++) { Chunk* chunk = *it; - for (CNSocket *s : chunk->players) { + for (const EntityRef& ref : chunk->entities) { + // TODO: support targetting other CombatNPCs + if (ref.type != EntityType::PLAYER) + continue; + + CNSocket *s = ref.sock; Player *plr = PlayerManager::getPlayer(s); if (plr->HP <= 0) @@ -298,7 +303,12 @@ static void useAbilities(Mob *mob, time_t currTime) { // find the players within range of eruption for (auto it = mob->viewableChunks.begin(); it != mob->viewableChunks.end(); it++) { Chunk* chunk = *it; - for (CNSocket *s : chunk->players) { + for (const EntityRef& ref : chunk->entities) { + // TODO: see aggroCheck() + if (ref.type != EntityType::PLAYER) + continue; + + CNSocket *s= ref.sock; Player *plr = PlayerManager::getPlayer(s); if (plr->HP <= 0) diff --git a/src/MobAI.hpp b/src/MobAI.hpp index 4c3cec1..b964681 100644 --- a/src/MobAI.hpp +++ b/src/MobAI.hpp @@ -53,8 +53,8 @@ struct Mob : public BaseNPC { // temporary; until we're sure what's what nlohmann::json data; - Mob(int x, int y, int z, int angle, uint64_t iID, int type, nlohmann::json d, int32_t id) - : BaseNPC(x, y, z, angle, iID, type, id), + Mob(int x, int y, int z, int angle, uint64_t iID, int t, nlohmann::json d, int32_t id) + : BaseNPC(x, y, z, angle, iID, t, id), maxHealth(d["m_iHP"]), sightRange(d["m_iSightRange"]) { state = MobState::ROAMING; @@ -78,12 +78,12 @@ struct Mob : public BaseNPC { // NOTE: there appear to be discrepancies in the dump appearanceData.iHP = maxHealth; - npcClass = NPC_MOB; + type = EntityType::MOB; } // constructor for /summon - Mob(int x, int y, int z, uint64_t iID, int type, nlohmann::json d, int32_t id) - : Mob(x, y, z, 0, iID, type, d, id) { + Mob(int x, int y, int z, uint64_t iID, int t, nlohmann::json d, int32_t id) + : Mob(x, y, z, 0, iID, t, d, id) { summoned = true; // will be despawned and deallocated when killed } diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index aa07692..dce89ff 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -88,8 +88,9 @@ void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, uint64_t I, void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t size) { for (auto it = npc->viewableChunks.begin(); it != npc->viewableChunks.end(); it++) { Chunk* chunk = *it; - for (CNSocket *s : chunk->players) { - s->sendPacket(buf, type, size); + for (const EntityRef& ref : chunk->entities) { + if (ref.type == EntityType::PLAYER) + ref.sock->sendPacket(buf, type, size); } } } @@ -282,8 +283,11 @@ BaseNPC* NPCManager::getNearestNPC(std::set* chunks, int X, int Y, int Z int lastDist = INT_MAX; for (auto c = chunks->begin(); c != chunks->end(); c++) { // haha get it Chunk* chunk = *c; - for (auto _npc = chunk->NPCs.begin(); _npc != chunk->NPCs.end(); _npc++) { - BaseNPC* npcTemp = NPCs[*_npc]; + for (auto ent = chunk->entities.begin(); ent != chunk->entities.end(); ent++) { + if (ent->type == EntityType::PLAYER) + continue; + + BaseNPC* npcTemp = (BaseNPC*)ent->getEntity(); int distXY = std::hypot(X - npcTemp->appearanceData.iX, Y - npcTemp->appearanceData.iY); int dist = std::hypot(distXY, Z - npcTemp->appearanceData.iZ); if (dist < lastDist) { diff --git a/src/Player.hpp b/src/Player.hpp index 368d546..8b7b724 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -93,6 +93,8 @@ struct Player : public Entity { time_t lastShot; std::vector buyback; + Player() { type = EntityType::PLAYER; } + virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; }; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index c3b1d79..657c943 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -331,11 +331,11 @@ void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, siz Player* plr = getPlayer(sock); for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) { Chunk* chunk = *it; - for (CNSocket* otherSock : chunk->players) { - if (otherSock == sock) + for (const EntityRef& ref : chunk->entities) { + if (ref.type != EntityType::PLAYER || ref.sock == sock) continue; - otherSock->sendPacket(buf, type, size); + ref.sock->sendPacket(buf, type, size); } } } diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 48807af..cdb14b8 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -40,11 +40,11 @@ namespace PlayerManager { Player* plr = getPlayer(sock); for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) { Chunk* chunk = *it; - for (CNSocket* otherSock : chunk->players) { - if (otherSock == sock) + for (const EntityRef& ref : chunk->entities) { + if (ref.type != EntityType::PLAYER || ref.sock == sock) continue; - otherSock->sendPacket(pkt, type); + ref.sock->sendPacket(pkt, type); } } } diff --git a/src/TableData.cpp b/src/TableData.cpp index 86dfbab..fc5477d 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -139,7 +139,7 @@ static void loadPaths(int* nextId) { 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); + BaseNPC* slider = new BaseNPC(point.x, point.y, point.z, 0, INSTANCE_OVERWORLD, 1, (*nextId)++, EntityType::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); Transport::NPCQueues[slider->appearanceData.iNPC_ID] = route; @@ -965,7 +965,7 @@ void TableData::flush() { continue; int x, y, z; - if (npc->npcClass == NPC_MOB) { + if (npc->type == EntityType::MOB) { Mob *m = (Mob*)npc; x = m->spawnX; y = m->spawnY; @@ -998,7 +998,7 @@ void TableData::flush() { int x, y, z; std::vector followers; - if (npc->npcClass == NPC_MOB) { + if (npc->type == EntityType::MOB) { Mob* m = (Mob*)npc; x = m->spawnX; y = m->spawnY; diff --git a/src/Transport.cpp b/src/Transport.cpp index 5d32eaf..4ffa088 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -256,13 +256,13 @@ static void stepNPCPathing() { } // skip if not simulating mobs - if (npc->npcClass == NPC_MOB && !MobAI::simulateMobs) { + if (npc->type == EntityType::MOB && !MobAI::simulateMobs) { it++; continue; } // do not roam if not roaming - if (npc->npcClass == NPC_MOB && ((Mob*)npc)->state != MobState::ROAMING) { + if (npc->type == EntityType::MOB && ((Mob*)npc)->state != MobState::ROAMING) { it++; continue; } @@ -277,9 +277,11 @@ static void stepNPCPathing() { // update NPC location to update viewables NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, point.x, point.y, point.z, npc->instanceID, npc->appearanceData.iAngle); - switch (npc->npcClass) { - case NPC_BUS: + // TODO: move walking logic into Entity stack + switch (npc->type) { + case EntityType::BUS: INITSTRUCT(sP_FE2CL_TRANSPORTATION_MOVE, busMove); + busMove.eTT = 3; busMove.iT_ID = npc->appearanceData.iNPC_ID; busMove.iMoveStyle = 0; // ??? @@ -290,7 +292,7 @@ static void stepNPCPathing() { NPCManager::sendToViewable(npc, &busMove, P_FE2CL_TRANSPORTATION_MOVE, sizeof(sP_FE2CL_TRANSPORTATION_MOVE)); break; - case NPC_MOB: + case EntityType::MOB: MobAI::incNextMovement((Mob*)npc); /* fallthrough */ default: @@ -310,7 +312,7 @@ static void stepNPCPathing() { * Move processed point to the back to maintain cycle, unless this is a * dynamically calculated mob route. */ - if (!(npc->npcClass == NPC_MOB && !((Mob*)npc)->staticPath)) + if (!(npc->type == EntityType::MOB && !((Mob*)npc)->staticPath)) queue->push(point); it++; // go to next entry in map diff --git a/src/core/Defines.hpp b/src/core/Defines.hpp index 1a6a33e..a18e1a6 100644 --- a/src/core/Defines.hpp +++ b/src/core/Defines.hpp @@ -24,14 +24,6 @@ enum eCN_GM_TeleportType { eCN_GM_TeleportMapType__Unstick }; -// NPC classes -enum NPCClass { - NPC_BASE = 0, - NPC_MOB = 1, - NPC_BUS = 2, - NPC_EGG = 3 -}; - // nano powers enum { EST_NONE = 0,