From 883a1c17e6f9f695ea591ed46fb3d44fd794d24d Mon Sep 17 00:00:00 2001 From: Jade Date: Sun, 15 Nov 2020 07:06:29 +0000 Subject: [PATCH] Group Mobs Initial Implementation * For now only mob.json is read for grouped mobs. * Grouped mobs are fully functional granted the mobs.json is prepared correctly. * Removed redundant move packet. --- src/MobManager.cpp | 64 ++++++++++++++++++++++++++++++++++------------ src/MobManager.hpp | 5 ++++ src/TableData.cpp | 34 ++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/src/MobManager.cpp b/src/MobManager.cpp index a032cce..ad6ac3a 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -434,6 +434,10 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { } } + // to guide their groupmates, group leaders still need to move despite being dead + if (mob->groupLeader == mob->appearanceData.iNPC_ID) + roamingStep(mob, currTime); + if (mob->killedTime != 0 && currTime - mob->killedTime < mob->regenTime * 100) return; @@ -443,8 +447,21 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { mob->state = MobState::ROAMING; // reset position - mob->appearanceData.iX = mob->spawnX; - mob->appearanceData.iY = mob->spawnY; + if (mob->groupLeader != 0) { + // mob is a group leader/follower, spawn where the group is. + 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; + } else { + std::cout << "[WARN] deadStep: mob cannot find it's leader!" << std::endl; + mob->appearanceData.iX = mob->spawnX; + mob->appearanceData.iY = mob->spawnY; + } + } else { + mob->appearanceData.iX = mob->spawnX; + mob->appearanceData.iY = mob->spawnY; + } mob->appearanceData.iZ = mob->spawnZ; INITSTRUCT(sP_FE2CL_NPC_NEW, pkt); @@ -523,15 +540,6 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { if (distance <= (int)mob->data["m_iAtkRange"]) { // attack logic if (mob->nextAttack == 0) { - INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; - pkt.iSpeed = (int)mob->data["m_iRunSpeed"]; - pkt.iToX = mob->appearanceData.iX; - pkt.iToY = mob->appearanceData.iY; - pkt.iToZ = mob->target->plr->z; - pkt.iMoveStyle = 1; - NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE)); - mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; //I *think* this is what this is npcAttackPc(mob, currTime); } else if (mob->nextAttack != 0 && currTime >= mob->nextAttack) { @@ -562,6 +570,10 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { pkt.iSpeed = speed; pkt.iToX = mob->appearanceData.iX = targ.first; pkt.iToY = mob->appearanceData.iY = targ.second; + if (mob->groupLeader != 0 && mob->groupLeader != mob->appearanceData.iNPC_ID) { + pkt.iToX += mob->offsetX; + pkt.iToY += mob->offsetY; + } pkt.iToZ = mob->target->plr->z; pkt.iMoveStyle = 1; @@ -591,8 +603,9 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { /* * We reuse nextAttack to avoid scanning for players all the time, but to still * do so more often than if we waited for nextMovement (which is way too slow). + * In the case of group leaders, this step will be called by dead mobs, so disable attack. */ - if (mob->nextAttack == 0 || currTime >= mob->nextAttack) { + if (mob->state != MobState::DEAD && (mob->nextAttack == 0 || currTime >= mob->nextAttack)) { mob->nextAttack = currTime + 500; if (aggroCheck(mob, currTime)) return; @@ -615,6 +628,9 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { * so we don't have to check if there's already entries in the queue since we know there won't be. */ + if (mob->groupLeader != 0 && mob->groupLeader != mob->appearanceData.iNPC_ID) // don't roam by yourself without group leader + return; + int xStart = mob->spawnX - mob->idleRange/2; int yStart = mob->spawnY - mob->idleRange/2; int speed = mob->data["m_iWalkSpeed"]; @@ -622,10 +638,6 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { int farX, farY; int distance; // for short walk detection - /* - * We don't want the mob to just take one step and stop, so we make sure - * it has walked a half-decent distance. - */ do { farX = xStart + rand() % mob->idleRange; farY = yStart + rand() % mob->idleRange; @@ -644,6 +656,26 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) { // add a route to the queue; to be processed in TransportManager::stepNPCPathing() TransportManager::lerp(&queue, from, to, speed); TransportManager::NPCQueues[mob->appearanceData.iNPC_ID] = queue; + + if (mob->groupLeader != 0 && mob->groupLeader == mob->appearanceData.iNPC_ID) { + // make followers follow this npc. + for (int i = 0; i < 4; i++) { + if (mob->groupMember[i] == 0) + break; + + 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 = 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); + TransportManager::NPCQueues[followerMob->appearanceData.iNPC_ID] = queue2; + } + } } void MobManager::retreatStep(Mob *mob, time_t currTime) { diff --git a/src/MobManager.hpp b/src/MobManager.hpp index f7b90c6..1ab5064 100644 --- a/src/MobManager.hpp +++ b/src/MobManager.hpp @@ -51,6 +51,11 @@ struct Mob : public BaseNPC { // drop int dropType; + // group + int groupLeader = 0; + int offsetX, offsetY; + int groupMember[4] = {0, 0, 0, 0}; + // temporary; until we're sure what's what nlohmann::json data; diff --git a/src/TableData.cpp b/src/TableData.cpp index 5e5f64a..f80ed3c 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -201,17 +201,51 @@ void TableData::init() { // read file into json inFile >> npcData; + int leaderMob = -1; + int leaderMobFollowers = 0; for (nlohmann::json::iterator _npc = npcData.begin(); _npc != npcData.end(); _npc++) { auto npc = _npc.value(); auto td = NPCManager::NPCData[(int)npc["iNPCType"]]; uint64_t instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"]; + Mob *tmp = new Mob(npc["iX"], npc["iY"], npc["iZ"], npc["iAngle"], instanceID, npc["iNPCType"], npc["iHP"], td, nextId); NPCManager::NPCs[nextId] = tmp; MobManager::Mobs[nextId] = (Mob*)NPCManager::NPCs[nextId]; NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"], instanceID, npc["iAngle"]); + // handling groups + if (npc.find("iOffsetX") != npc.end() && MobManager::Mobs.find(nextId) != MobManager::Mobs.end()) { + Mob* currNpc = MobManager::Mobs[nextId]; + + if (leaderMob == -1) { + if (MobManager::Mobs.find(nextId-1) != MobManager::Mobs.end()) { + Mob* leadNpc = MobManager::Mobs[nextId-1]; + leaderMob = nextId-1; + leadNpc->groupMember[leaderMobFollowers] = nextId; + leaderMobFollowers++; + currNpc->groupLeader = nextId-1; + leadNpc->groupLeader = nextId-1; + } + } else { + if (MobManager::Mobs.find(leaderMob) != MobManager::Mobs.end()) { + Mob* leadNpc = MobManager::Mobs[leaderMob]; + leaderMob = leaderMob; + leadNpc->groupMember[leaderMobFollowers] = nextId; + leaderMobFollowers++; + currNpc->groupLeader = leaderMob; + } + } + + currNpc->offsetX = (int)npc["iOffsetX"]; + currNpc->offsetY = npc.find("iOffsetY") == npc.end() ? 0 : (int)npc["iOffsetY"]; + std::cout << "[INFO] Added group NPC " << nextId << " to ID " << currNpc->groupLeader << std::endl; + } else { + leaderMob = -1; + leaderMobFollowers = 0; + } + nextId++; }