From 3ce9ae5f7726fcda043b75ddd12286b489b3ca45 Mon Sep 17 00:00:00 2001 From: gsemaj Date: Sun, 20 Jun 2021 14:37:37 -0400 Subject: [PATCH] [WIP] Replace appearance data with individual fields Storing certain things in appearance data and others in their own fields was gross. Now everything is stored on the same level and functions have been added to generate appearance data when it's needed by the client. --- src/Abilities.cpp | 64 ++++++++++----------- src/Chunking.cpp | 30 +++++----- src/Combat.cpp | 38 ++++++------- src/CustomCommands.cpp | 124 ++++++++++++++++++++--------------------- src/Eggs.cpp | 6 +- src/Entities.cpp | 67 ++++++++++++++-------- src/Entities.hpp | 26 +++++---- src/Items.cpp | 6 +- src/Missions.cpp | 4 +- src/MobAI.cpp | 75 ++++++++++++------------- src/MobAI.hpp | 4 +- src/NPCManager.cpp | 28 +++++----- src/Player.hpp | 2 + src/PlayerMovement.cpp | 4 +- src/TableData.cpp | 38 ++++++------- src/Transport.cpp | 6 +- 16 files changed, 273 insertions(+), 249 deletions(-) diff --git a/src/Abilities.cpp b/src/Abilities.cpp index 27f36b9..bc438f6 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -151,15 +151,15 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target Combat::hitMob(sock, mob, 0); respdata[i].eCT = 4; - respdata[i].iID = mob->appearanceData.iNPC_ID; + respdata[i].iID = mob->id; respdata[i].bProtected = 1; if (mob->skillStyle < 0 && mob->state != MobState::RETREAT - && !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom - mob->appearanceData.iConditionBitFlag |= bitFlag; + && !(mob->cbf & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom + mob->cbf |= bitFlag; mob->unbuffTimes[bitFlag] = getTime() + duration * 100; respdata[i].bProtected = 0; } - respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + respdata[i].iConditionBitFlag = mob->cbf; return true; } @@ -224,16 +224,16 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int respdata[i].eCT = 4; respdata[i].iDamage = duration / 10; - respdata[i].iID = mob->appearanceData.iNPC_ID; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iID = mob->id; + respdata[i].iHP = mob->hp; respdata[i].bProtected = 1; if (mob->skillStyle < 0 && mob->state != MobState::RETREAT - && !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom - mob->appearanceData.iConditionBitFlag |= bitFlag; + && !(mob->cbf & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom + mob->cbf |= bitFlag; mob->unbuffTimes[bitFlag] = getTime() + duration * 100; respdata[i].bProtected = 0; } - respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + respdata[i].iConditionBitFlag = mob->cbf; return true; } @@ -293,8 +293,8 @@ bool doDamage(CNSocket *sock, sSkillResult_Damage *respdata, int i, int32_t targ respdata[i].eCT = 4; respdata[i].iDamage = damage; - respdata[i].iID = mob->appearanceData.iNPC_ID; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iID = mob->id; + respdata[i].iHP = mob->hp; return true; } @@ -348,8 +348,8 @@ bool doLeech(CNSocket *sock, sSkillResult_Heal_HP *healdata, int i, int32_t targ damagedata->eCT = 4; damagedata->iDamage = damage; - damagedata->iID = mob->appearanceData.iNPC_ID; - damagedata->iHP = mob->appearanceData.iHP; + damagedata->iID = mob->id; + damagedata->iHP = mob->hp; return true; } @@ -476,13 +476,13 @@ bool doHeal(Mob* mob, sSkillResult_Heal_HP* respdata, int i, int32_t targetID, i Mob* targetMob = (Mob*)npc; int healedAmount = amount * targetMob->maxHealth / 1000; - targetMob->appearanceData.iHP += healedAmount; - if (targetMob->appearanceData.iHP > targetMob->maxHealth) - targetMob->appearanceData.iHP = targetMob->maxHealth; + targetMob->hp += healedAmount; + if (targetMob->hp > targetMob->maxHealth) + targetMob->hp = targetMob->maxHealth; respdata[i].eCT = 4; - respdata[i].iID = targetMob->appearanceData.iNPC_ID; - respdata[i].iHP = targetMob->appearanceData.iHP; + respdata[i].iID = targetMob->id; + respdata[i].iHP = targetMob->hp; respdata[i].iHealHP = healedAmount; return true; @@ -490,13 +490,13 @@ bool doHeal(Mob* mob, sSkillResult_Heal_HP* respdata, int i, int32_t targetID, i bool doReturnHeal(Mob* mob, sSkillResult_Heal_HP* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { int healedAmount = amount * mob->maxHealth / 1000; - mob->appearanceData.iHP += healedAmount; - if (mob->appearanceData.iHP > mob->maxHealth) - mob->appearanceData.iHP = mob->maxHealth; + mob->hp += healedAmount; + if (mob->hp > mob->maxHealth) + mob->hp = mob->maxHealth; respdata[i].eCT = 4; - respdata[i].iID = mob->appearanceData.iNPC_ID; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iID = mob->id; + respdata[i].iHP = mob->hp; respdata[i].iHealHP = healedAmount; return true; @@ -567,13 +567,13 @@ bool doLeech(Mob* mob, sSkillResult_Heal_HP* healdata, int i, int32_t targetID, int healedAmount = amount * PC_MAXHEALTH(plr->level) / 1000; - mob->appearanceData.iHP += healedAmount; - if (mob->appearanceData.iHP > mob->maxHealth) - mob->appearanceData.iHP = mob->maxHealth; + mob->hp += healedAmount; + if (mob->hp > mob->maxHealth) + mob->hp = mob->maxHealth; healdata->eCT = 4; - healdata->iID = mob->appearanceData.iNPC_ID; - healdata->iHP = mob->appearanceData.iHP; + healdata->iID = mob->id; + healdata->iHP = mob->hp; healdata->iHealHP = healedAmount; int damage = healedAmount; @@ -639,9 +639,9 @@ bool doBatteryDrain(Mob* mob, sSkillResult_BatteryDrain* respdata, int i, int32_ bool doBuff(Mob* mob, sSkillResult_Buff* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { respdata[i].eCT = 4; - respdata[i].iID = mob->appearanceData.iNPC_ID; - mob->appearanceData.iConditionBitFlag |= bitFlag; - respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + respdata[i].iID = mob->id; + mob->cbf |= bitFlag; + respdata[i].iConditionBitFlag = mob->cbf; return true; } @@ -670,7 +670,7 @@ templateiNPC_ID = mob->appearanceData.iNPC_ID; + resp->iNPC_ID = mob->id; resp->iSkillID = skillID; resp->iValue1 = mob->hitX; resp->iValue2 = mob->hitY; diff --git a/src/Chunking.cpp b/src/Chunking.cpp index fcc9150..04b391f 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -279,42 +279,42 @@ void Chunking::createInstance(uint64_t instanceID) { if (((Mob*)baseNPC)->groupLeader != 0 && ((Mob*)baseNPC)->groupLeader != npcID) continue; // follower; don't copy individually - Mob* newMob = new Mob(baseNPC->x, baseNPC->y, baseNPC->z, baseNPC->appearanceData.iAngle, - instanceID, baseNPC->appearanceData.iNPCType, NPCManager::NPCData[baseNPC->appearanceData.iNPCType], NPCManager::nextId--); - NPCManager::NPCs[newMob->appearanceData.iNPC_ID] = newMob; + Mob* newMob = new Mob(baseNPC->x, baseNPC->y, baseNPC->z, baseNPC->angle, + instanceID, baseNPC->type, NPCManager::NPCData[baseNPC->type], NPCManager::nextId--); + NPCManager::NPCs[newMob->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 + newMob->groupLeader = newMob->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->x, baseFollower->y, baseFollower->z, baseFollower->appearanceData.iAngle, - instanceID, baseFollower->appearanceData.iNPCType, NPCManager::NPCData[baseFollower->appearanceData.iNPCType], followerID); + Mob* newMobFollower = new Mob(baseFollower->x, baseFollower->y, baseFollower->z, baseFollower->angle, + instanceID, baseFollower->type, NPCManager::NPCData[baseFollower->type], followerID); // add follower to NPC maps NPCManager::NPCs[followerID] = newMobFollower; // set follower-specific properties - newMobFollower->groupLeader = newMob->appearanceData.iNPC_ID; + newMobFollower->groupLeader = newMob->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->x, baseFollower->y, baseFollower->z, - instanceID, baseFollower->appearanceData.iAngle); + instanceID, baseFollower->angle); } } } - NPCManager::updateNPCPosition(newMob->appearanceData.iNPC_ID, baseNPC->x, baseNPC->y, baseNPC->z, - instanceID, baseNPC->appearanceData.iAngle); + NPCManager::updateNPCPosition(newMob->id, baseNPC->x, baseNPC->y, baseNPC->z, + instanceID, baseNPC->angle); } else { - BaseNPC* newNPC = new BaseNPC(baseNPC->x, baseNPC->y, baseNPC->z, baseNPC->appearanceData.iAngle, - instanceID, baseNPC->appearanceData.iNPCType, NPCManager::nextId--); - NPCManager::NPCs[newNPC->appearanceData.iNPC_ID] = newNPC; - NPCManager::updateNPCPosition(newNPC->appearanceData.iNPC_ID, baseNPC->x, baseNPC->y, baseNPC->z, - instanceID, baseNPC->appearanceData.iAngle); + BaseNPC* newNPC = new BaseNPC(baseNPC->x, baseNPC->y, baseNPC->z, baseNPC->angle, + instanceID, baseNPC->type, NPCManager::nextId--); + NPCManager::NPCs[newNPC->id] = newNPC; + NPCManager::updateNPCPosition(newNPC->id, baseNPC->x, baseNPC->y, baseNPC->z, + instanceID, baseNPC->angle); } } } diff --git a/src/Combat.cpp b/src/Combat.cpp index ce5f668..2e7f76c 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -139,9 +139,9 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) { damage.first = hitMob(sock, mob, damage.first); - respdata[i].iID = mob->appearanceData.iNPC_ID; + respdata[i].iID = mob->id; respdata[i].iDamage = damage.first; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iHP = mob->hp; respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } @@ -168,7 +168,7 @@ void Combat::npcAttackPc(Mob *mob, time_t currTime) { if (!(plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)) plr->HP -= damage.first; - pkt->iNPC_ID = mob->appearanceData.iNPC_ID; + pkt->iNPC_ID = mob->id; pkt->iPCCnt = 1; atk->iID = plr->iID; @@ -207,20 +207,20 @@ int Combat::hitMob(CNSocket *sock, Mob *mob, int damage) { MobAI::followToCombat(mob); } - mob->appearanceData.iHP -= damage; + mob->hp -= damage; // wake up sleeping monster - if (mob->appearanceData.iConditionBitFlag & CSB_BIT_MEZ) { - mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ; + if (mob->cbf & CSB_BIT_MEZ) { + mob->cbf &= ~CSB_BIT_MEZ; INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); pkt1.eCT = 2; - pkt1.iID = mob->appearanceData.iNPC_ID; - pkt1.iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + pkt1.iID = mob->id; + pkt1.iConditionBitFlag = mob->cbf; NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); } - if (mob->appearanceData.iHP <= 0) + if (mob->hp <= 0) killMob(mob->target, mob); return damage; @@ -253,7 +253,7 @@ static void genQItemRolls(Player *leader, std::map& rolls) { void Combat::killMob(CNSocket *sock, Mob *mob) { mob->state = MobState::DEAD; mob->target = nullptr; - mob->appearanceData.iConditionBitFlag = 0; + mob->cbf = 0; mob->skillStyle = -1; mob->unbuffTimes.clear(); mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step? @@ -273,7 +273,7 @@ void Combat::killMob(CNSocket *sock, Mob *mob) { if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { Items::giveMobDrop(sock, mob, rolled, eventRolled); - Missions::mobKilled(sock, mob->appearanceData.iNPCType, qitemRolls); + Missions::mobKilled(sock, mob->type, qitemRolls); } else { for (int i = 0; i < leader->groupCnt; i++) { CNSocket* sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]); @@ -288,7 +288,7 @@ void Combat::killMob(CNSocket *sock, Mob *mob) { continue; Items::giveMobDrop(sockTo, mob, rolled, eventRolled); - Missions::mobKilled(sockTo, mob->appearanceData.iNPCType, qitemRolls); + Missions::mobKilled(sockTo, mob->type, qitemRolls); } } } @@ -298,10 +298,10 @@ void Combat::killMob(CNSocket *sock, Mob *mob) { // fire any triggered events for (NPCEvent& event : NPCManager::NPCEvents) - if (event.trigger == ON_KILLED && event.npcType == mob->appearanceData.iNPCType) + if (event.trigger == ON_KILLED && event.npcType == mob->type) event.handler(sock, mob); - auto it = Transport::NPCQueues.find(mob->appearanceData.iNPC_ID); + auto it = Transport::NPCQueues.find(mob->id); if (it == Transport::NPCQueues.end() || it->second.empty()) return; @@ -319,7 +319,7 @@ void Combat::killMob(CNSocket *sock, Mob *mob) { queue.push(point); } } else { - Transport::NPCQueues.erase(mob->appearanceData.iNPC_ID); + Transport::NPCQueues.erase(mob->id); } } @@ -513,9 +513,9 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { damage.first = hitMob(sock, mob, damage.first); respdata[i].eCT = pktdata[i*2+1]; - respdata[i].iID = mob->appearanceData.iNPC_ID; + respdata[i].iID = mob->id; respdata[i].iDamage = damage.first; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iHP = mob->hp; respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } } @@ -711,9 +711,9 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) { damage.first = hitMob(sock, mob, damage.first); - respdata[i].iID = mob->appearanceData.iNPC_ID; + respdata[i].iID = mob->id; respdata[i].iDamage = damage.first; - respdata[i].iHP = mob->appearanceData.iHP; + respdata[i].iHP = mob->hp; respdata[i].iHitFlag = damage.second; } diff --git a/src/CustomCommands.cpp b/src/CustomCommands.cpp index b277328..d171bbb 100644 --- a/src/CustomCommands.cpp +++ b/src/CustomCommands.cpp @@ -243,20 +243,20 @@ static void summonWCommand(std::string full, std::vector& args, CNS BaseNPC *npc = NPCManager::summonNPC(plr->x, plr->y, plr->z, plr->instanceID, type, true); // update angle - npc->appearanceData.iAngle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, plr->instanceID, npc->appearanceData.iAngle); + npc->angle = (plr->angle + 180) % 360; + NPCManager::updateNPCPosition(npc->id, plr->x, plr->y, plr->z, plr->instanceID, npc->angle); // if we're in a lair, we need to spawn the NPC in both the private instance and the template if (PLAYERID(plr->instanceID) != 0) { npc = NPCManager::summonNPC(plr->x, plr->y, plr->z, plr->instanceID, type, true, true); - npc->appearanceData.iAngle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, npc->instanceID, npc->appearanceData.iAngle); + npc->angle = (plr->angle + 180) % 360; + NPCManager::updateNPCPosition(npc->id, plr->x, plr->y, plr->z, npc->instanceID, npc->angle); } Chat::sendServerMessage(sock, "/summonW: placed mob with type: " + std::to_string(type) + - ", id: " + std::to_string(npc->appearanceData.iNPC_ID)); - TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; // only record the one in the template + ", id: " + std::to_string(npc->id)); + TableData::RunningMobs[npc->id] = npc; // only record the one in the template } static void unsummonWCommand(std::string full, std::vector& args, CNSocket* sock) { @@ -269,21 +269,21 @@ static void unsummonWCommand(std::string full, std::vector& args, C return; } - if (TableData::RunningEggs.find(npc->appearanceData.iNPC_ID) != TableData::RunningEggs.end()) { - Chat::sendServerMessage(sock, "/unsummonW: removed egg with type: " + std::to_string(npc->appearanceData.iNPCType) + - ", id: " + std::to_string(npc->appearanceData.iNPC_ID)); - TableData::RunningEggs.erase(npc->appearanceData.iNPC_ID); - NPCManager::destroyNPC(npc->appearanceData.iNPC_ID); + if (TableData::RunningEggs.find(npc->id) != TableData::RunningEggs.end()) { + Chat::sendServerMessage(sock, "/unsummonW: removed egg with type: " + std::to_string(npc->type) + + ", id: " + std::to_string(npc->id)); + TableData::RunningEggs.erase(npc->id); + NPCManager::destroyNPC(npc->id); return; } - if (TableData::RunningMobs.find(npc->appearanceData.iNPC_ID) == TableData::RunningMobs.end() - && TableData::RunningGroups.find(npc->appearanceData.iNPC_ID) == TableData::RunningGroups.end()) { + if (TableData::RunningMobs.find(npc->id) == TableData::RunningMobs.end() + && TableData::RunningGroups.find(npc->id) == TableData::RunningGroups.end()) { Chat::sendServerMessage(sock, "/unsummonW: Closest NPC is not a gruntwork mob."); return; } - if (NPCManager::NPCs.find(npc->appearanceData.iNPC_ID) != NPCManager::NPCs.end() && NPCManager::NPCs[npc->appearanceData.iNPC_ID]->kind == EntityType::MOB) { + if (NPCManager::NPCs.find(npc->id) != NPCManager::NPCs.end() && NPCManager::NPCs[npc->id]->kind == EntityType::MOB) { int leadId = ((Mob*)npc)->groupLeader; if (leadId != 0) { if (NPCManager::NPCs.find(leadId) == NPCManager::NPCs.end() || NPCManager::NPCs[leadId]->kind != EntityType::MOB) { @@ -308,12 +308,12 @@ static void unsummonWCommand(std::string full, std::vector& args, C } } - Chat::sendServerMessage(sock, "/unsummonW: removed mob with type: " + std::to_string(npc->appearanceData.iNPCType) + - ", id: " + std::to_string(npc->appearanceData.iNPC_ID)); + Chat::sendServerMessage(sock, "/unsummonW: removed mob with type: " + std::to_string(npc->type) + + ", id: " + std::to_string(npc->id)); - TableData::RunningMobs.erase(npc->appearanceData.iNPC_ID); + TableData::RunningMobs.erase(npc->id); - NPCManager::destroyNPC(npc->appearanceData.iNPC_ID); + NPCManager::destroyNPC(npc->id); } static void toggleAiCommand(std::string full, std::vector& args, CNSocket* sock) { @@ -356,24 +356,24 @@ static void npcRotateCommand(std::string full, std::vector& args, C } int angle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, npc->z, npc->instanceID, angle); + NPCManager::updateNPCPosition(npc->id, npc->x, npc->y, npc->z, npc->instanceID, angle); // if it's a gruntwork NPC, rotate in-place - if (TableData::RunningMobs.find(npc->appearanceData.iNPC_ID) != TableData::RunningMobs.end()) { - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, npc->z, npc->instanceID, angle); + if (TableData::RunningMobs.find(npc->id) != TableData::RunningMobs.end()) { + NPCManager::updateNPCPosition(npc->id, npc->x, npc->y, npc->z, npc->instanceID, angle); Chat::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) + " for gruntwork NPC " - + std::to_string(npc->appearanceData.iNPC_ID)); + + std::to_string(npc->id)); } else { - TableData::RunningNPCRotations[npc->appearanceData.iNPC_ID] = angle; + TableData::RunningNPCRotations[npc->id] = angle; Chat::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) + " for NPC " - + std::to_string(npc->appearanceData.iNPC_ID)); + + std::to_string(npc->id)); } // update rotation clientside INITSTRUCT(sP_FE2CL_NPC_ENTER, pkt); - pkt.NPCAppearanceData = npc->appearanceData; + pkt.NPCAppearanceData = npc->getAppearanceData(); sock->sendPacket(pkt, P_FE2CL_NPC_ENTER); } @@ -443,9 +443,9 @@ static void npcInstanceCommand(std::string full, std::vector& args, return; } - Chat::sendServerMessage(sock, "[NPCI] Moving NPC with ID " + std::to_string(npc->appearanceData.iNPC_ID) + " to instance " + std::to_string(instance)); - TableData::RunningNPCMapNumbers[npc->appearanceData.iNPC_ID] = instance; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, npc->z, instance, npc->appearanceData.iAngle); + Chat::sendServerMessage(sock, "[NPCI] Moving NPC with ID " + std::to_string(npc->id) + " to instance " + std::to_string(instance)); + TableData::RunningNPCMapNumbers[npc->id] = instance; + NPCManager::updateNPCPosition(npc->id, npc->x, npc->y, npc->z, instance, npc->angle); } static void minfoCommand(std::string full, std::vector& args, CNSocket* sock) { @@ -610,39 +610,39 @@ static void summonGroupCommand(std::string full, std::vector& args, BaseNPC *npc = NPCManager::summonNPC(x, y, z, plr->instanceID, type, wCommand); if (team == 2 && i > 0 && npc->kind == EntityType::MOB) { - leadNpc->groupMember[i-1] = npc->appearanceData.iNPC_ID; - Mob* mob = (Mob*)NPCManager::NPCs[npc->appearanceData.iNPC_ID]; - mob->groupLeader = leadNpc->appearanceData.iNPC_ID; + leadNpc->groupMember[i-1] = npc->id; + Mob* mob = (Mob*)NPCManager::NPCs[npc->id]; + mob->groupLeader = leadNpc->id; mob->offsetX = x - plr->x; mob->offsetY = y - plr->y; } - npc->appearanceData.iAngle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z, plr->instanceID, npc->appearanceData.iAngle); + npc->angle = (plr->angle + 180) % 360; + NPCManager::updateNPCPosition(npc->id, x, y, z, plr->instanceID, npc->angle); // if we're in a lair, we need to spawn the NPC in both the private instance and the template if (PLAYERID(plr->instanceID) != 0) { npc = NPCManager::summonNPC(plr->x, plr->y, plr->z, plr->instanceID, type, wCommand, true); if (team == 2 && i > 0 && npc->kind == EntityType::MOB) { - leadNpc->groupMember[i-1] = npc->appearanceData.iNPC_ID; - Mob* mob = (Mob*)NPCManager::NPCs[npc->appearanceData.iNPC_ID]; - mob->groupLeader = leadNpc->appearanceData.iNPC_ID; + leadNpc->groupMember[i-1] = npc->id; + Mob* mob = (Mob*)NPCManager::NPCs[npc->id]; + mob->groupLeader = leadNpc->id; mob->offsetX = x - plr->x; mob->offsetY = y - plr->y; } - npc->appearanceData.iAngle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z, plr->instanceID, npc->appearanceData.iAngle); + npc->angle = (plr->angle + 180) % 360; + NPCManager::updateNPCPosition(npc->id, x, y, z, plr->instanceID, npc->angle); } Chat::sendServerMessage(sock, "/summonGroup(W): placed mob with type: " + std::to_string(type) + - ", id: " + std::to_string(npc->appearanceData.iNPC_ID)); + ", id: " + std::to_string(npc->id)); if (i == 0 && team == 2 && npc->kind == EntityType::MOB) { type = type2; - leadNpc = (Mob*)NPCManager::NPCs[npc->appearanceData.iNPC_ID]; - leadNpc->groupLeader = leadNpc->appearanceData.iNPC_ID; + leadNpc = (Mob*)NPCManager::NPCs[npc->id]; + leadNpc->groupLeader = leadNpc->id; } } @@ -654,7 +654,7 @@ static void summonGroupCommand(std::string full, std::vector& args, return; } - TableData::RunningGroups[leadNpc->appearanceData.iNPC_ID] = leadNpc; // only record the leader + TableData::RunningGroups[leadNpc->id] = leadNpc; // only record the leader } static void flushCommand(std::string full, std::vector& args, CNSocket* sock) { @@ -671,15 +671,15 @@ static void whoisCommand(std::string full, std::vector& args, CNSoc return; } - Chat::sendServerMessage(sock, "[WHOIS] ID: " + std::to_string(npc->appearanceData.iNPC_ID)); - 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] ID: " + std::to_string(npc->id)); + Chat::sendServerMessage(sock, "[WHOIS] Type: " + std::to_string(npc->type)); + Chat::sendServerMessage(sock, "[WHOIS] HP: " + std::to_string(npc->hp)); + Chat::sendServerMessage(sock, "[WHOIS] CBF: " + std::to_string(npc->cbf)); Chat::sendServerMessage(sock, "[WHOIS] EntityType: " + std::to_string((int)npc->kind)); Chat::sendServerMessage(sock, "[WHOIS] X: " + std::to_string(npc->x)); Chat::sendServerMessage(sock, "[WHOIS] Y: " + std::to_string(npc->y)); Chat::sendServerMessage(sock, "[WHOIS] Z: " + std::to_string(npc->z)); - Chat::sendServerMessage(sock, "[WHOIS] Angle: " + std::to_string(npc->appearanceData.iAngle)); + Chat::sendServerMessage(sock, "[WHOIS] Angle: " + std::to_string(npc->angle)); std::string chunkPosition = std::to_string(std::get<0>(npc->chunkPos)) + ", " + std::to_string(std::get<1>(npc->chunkPos)) + ", " + std::to_string(std::get<2>(npc->chunkPos)); Chat::sendServerMessage(sock, "[WHOIS] Chunk: {" + chunkPosition + "}"); Chat::sendServerMessage(sock, "[WHOIS] MapNum: " + std::to_string(MAPNUM(npc->instanceID))); @@ -705,7 +705,7 @@ static void lairUnlockCommand(std::string full, std::vector& args, continue; for (auto it = NPCManager::Warps.begin(); it != NPCManager::Warps.end(); it++) { - if (it->second.npcID == npc->appearanceData.iNPCType) { + if (it->second.npcID == npc->type) { taskID = it->second.limitTaskID; missionID = Missions::Tasks[taskID]->task["m_iHMissionID"]; lastDist = dist; @@ -981,7 +981,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock pathPoints.push_back(marker); // map from player TableData::RunningNPCPaths[plr->iID] = std::make_pair(npc, pathPoints); - Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->appearanceData.iNPC_ID) + " is now following you"); + Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->id) + " is now following you"); updatePathMarkers(sock); return; } @@ -1008,8 +1008,8 @@ static void pathCommand(std::string full, std::vector& args, CNSock // /path here if (args[1] == "here") { // bring the NPC to where the player is standing - Transport::NPCQueues.erase(npc->appearanceData.iNPC_ID); // delete transport queue - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, npc->instanceID, 0); + Transport::NPCQueues.erase(npc->id); // delete transport queue + NPCManager::updateNPCPosition(npc->id, plr->x, plr->y, plr->z, npc->instanceID, 0); npc->disappearFromViewOf(sock); npc->enterIntoViewOf(sock); Chat::sendServerMessage(sock, "[PATH] Come here"); @@ -1047,9 +1047,9 @@ static void pathCommand(std::string full, std::vector& args, CNSock speed = speedArg; } // return NPC to home - Transport::NPCQueues.erase(npc->appearanceData.iNPC_ID); // delete transport queue + Transport::NPCQueues.erase(npc->id); // delete transport queue BaseNPC* home = entry->second[0]; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, home->x, home->y, home->z, npc->instanceID, 0); + NPCManager::updateNPCPosition(npc->id, home->x, home->y, home->z, npc->instanceID, 0); npc->disappearFromViewOf(sock); npc->enterIntoViewOf(sock); @@ -1065,7 +1065,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock Transport::lerp(&keyframes, from, to, speed); // lerp from A to B from = to; // update point A } - Transport::NPCQueues[npc->appearanceData.iNPC_ID] = keyframes; + Transport::NPCQueues[npc->id] = keyframes; entry->second.pop_back(); // remove temp end point Chat::sendServerMessage(sock, "[PATH] Testing NPC path"); @@ -1075,9 +1075,9 @@ static void pathCommand(std::string full, std::vector& args, CNSock // /path cancel if (args[1] == "cancel") { // return NPC to home - Transport::NPCQueues.erase(npc->appearanceData.iNPC_ID); // delete transport queue + Transport::NPCQueues.erase(npc->id); // delete transport queue BaseNPC* home = entry->second[0]; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, home->x, home->y, home->z, npc->instanceID, 0); + NPCManager::updateNPCPosition(npc->id, home->x, home->y, home->z, npc->instanceID, 0); npc->disappearFromViewOf(sock); npc->enterIntoViewOf(sock); // deallocate markers @@ -1087,7 +1087,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock } // unmap TableData::RunningNPCPaths.erase(plr->iID); - Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->appearanceData.iNPC_ID) + " is no longer following you"); + Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->id) + " is no longer following you"); return; } @@ -1115,9 +1115,9 @@ static void pathCommand(std::string full, std::vector& args, CNSock } // return NPC to home and set path to repeat - Transport::NPCQueues.erase(npc->appearanceData.iNPC_ID); // delete transport queue + Transport::NPCQueues.erase(npc->id); // delete transport queue BaseNPC* home = entry->second[0]; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, home->x, home->y, home->z, npc->instanceID, 0); + NPCManager::updateNPCPosition(npc->id, home->x, home->y, home->z, npc->instanceID, 0); npc->disappearFromViewOf(sock); npc->enterIntoViewOf(sock); npc->loopingPath = true; @@ -1134,7 +1134,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock Transport::lerp(&keyframes, from, to, speed); // lerp from A to B from = to; // update point A } - Transport::NPCQueues[npc->appearanceData.iNPC_ID] = keyframes; + Transport::NPCQueues[npc->id] = keyframes; entry->second.pop_back(); // remove temp end point // save to gruntwork @@ -1161,7 +1161,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock finishedPath.isLoop = true; finishedPath.speed = speed; finishedPath.points = finalPoints; - finishedPath.targetIDs.push_back(npc->appearanceData.iNPC_ID); + finishedPath.targetIDs.push_back(npc->id); TableData::FinishedNPCPaths.push_back(finishedPath); @@ -1173,7 +1173,7 @@ static void pathCommand(std::string full, std::vector& args, CNSock // unmap TableData::RunningNPCPaths.erase(plr->iID); - Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->appearanceData.iNPC_ID) + " is no longer following you"); + Chat::sendServerMessage(sock, "[PATH] NPC " + std::to_string(npc->id) + " is no longer following you"); TableData::flush(); Chat::sendServerMessage(sock, "[PATH] Path saved to gruntwork"); diff --git a/src/Eggs.cpp b/src/Eggs.cpp index 52fa35e..f29d5cc 100644 --- a/src/Eggs.cpp +++ b/src/Eggs.cpp @@ -135,7 +135,7 @@ static void eggStep(CNServer* serv, time_t currTime) { // respawn it egg->dead = false; egg->deadUntil = 0; - egg->appearanceData.iHP = 400; + egg->hp = 400; Chunking::addEntityToChunks(Chunking::getViewableChunks(egg->chunkPos), {npc.first}); } @@ -180,7 +180,7 @@ static void eggPickup(CNSocket* sock, CNPacketData* data) { } */ - int typeId = egg->appearanceData.iNPCType; + int typeId = egg->type; if (EggTypes.find(typeId) == EggTypes.end()) { std::cout << "[WARN] Egg Type " << typeId << " not found!" << std::endl; return; @@ -255,7 +255,7 @@ static void eggPickup(CNSocket* sock, CNPacketData* data) { Chunking::removeEntityFromChunks(Chunking::getViewableChunks(egg->chunkPos), eggRef); egg->dead = true; egg->deadUntil = getTime() + (time_t)type->regen * 1000; - egg->appearanceData.iHP = 0; + egg->hp = 0; } } diff --git a/src/Entities.cpp b/src/Entities.cpp index ea52eba..a859af2 100644 --- a/src/Entities.cpp +++ b/src/Entities.cpp @@ -39,15 +39,26 @@ Entity *EntityRef::getEntity() const { return NPCManager::NPCs[id]; } +sNPCAppearanceData BaseNPC::getAppearanceData() { + sNPCAppearanceData data = {}; + data.iAngle = angle; + data.iBarkerType = barkerType; + data.iConditionBitFlag = cbf; + data.iHP = hp; + data.iNPCType = type; + data.iNPC_ID = id; + data.iX = x; + data.iY = y; + data.iZ = z; + return data; +} + /* * Entity coming into view. */ void BaseNPC::enterIntoViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_NPC_ENTER, pkt); - pkt.NPCAppearanceData = appearanceData; - pkt.NPCAppearanceData.iX = x; - pkt.NPCAppearanceData.iY = y; - pkt.NPCAppearanceData.iZ = z; + pkt.NPCAppearanceData = getAppearanceData(); sock->sendPacket(pkt, P_FE2CL_NPC_ENTER); } @@ -56,7 +67,7 @@ void Bus::enterIntoViewOf(CNSocket *sock) { // TODO: Potentially decouple this from BaseNPC? pkt.AppearanceData = { - 3, appearanceData.iNPC_ID, appearanceData.iNPCType, + 3, id, type, x, y, z }; @@ -66,28 +77,36 @@ void Bus::enterIntoViewOf(CNSocket *sock) { void Egg::enterIntoViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_SHINY_ENTER, pkt); - Eggs::npcDataToEggData(x, y, z, &appearanceData, &pkt.ShinyAppearanceData); + // TODO: Potentially decouple this from BaseNPC? + pkt.ShinyAppearanceData = { + id, type, 0, // client doesn't care about map num + x, y, z + }; sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER); } - + +sPCAppearanceData Player::getAppearanceData() { + sPCAppearanceData data = {}; + data.iID = iID; + data.iHP = HP; + data.iLv = level; + data.iX = x; + data.iY = y; + data.iZ = z; + data.iAngle = angle; + data.PCStyle = PCStyle; + data.Nano = Nanos[activeNano]; + data.iPCState = iPCState; + data.iSpecialState = iSpecialState; + memcpy(data.ItemEquip, Equip, sizeof(sItemBase) * AEQUIP_COUNT); + return data; +} + // TODO: this is less effiecient than it was, because of memset() void Player::enterIntoViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_PC_NEW, pkt); - - pkt.PCAppearanceData.iID = iID; - pkt.PCAppearanceData.iHP = HP; - pkt.PCAppearanceData.iLv = level; - pkt.PCAppearanceData.iX = x; - pkt.PCAppearanceData.iY = y; - pkt.PCAppearanceData.iZ = z; - pkt.PCAppearanceData.iAngle = angle; - pkt.PCAppearanceData.PCStyle = PCStyle; - pkt.PCAppearanceData.Nano = Nanos[activeNano]; - pkt.PCAppearanceData.iPCState = iPCState; - pkt.PCAppearanceData.iSpecialState = iSpecialState; - memcpy(pkt.PCAppearanceData.ItemEquip, Equip, sizeof(sItemBase) * AEQUIP_COUNT); - + pkt.PCAppearanceData = getAppearanceData(); sock->sendPacket(pkt, P_FE2CL_PC_NEW); } @@ -96,20 +115,20 @@ void Player::enterIntoViewOf(CNSocket *sock) { */ void BaseNPC::disappearFromViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_NPC_EXIT, pkt); - pkt.iNPC_ID = appearanceData.iNPC_ID; + pkt.iNPC_ID = id; sock->sendPacket(pkt, P_FE2CL_NPC_EXIT); } void Bus::disappearFromViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, pkt); pkt.eTT = 3; - pkt.iT_ID = appearanceData.iNPC_ID; + pkt.iT_ID = id; sock->sendPacket(pkt, P_FE2CL_TRANSPORTATION_EXIT); } void Egg::disappearFromViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_SHINY_EXIT, pkt); - pkt.iShinyID = appearanceData.iNPC_ID; + pkt.iShinyID = id; sock->sendPacket(pkt, P_FE2CL_SHINY_EXIT); } diff --git a/src/Entities.hpp b/src/Entities.hpp index a231541..9e56ea0 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -74,25 +74,31 @@ struct EntityRef { */ class BaseNPC : public Entity { public: - sNPCAppearanceData appearanceData = {}; + int id; + int type; + int hp; + int angle; + int cbf; + int barkerType; bool loopingPath = false; - BaseNPC(int _X, int _Y, int _Z, int angle, uint64_t iID, int t, int id) { // XXX + BaseNPC(int _X, int _Y, int _Z, int _A, uint64_t iID, int t, int _id) { // XXX x = _X; y = _Y; z = _Z; - appearanceData.iNPCType = t; - appearanceData.iHP = 400; - appearanceData.iAngle = angle; - appearanceData.iConditionBitFlag = 0; - appearanceData.iBarkerType = 0; - appearanceData.iNPC_ID = id; - + type = t; + hp = 400; + angle = _A; + cbf = 0; + barkerType = 0; + id = _id; instanceID = iID; }; virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; + + sNPCAppearanceData getAppearanceData(); }; struct CombatNPC : public BaseNPC { @@ -115,7 +121,7 @@ struct CombatNPC : public BaseNPC { _stepAI(this, currTime); } - virtual bool isAlive() override { return appearanceData.iHP > 0; } + virtual bool isAlive() override { return hp > 0; } }; // Mob is in MobAI.hpp, Player is in Player.hpp diff --git a/src/Items.cpp b/src/Items.cpp index 9328c66..166526b 100644 --- a/src/Items.cpp +++ b/src/Items.cpp @@ -822,12 +822,12 @@ static void giveSingleDrop(CNSocket *sock, Mob* mob, int mobDropId, const DropRo void Items::giveMobDrop(CNSocket *sock, Mob* mob, const DropRoll& rolled, const DropRoll& eventRolled) { // sanity check - if (Items::MobToDropMap.find(mob->appearanceData.iNPCType) == Items::MobToDropMap.end()) { - std::cout << "[WARN] Mob ID " << mob->appearanceData.iNPCType << " has no drops assigned" << std::endl; + if (Items::MobToDropMap.find(mob->type) == Items::MobToDropMap.end()) { + std::cout << "[WARN] Mob ID " << mob->type << " has no drops assigned" << std::endl; return; } // find mob drop id - int mobDropId = Items::MobToDropMap[mob->appearanceData.iNPCType]; + int mobDropId = Items::MobToDropMap[mob->type]; giveSingleDrop(sock, mob, mobDropId, rolled); diff --git a/src/Missions.cpp b/src/Missions.cpp index 9d2dd69..bd6891e 100644 --- a/src/Missions.cpp +++ b/src/Missions.cpp @@ -373,9 +373,9 @@ static void taskStart(CNSocket* sock, CNPacketData* data) { for (EntityRef ref : chunk->entities) { if (ref.type != EntityType::PLAYER) { BaseNPC* npc = (BaseNPC*)ref.getEntity(); - NPCPath* path = Transport::findApplicablePath(npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, missionData->iTaskNum); + NPCPath* path = Transport::findApplicablePath(npc->id, npc->type, missionData->iTaskNum); if (path != nullptr) { - Transport::constructPathNPC(npc->appearanceData.iNPC_ID, path); + Transport::constructPathNPC(npc->id, path); return; } } diff --git a/src/MobAI.cpp b/src/MobAI.cpp index 562ccce..e396499 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -47,13 +47,13 @@ static std::pair lerp(int x1, int y1, int x2, int y2, int speed) { void MobAI::clearDebuff(Mob *mob) { mob->skillStyle = -1; - mob->appearanceData.iConditionBitFlag = 0; + mob->cbf = 0; mob->unbuffTimes.clear(); INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); pkt1.eCT = 2; - pkt1.iID = mob->appearanceData.iNPC_ID; - pkt1.iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + pkt1.iID = mob->id; + pkt1.iConditionBitFlag = mob->cbf; NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); } @@ -196,7 +196,7 @@ static void dealCorruption(Mob *mob, std::vector targetData, int skillID, i sP_FE2CL_NPC_SKILL_CORRUPTION_HIT *resp = (sP_FE2CL_NPC_SKILL_CORRUPTION_HIT*)respbuf; sCAttackResult *respdata = (sCAttackResult*)(respbuf+sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_HIT)); - resp->iNPC_ID = mob->appearanceData.iNPC_ID; + resp->iNPC_ID = mob->id; resp->iSkillID = skillID; resp->iStyle = style; resp->iValue1 = plr->x; @@ -247,7 +247,7 @@ static void dealCorruption(Mob *mob, std::vector targetData, int skillID, i if (plr->Nanos[plr->activeNano].iStamina > 150) respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina = 150; // fire damage power disguised as a corruption attack back at the enemy - std::vector targetData2 = {1, mob->appearanceData.iNPC_ID, 0, 0, 0}; + std::vector targetData2 = {1, mob->id, 0, 0, 0}; for (auto& pwr : Abilities::Powers) if (pwr.skillType == EST_DAMAGE) pwr.handle(sock, targetData2, plr->activeNano, skillID, 0, 200); @@ -362,7 +362,7 @@ static void useAbilities(Mob *mob, time_t currTime) { if (random < prob1 + prob2) { // corruption windup int skillID = (int)mob->data["m_iCorruptionType"]; INITSTRUCT(sP_FE2CL_NPC_SKILL_CORRUPTION_READY, pkt); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; + pkt.iNPC_ID = mob->id; pkt.iSkillID = skillID; pkt.iValue1 = plr->x; pkt.iValue2 = plr->y; @@ -381,7 +381,7 @@ static void useAbilities(Mob *mob, time_t currTime) { if (random < prob1 + prob2 + prob3) { // eruption windup int skillID = (int)mob->data["m_iMegaType"]; INITSTRUCT(sP_FE2CL_NPC_SKILL_READY, pkt); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; + pkt.iNPC_ID = mob->id; pkt.iSkillID = skillID; pkt.iValue1 = mob->hitX = plr->x; pkt.iValue2 = mob->hitY = plr->y; @@ -406,13 +406,13 @@ void MobAI::enterCombat(CNSocket *sock, Mob *mob) { mob->roamZ = mob->z; int skillID = (int)mob->data["m_iPassiveBuff"]; // cast passive - std::vector targetData = {1, mob->appearanceData.iNPC_ID, 0, 0, 0}; + std::vector targetData = {1, mob->id, 0, 0, 0}; for (auto& pwr : Abilities::Powers) if (pwr.skillType == Abilities::SkillTable[skillID].skillType) pwr.handle(mob, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]); for (NPCEvent& event : NPCManager::NPCEvents) // trigger an ON_COMBAT - if (event.trigger == ON_COMBAT && event.npcType == mob->appearanceData.iNPCType) + if (event.trigger == ON_COMBAT && event.npcType == mob->type) event.handler(sock, mob); } @@ -426,18 +426,18 @@ static void drainMobHP(Mob *mob, int amount) { sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf; sSkillResult_Damage *drain = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK)); - pkt->iID = mob->appearanceData.iNPC_ID; + pkt->iID = mob->id; pkt->eCT = 4; // mob pkt->iTB_ID = ECSB_BOUNDINGBALL; drain->eCT = 4; - drain->iID = mob->appearanceData.iNPC_ID; + drain->iID = mob->id; drain->iDamage = amount; - drain->iHP = mob->appearanceData.iHP -= amount; + drain->iHP = mob->hp -= amount; NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); - if (mob->appearanceData.iHP <= 0) + if (mob->hp <= 0) Combat::killMob(mob->target, mob); } @@ -448,14 +448,14 @@ static void deadStep(Mob *mob, time_t currTime) { INITSTRUCT(sP_FE2CL_NPC_EXIT, pkt); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; + pkt.iNPC_ID = mob->id; NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); // if it was summoned, mark it for removal if (mob->summoned) { std::cout << "[INFO] Queueing killed summoned mob for removal" << std::endl; - NPCManager::queueNPCRemoval(mob->appearanceData.iNPC_ID); + NPCManager::queueNPCRemoval(mob->id); return; } @@ -466,15 +466,15 @@ static void 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) + if (mob->groupLeader == mob->id) roamingStep(mob, currTime); if (mob->killedTime != 0 && currTime - mob->killedTime < mob->regenTime * 100) return; - std::cout << "respawning mob " << mob->appearanceData.iNPC_ID << " with HP = " << mob->maxHealth << std::endl; + std::cout << "respawning mob " << mob->id << " with HP = " << mob->maxHealth << std::endl; - mob->appearanceData.iHP = mob->maxHealth; + mob->hp = mob->maxHealth; mob->state = MobState::ROAMING; // if mob is a group leader/follower, spawn where the group is. @@ -491,10 +491,7 @@ static void deadStep(Mob *mob, time_t currTime) { INITSTRUCT(sP_FE2CL_NPC_NEW, pkt); - pkt.NPCAppearanceData = mob->appearanceData; - pkt.NPCAppearanceData.iX = mob->x; - pkt.NPCAppearanceData.iY = mob->y; - pkt.NPCAppearanceData.iZ = mob->z; + pkt.NPCAppearanceData = mob->getAppearanceData(); // notify all nearby players NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW)); @@ -532,13 +529,13 @@ static void combatStep(Mob *mob, time_t currTime) { // drain if (mob->skillStyle < 0 && (mob->lastDrainTime == 0 || currTime - mob->lastDrainTime >= 1000) - && mob->appearanceData.iConditionBitFlag & CSB_BIT_BOUNDINGBALL) { + && mob->cbf & CSB_BIT_BOUNDINGBALL) { drainMobHP(mob, mob->maxHealth / 20); // lose 5% every second mob->lastDrainTime = currTime; } // if drain killed the mob, return early - if (mob->appearanceData.iHP <= 0) + if (mob->hp <= 0) return; // unbuffing @@ -546,12 +543,12 @@ static void combatStep(Mob *mob, time_t currTime) { while (it != mob->unbuffTimes.end()) { if (currTime >= it->second) { - mob->appearanceData.iConditionBitFlag &= ~it->first; + mob->cbf &= ~it->first; INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); pkt1.eCT = 2; - pkt1.iID = mob->appearanceData.iNPC_ID; - pkt1.iConditionBitFlag = mob->appearanceData.iConditionBitFlag; + pkt1.iID = mob->id; + pkt1.iConditionBitFlag = mob->cbf; NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); it = mob->unbuffTimes.erase(it); @@ -561,7 +558,7 @@ static void combatStep(Mob *mob, time_t currTime) { } // skip attack if stunned or asleep - if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ)) { + if (mob->cbf & (CSB_BIT_STUN|CSB_BIT_MEZ)) { mob->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up. return; } @@ -587,7 +584,7 @@ static void combatStep(Mob *mob, time_t currTime) { mob->nextAttack = 0; // halve movement speed if snared - if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED) + if (mob->cbf & CSB_BIT_DN_MOVE_SPEED) speed /= 2; int targetX = plr->x; @@ -602,11 +599,11 @@ static void combatStep(Mob *mob, time_t currTime) { if (distanceToTravel < speed*2/5 && currTime >= mob->nextAttack) mob->nextAttack = 0; - NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->z, mob->instanceID, mob->appearanceData.iAngle); + NPCManager::updateNPCPosition(mob->id, targ.first, targ.second, mob->z, mob->instanceID, mob->angle); INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; + pkt.iNPC_ID = mob->id; pkt.iSpeed = speed; pkt.iToX = mob->x = targ.first; pkt.iToY = mob->y = targ.second; @@ -664,7 +661,7 @@ static void roamingStep(Mob *mob, time_t currTime) { if (mob->staticPath) return; - if (mob->groupLeader != 0 && mob->groupLeader != mob->appearanceData.iNPC_ID) // don't roam by yourself without group leader + if (mob->groupLeader != 0 && mob->groupLeader != mob->id) // don't roam by yourself without group leader return; /* @@ -704,7 +701,7 @@ static void roamingStep(Mob *mob, time_t currTime) { farY = std::clamp(farY, yStart, yStart + mob->idleRange); // halve movement speed if snared - if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED) + if (mob->cbf & CSB_BIT_DN_MOVE_SPEED) speed /= 2; std::queue queue; @@ -713,9 +710,9 @@ static void roamingStep(Mob *mob, time_t currTime) { // add a route to the queue; to be processed in Transport::stepNPCPathing() Transport::lerp(&queue, from, to, speed); - Transport::NPCQueues[mob->appearanceData.iNPC_ID] = queue; + Transport::NPCQueues[mob->id] = queue; - if (mob->groupLeader != 0 && mob->groupLeader == mob->appearanceData.iNPC_ID) { + if (mob->groupLeader != 0 && mob->groupLeader == mob->id) { // make followers follow this npc. for (int i = 0; i < 4; i++) { if (mob->groupMember[i] == 0) @@ -731,7 +728,7 @@ static void roamingStep(Mob *mob, time_t currTime) { from = { followerMob->x, followerMob->y, followerMob->z }; to = { farX + followerMob->offsetX, farY + followerMob->offsetY, followerMob->z }; Transport::lerp(&queue2, from, to, speed); - Transport::NPCQueues[followerMob->appearanceData.iNPC_ID] = queue2; + Transport::NPCQueues[followerMob->id] = queue2; } } } @@ -751,7 +748,7 @@ static void retreatStep(Mob *mob, time_t currTime) { auto targ = lerp(mob->x, mob->y, mob->roamX, mob->roamY, (int)mob->speed*4/5); - pkt.iNPC_ID = mob->appearanceData.iNPC_ID; + pkt.iNPC_ID = mob->id; pkt.iSpeed = (int)mob->speed * 2; pkt.iToX = mob->x = targ.first; pkt.iToY = mob->y = targ.second; @@ -766,10 +763,10 @@ static void retreatStep(Mob *mob, time_t currTime) { //if (distance <= mob->data["m_iIdleRange"]) { if (distance <= 10) { // retreat back to the spawn point mob->state = MobState::ROAMING; - mob->appearanceData.iHP = mob->maxHealth; + mob->hp = mob->maxHealth; mob->killedTime = 0; mob->nextAttack = 0; - mob->appearanceData.iConditionBitFlag = 0; + mob->cbf = 0; // cast a return home heal spell, this is the right way(tm) std::vector targetData = {1, 0, 0, 0, 0}; diff --git a/src/MobAI.hpp b/src/MobAI.hpp index 452f6c6..dbf9c84 100644 --- a/src/MobAI.hpp +++ b/src/MobAI.hpp @@ -72,10 +72,10 @@ struct Mob : public CombatNPC { offsetX = 0; offsetY = 0; - appearanceData.iConditionBitFlag = 0; + cbf = 0; // NOTE: there appear to be discrepancies in the dump - appearanceData.iHP = maxHealth; + hp = maxHealth; kind = EntityType::MOB; _stepAI = MobAI::step; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index eb262d9..50b3a0a 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -67,7 +67,7 @@ void NPCManager::destroyNPC(int32_t id) { void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, uint64_t I, int angle) { BaseNPC* npc = NPCs[id]; - npc->appearanceData.iAngle = angle; + npc->angle = angle; ChunkPos oldChunk = npc->chunkPos; ChunkPos newChunk = Chunking::chunkPosAt(X, Y, I); npc->x = X; @@ -154,7 +154,7 @@ static void npcSummonHandler(CNSocket* sock, CNPacketData* data) { for (int i = 0; i < req->iNPCCnt; i++) { BaseNPC *npc = summonNPC(plr->x, plr->y, plr->z, plr->instanceID, req->iNPCType); - updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, plr->instanceID, 0); + updateNPCPosition(npc->id, plr->x, plr->y, plr->z, plr->instanceID, 0); } } @@ -305,16 +305,16 @@ static void lordFuseStageTwo(CNSocket *sock, BaseNPC *npc) { // Blastons, Heal Mob *newbody = (Mob*)NPCManager::summonNPC(oldbody->spawnX, oldbody->spawnY, oldbody->spawnZ, plr->instanceID, 2467); - newbody->appearanceData.iAngle = oldbody->appearanceData.iAngle; - NPCManager::updateNPCPosition(newbody->appearanceData.iNPC_ID, newbody->spawnX, newbody->spawnY, newbody->spawnZ, - plr->instanceID, oldbody->appearanceData.iAngle); + newbody->angle = oldbody->angle; + NPCManager::updateNPCPosition(newbody->id, newbody->spawnX, newbody->spawnY, newbody->spawnZ, + plr->instanceID, oldbody->angle); // right arm, Adaptium, Stun Mob *arm = (Mob*)NPCManager::summonNPC(oldbody->spawnX - 600, oldbody->spawnY, oldbody->spawnZ, plr->instanceID, 2469); - arm->appearanceData.iAngle = oldbody->appearanceData.iAngle; - NPCManager::updateNPCPosition(arm->appearanceData.iNPC_ID, arm->spawnX, arm->spawnY, arm->spawnZ, - plr->instanceID, oldbody->appearanceData.iAngle); + arm->angle = oldbody->angle; + NPCManager::updateNPCPosition(arm->id, arm->spawnX, arm->spawnY, arm->spawnZ, + plr->instanceID, oldbody->angle); } // summon left arm and stage 3 body @@ -327,16 +327,16 @@ static void lordFuseStageThree(CNSocket *sock, BaseNPC *npc) { // Cosmix, Damage Point Mob *newbody = (Mob*)NPCManager::summonNPC(oldbody->spawnX, oldbody->spawnY, oldbody->spawnZ, plr->instanceID, 2468); - newbody->appearanceData.iAngle = oldbody->appearanceData.iAngle; - NPCManager::updateNPCPosition(newbody->appearanceData.iNPC_ID, newbody->spawnX, newbody->spawnY, newbody->spawnZ, - plr->instanceID, oldbody->appearanceData.iAngle); + newbody->angle = oldbody->angle; + NPCManager::updateNPCPosition(newbody->id, newbody->spawnX, newbody->spawnY, newbody->spawnZ, + plr->instanceID, oldbody->angle); // Blastons, Heal Mob *arm = (Mob*)NPCManager::summonNPC(oldbody->spawnX + 600, oldbody->spawnY, oldbody->spawnZ, plr->instanceID, 2470); - arm->appearanceData.iAngle = oldbody->appearanceData.iAngle; - NPCManager::updateNPCPosition(arm->appearanceData.iNPC_ID, arm->spawnX, arm->spawnY, arm->spawnZ, - plr->instanceID, oldbody->appearanceData.iAngle); + arm->angle = oldbody->angle; + NPCManager::updateNPCPosition(arm->id, arm->spawnX, arm->spawnY, arm->spawnZ, + plr->instanceID, oldbody->angle); } std::vector NPCManager::NPCEvents = { diff --git a/src/Player.hpp b/src/Player.hpp index 7821593..0c425d1 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -89,4 +89,6 @@ struct Player : public Entity { virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; + + sPCAppearanceData getAppearanceData(); }; diff --git a/src/PlayerMovement.cpp b/src/PlayerMovement.cpp index c19af28..b00f15b 100644 --- a/src/PlayerMovement.cpp +++ b/src/PlayerMovement.cpp @@ -33,7 +33,7 @@ static void movePlayer(CNSocket* sock, CNPacketData* data) { // [gruntwork] check if player has a follower and move it if (TableData::RunningNPCPaths.find(plr->iID) != TableData::RunningNPCPaths.end()) { BaseNPC* follower = TableData::RunningNPCPaths[plr->iID].first; - Transport::NPCQueues.erase(follower->appearanceData.iNPC_ID); // erase existing points + Transport::NPCQueues.erase(follower->id); // erase existing points std::queue queue; Vec3 from = { follower->x, follower->y, follower->z }; float drag = 0.95f; // this ensures that they don't bump into the player @@ -45,7 +45,7 @@ static void movePlayer(CNSocket* sock, CNPacketData* data) { // add a route to the queue; to be processed in Transport::stepNPCPathing() Transport::lerp(&queue, from, to, NPC_DEFAULT_SPEED * 1.5); // little faster than typical - Transport::NPCQueues[follower->appearanceData.iNPC_ID] = queue; + Transport::NPCQueues[follower->id] = queue; } } diff --git a/src/TableData.cpp b/src/TableData.cpp index 892509f..92a4aca 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -315,9 +315,9 @@ static void loadPaths(json& pathData, int32_t* nextId) { passedDistance -= SLIDER_GAP_SIZE; // step down // spawn a slider Bus* slider = new Bus(point.x, point.y, point.z, 0, INSTANCE_OVERWORLD, 1, (*nextId)--); - NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider; - NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->x, slider->y, slider->z, INSTANCE_OVERWORLD, 0); - Transport::NPCQueues[slider->appearanceData.iNPC_ID] = route; + NPCManager::NPCs[slider->id] = slider; + NPCManager::updateNPCPosition(slider->id, slider->x, slider->y, slider->z, INSTANCE_OVERWORLD, 0); + Transport::NPCQueues[slider->id] = route; } // rotate route.pop(); @@ -755,7 +755,7 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) { if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) continue; // NPC not found BaseNPC* npc = NPCManager::NPCs[npcID]; - npc->appearanceData.iAngle = angle; + npc->angle = angle; RunningNPCRotations[npcID] = angle; } @@ -768,8 +768,8 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) { if (NPCManager::NPCs.find(npcID) == NPCManager::NPCs.end()) continue; // NPC not found BaseNPC* npc = NPCManager::NPCs[npcID]; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, - npc->z, instanceID, npc->appearanceData.iAngle); + NPCManager::updateNPCPosition(npc->id, npc->x, npc->y, + npc->z, instanceID, npc->angle); RunningNPCMapNumbers[npcID] = instanceID; } @@ -794,9 +794,9 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) { npc = new BaseNPC(mob["iX"], mob["iY"], mob["iZ"], mob["iAngle"], instanceID, mob["iNPCType"], id); } - NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; - RunningMobs[npc->appearanceData.iNPC_ID] = npc; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]); + NPCManager::NPCs[npc->id] = npc; + RunningMobs[npc->id] = npc; + NPCManager::updateNPCPosition(npc->id, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]); } // mob groups @@ -840,7 +840,7 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) { tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"]; tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"]; - tmpFol->groupLeader = tmp->appearanceData.iNPC_ID; + tmpFol->groupLeader = tmp->id; tmp->groupMember[followerCount++] = *nextId; (*nextId)--; @@ -850,7 +850,7 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) { std::cout << "[WARN] Mob group leader with ID " << *nextId << " has too many followers (" << followers.size() << ")\n"; } - RunningGroups[tmp->appearanceData.iNPC_ID] = tmp; // store as running + RunningGroups[tmp->id] = tmp; // store as running } auto eggs = gruntwork["eggs"]; @@ -1003,7 +1003,7 @@ static void loadMobs(json& npcData, int32_t* nextId) { tmpFol->offsetX = follower.find("iOffsetX") == follower.end() ? 0 : (int)follower["iOffsetX"]; tmpFol->offsetY = follower.find("iOffsetY") == follower.end() ? 0 : (int)follower["iOffsetY"]; - tmpFol->groupLeader = tmp->appearanceData.iNPC_ID; + tmpFol->groupLeader = tmp->id; tmp->groupMember[followerCount++] = *nextId; (*nextId)--; @@ -1238,13 +1238,13 @@ void TableData::flush() { } // NOTE: this format deviates slightly from the one in mobs.json - mob["iNPCType"] = (int)npc->appearanceData.iNPCType; + mob["iNPCType"] = (int)npc->type; mob["iX"] = x; mob["iY"] = y; mob["iZ"] = z; mob["iMapNum"] = MAPNUM(npc->instanceID); // this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh - mob["iAngle"] = npc->appearanceData.iAngle; + mob["iAngle"] = npc->angle; // it's called mobs, but really it's everything gruntwork["mobs"].push_back(mob); @@ -1264,7 +1264,7 @@ void TableData::flush() { x = m->spawnX; y = m->spawnY; z = m->spawnZ; - if (m->groupLeader != m->appearanceData.iNPC_ID) { // make sure this is a leader + if (m->groupLeader != m->id) { // make sure this is a leader std::cout << "[WARN] Non-leader mob found in running groups; ignoring\n"; continue; } @@ -1285,13 +1285,13 @@ void TableData::flush() { } // NOTE: this format deviates slightly from the one in mobs.json - mob["iNPCType"] = (int)npc->appearanceData.iNPCType; + mob["iNPCType"] = (int)npc->type; mob["iX"] = x; mob["iY"] = y; mob["iZ"] = z; mob["iMapNum"] = MAPNUM(npc->instanceID); // this is a bit imperfect, since this is a live angle, not a spawn angle so it'll change often, but eh - mob["iAngle"] = npc->appearanceData.iAngle; + mob["iAngle"] = npc->angle; // followers while (followers.size() > 0) { @@ -1300,7 +1300,7 @@ void TableData::flush() { // populate JSON entry json fol; - fol["iNPCType"] = follower->appearanceData.iNPCType; + fol["iNPCType"] = follower->type; fol["iOffsetX"] = follower->offsetX; fol["iOffsetY"] = follower->offsetY; @@ -1325,7 +1325,7 @@ void TableData::flush() { int mapnum = MAPNUM(npc->instanceID); if (mapnum != 0) egg["iMapNum"] = mapnum; - egg["iType"] = npc->appearanceData.iNPCType; + egg["iType"] = npc->type; gruntwork["eggs"].push_back(egg); } diff --git a/src/Transport.cpp b/src/Transport.cpp index 7ed5836..36b0de6 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -276,7 +276,7 @@ static void stepNPCPathing() { int distanceBetween = hypot(dXY, point.z - npc->z); // total distance // update NPC location to update viewables - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, point.x, point.y, point.z, npc->instanceID, npc->appearanceData.iAngle); + NPCManager::updateNPCPosition(npc->id, point.x, point.y, point.z, npc->instanceID, npc->angle); // TODO: move walking logic into Entity stack switch (npc->kind) { @@ -284,7 +284,7 @@ static void stepNPCPathing() { INITSTRUCT(sP_FE2CL_TRANSPORTATION_MOVE, busMove); busMove.eTT = 3; - busMove.iT_ID = npc->appearanceData.iNPC_ID; + busMove.iT_ID = npc->id; busMove.iMoveStyle = 0; // ??? busMove.iToX = point.x; busMove.iToY = point.y; @@ -298,7 +298,7 @@ static void stepNPCPathing() { /* fallthrough */ default: INITSTRUCT(sP_FE2CL_NPC_MOVE, move); - move.iNPC_ID = npc->appearanceData.iNPC_ID; + move.iNPC_ID = npc->id; move.iMoveStyle = 0; // ??? move.iToX = point.x; move.iToY = point.y;