From 4b834579c56751df44550de6a71d2f7b70f93558 Mon Sep 17 00:00:00 2001 From: gsemaj Date: Wed, 13 Apr 2022 15:09:43 -0400 Subject: [PATCH] [refactor] Remaining ICombatant implementation --- src/Abilities.cpp | 766 ----------------------------------------- src/Abilities.hpp | 29 -- src/Chunking.cpp | 26 +- src/Combat.cpp | 121 ++----- src/CustomCommands.cpp | 16 +- src/Eggs.cpp | 40 ++- src/Entities.cpp | 8 +- src/Entities.hpp | 38 +- src/Groups.cpp | 13 +- src/Missions.cpp | 2 +- src/MobAI.cpp | 450 +++++++++++++----------- src/MobAI.hpp | 39 ++- src/NPCManager.cpp | 6 +- src/Nanos.cpp | 29 +- src/Player.hpp | 2 +- src/PlayerManager.cpp | 5 +- src/PlayerManager.hpp | 2 +- src/TableData.cpp | 6 +- src/Transport.cpp | 10 +- 19 files changed, 412 insertions(+), 1196 deletions(-) diff --git a/src/Abilities.cpp b/src/Abilities.cpp index d0c2a44..8c7f84d 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -6,774 +6,8 @@ #include "Groups.hpp" #include "Eggs.hpp" -/* - * TODO: This file is in desperate need of deduplication and rewriting. - */ - std::map Abilities::SkillTable; -/* - * targetData approach - * first integer is the count - * second to fifth integers are IDs, these can be either player iID or mob's iID - */ -std::vector Abilities::findTargets(Player* plr, int skillID, CNPacketData* data) { - std::vector tD(5); - - if (SkillTable[skillID].targetType <= 2 && data != nullptr) { // client gives us the targets - sP_CL2FE_REQ_NANO_SKILL_USE* pkt = (sP_CL2FE_REQ_NANO_SKILL_USE*)data->buf; - - // validate request check - if (!validInVarPacket(sizeof(sP_CL2FE_REQ_NANO_SKILL_USE), pkt->iTargetCnt, sizeof(int32_t), data->size)) { - std::cout << "[WARN] bad sP_CL2FE_REQ_NANO_SKILL_USE packet size" << std::endl; - return tD; - } - - int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_NANO_SKILL_USE)); - tD[0] = pkt->iTargetCnt; - - for (int i = 0; i < pkt->iTargetCnt; i++) - tD[i+1] = pktdata[i]; - - } else if (SkillTable[skillID].targetType == 2) { // self target only - tD[0] = 1; - tD[1] = plr->iID; - - } else if (SkillTable[skillID].targetType == 3) { // entire group as target - Player *otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - - if (otherPlr == nullptr) - return tD; - - if (SkillTable[skillID].effectArea == 0) { // for buffs - tD[0] = otherPlr->groupCnt; - for (int i = 0; i < otherPlr->groupCnt; i++) - tD[i+1] = otherPlr->groupIDs[i]; - return tD; - } - - for (int i = 0; i < otherPlr->groupCnt; i++) { // group heals have an area limit - Player *otherPlr2 = PlayerManager::getPlayerFromID(otherPlr->groupIDs[i]); - if (otherPlr2 == nullptr) - continue; - if (true) {//hypot(otherPlr2->x - plr->x, otherPlr2->y - plr->y) < SkillTable[skillID].effectArea) { - tD[i+1] = otherPlr->groupIDs[i]; - tD[0] += 1; - } - } - } - - return tD; -} - -void Abilities::removeBuff(CNSocket* sock, std::vector targetData, int32_t bitFlag, int16_t timeBuffID, int16_t amount, bool groupPower) { - Player *plr = PlayerManager::getPlayer(sock); - - plr->iSelfConditionBitFlag &= ~bitFlag; - int groupFlags = 0; - - if (groupPower) { - plr->iGroupConditionBitFlag &= ~bitFlag; - Player *leader = PlayerManager::getPlayerFromID(plr->iIDGroup); - if (leader != nullptr) - groupFlags = Groups::getGroupFlags(leader); - } - - for (int i = 0; i < targetData[0]; i++) { - Player* varPlr = PlayerManager::getPlayerFromID(targetData[i+1]); - if (!((groupFlags | varPlr->iSelfConditionBitFlag) & bitFlag)) { - CNSocket* sockTo = PlayerManager::getSockFromID(targetData[i+1]); - if (sockTo == nullptr) - continue; // sanity check - - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); - resp.eCSTB = timeBuffID; // eCharStatusTimeBuffID - resp.eTBU = 2; // eTimeBuffUpdate - resp.eTBT = 1; // eTimeBuffType 1 means nano - varPlr->iConditionBitFlag &= ~bitFlag; - resp.iConditionBitFlag = varPlr->iConditionBitFlag |= groupFlags | varPlr->iSelfConditionBitFlag; - - if (amount > 0) - resp.TimeBuff.iValue = amount; - - sockTo->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - } - } -} - -int Abilities::applyBuff(CNSocket* sock, int skillID, int eTBU, int eTBT, int32_t groupFlags) { - if (SkillTable[skillID].drainType == 1) - return 0; - - int32_t bitFlag = 0; - - for (auto& pwr : Powers) { - if (pwr.skillType == SkillTable[skillID].skillType) { - bitFlag = pwr.bitFlag; - Player *plr = PlayerManager::getPlayer(sock); - if (eTBU == 1 || !((groupFlags | plr->iSelfConditionBitFlag) & bitFlag)) { - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); - resp.eCSTB = pwr.timeBuffID; - resp.eTBU = eTBU; - resp.eTBT = eTBT; - - if (eTBU == 1) - plr->iConditionBitFlag |= bitFlag; - else - plr->iConditionBitFlag &= ~bitFlag; - - resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; - resp.TimeBuff.iValue = SkillTable[skillID].powerIntensity[0]; - sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - } - return bitFlag; - } - } - - return 0; -} - namespace Abilities { -bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - if (NPCManager::NPCs.find(targetID) == NPCManager::NPCs.end()) { - std::cout << "[WARN] doDebuff: NPC ID not found" << std::endl; - return false; - } - - BaseNPC* npc = NPCManager::NPCs[targetID]; - if (npc->kind != EntityType::MOB) { - std::cout << "[WARN] doDebuff: NPC is not a mob" << std::endl; - return false; - } - - Mob* mob = (Mob*)npc; - mob->takeDamage(sock, 0); - - respdata[i].eCT = 4; - respdata[i].iID = mob->id; - respdata[i].bProtected = 1; - if (mob->skillStyle < 0 && mob->state != AIState::RETREAT - && !(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->cbf; - - return true; -} - -bool doBuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player *plr = nullptr; - CNSocket *sockTo = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - sockTo = pair.first; - plr = pair.second; - break; - } - } - - // player not found - if (sockTo == nullptr || plr == nullptr) { - std::cout << "[WARN] doBuff: player ID not found" << std::endl; - return false; - } - - respdata[i].eCT = 1; - respdata[i].iID = plr->iID; - respdata[i].iConditionBitFlag = 0; - - // only apply buffs if the player is actually alive - if (plr->HP > 0) { - respdata[i].iConditionBitFlag = bitFlag; - - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt); - pkt.eCSTB = timeBuffID; // eCharStatusTimeBuffID - pkt.eTBU = 1; // eTimeBuffUpdate - pkt.eTBT = 1; // eTimeBuffType 1 means nano - pkt.iConditionBitFlag = plr->iConditionBitFlag |= bitFlag; - - if (amount > 0) - pkt.TimeBuff.iValue = amount; - - sockTo->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - } - - return true; -} - -bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - if (NPCManager::NPCs.find(targetID) == NPCManager::NPCs.end()) { - // not sure how to best handle this - std::cout << "[WARN] doDamageNDebuff: NPC ID not found" << std::endl; - return false; - } - - BaseNPC* npc = NPCManager::NPCs[targetID]; - if (npc->kind != EntityType::MOB) { - std::cout << "[WARN] doDamageNDebuff: NPC is not a mob" << std::endl; - return false; - } - - Mob* mob = (Mob*)npc; - - mob->takeDamage(sock, 0); - - respdata[i].eCT = 4; - respdata[i].iDamage = duration / 10; - respdata[i].iID = mob->id; - respdata[i].iHP = mob->hp; - respdata[i].bProtected = 1; - if (mob->skillStyle < 0 && mob->state != AIState::RETREAT - && !(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->cbf; - - return true; -} - -bool doHeal(CNSocket *sock, sSkillResult_Heal_HP *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player *plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doHeal: player ID not found" << std::endl; - return false; - } - - int healedAmount = PC_MAXHEALTH(plr->level) * amount / 1000; - - // do not heal dead players - if (plr->HP <= 0) - healedAmount = 0; - - plr->HP += healedAmount; - - if (plr->HP > PC_MAXHEALTH(plr->level)) - plr->HP = PC_MAXHEALTH(plr->level); - - respdata[i].eCT = 1; - respdata[i].iID = plr->iID; - respdata[i].iHP = plr->HP; - respdata[i].iHealHP = healedAmount; - - return true; -} - -bool doDamage(CNSocket *sock, sSkillResult_Damage *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - if (NPCManager::NPCs.find(targetID) == NPCManager::NPCs.end()) { - // not sure how to best handle this - std::cout << "[WARN] doDamage: NPC ID not found" << std::endl; - return false; - } - - BaseNPC* npc = NPCManager::NPCs[targetID]; - if (npc->kind != EntityType::MOB) { - std::cout << "[WARN] doDamage: NPC is not a mob" << std::endl; - return false; - } - - Mob* mob = (Mob*)npc; - Player *plr = PlayerManager::getPlayer(sock); - - int damage = mob->takeDamage(sock, std::max(PC_MAXHEALTH(plr->level) * amount / 1000, mob->maxHealth * amount / 1000)); - - respdata[i].eCT = 4; - respdata[i].iDamage = damage; - respdata[i].iID = mob->id; - respdata[i].iHP = mob->hp; - - return true; -} - -/* - * NOTE: Leech is specially encoded. - * - * It manages to fit inside the nanoPower<>() mold with only a slight hack, - * but it really is it's own thing. There is a hard assumption that players - * will only every leech a single mob, and the sanity check that enforces that - * assumption is critical. - */ - -bool doLeech(CNSocket *sock, sSkillResult_Heal_HP *healdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - // this sanity check is VERY important - if (i != 0) { - std::cout << "[WARN] Player attempted to leech more than one mob!" << std::endl; - return false; - } - - sSkillResult_Damage *damagedata = (sSkillResult_Damage*)(((uint8_t*)healdata) + sizeof(sSkillResult_Heal_HP)); - Player *plr = PlayerManager::getPlayer(sock); - - int healedAmount = amount; - - plr->HP += healedAmount; - - if (plr->HP > PC_MAXHEALTH(plr->level)) - plr->HP = PC_MAXHEALTH(plr->level); - - healdata->eCT = 1; - healdata->iID = plr->iID; - healdata->iHP = plr->HP; - healdata->iHealHP = healedAmount; - - if (NPCManager::NPCs.find(targetID) == NPCManager::NPCs.end()) { - // not sure how to best handle this - std::cout << "[WARN] doLeech: NPC ID not found" << std::endl; - return false; - } - - BaseNPC* npc = NPCManager::NPCs[targetID]; - if (npc->kind != EntityType::MOB) { - std::cout << "[WARN] doLeech: NPC is not a mob" << std::endl; - return false; - } - - Mob* mob = (Mob*)npc; - - int damage = mob->takeDamage(sock, amount * 2); - - damagedata->eCT = 4; - damagedata->iDamage = damage; - damagedata->iID = mob->id; - damagedata->iHP = mob->hp; - - return true; -} - -bool doResurrect(CNSocket *sock, sSkillResult_Resurrect *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player *plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doResurrect: player ID not found" << std::endl; - return false; - } - - respdata[i].eCT = 1; - respdata[i].iID = plr->iID; - respdata[i].iRegenHP = plr->HP; - - return true; -} - -bool doMove(CNSocket *sock, sSkillResult_Move *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player *plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doMove: player ID not found" << std::endl; - return false; - } - - Player *plr2 = PlayerManager::getPlayer(sock); - - respdata[i].eCT = 1; - respdata[i].iID = plr->iID; - respdata[i].iMapNum = plr2->recallInstance; - respdata[i].iMoveX = plr2->recallX; - respdata[i].iMoveY = plr2->recallY; - respdata[i].iMoveZ = plr2->recallZ; - - return true; -} - -bool doDamageNDebuff(Mob* mob, sSkillResult_Damage_N_Debuff* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - CNSocket* sock = nullptr; - Player* plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - sock = pair.first; - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doDamageNDebuff: player ID not found" << std::endl; - return false; - } - - respdata[i].eCT = 1; - respdata[i].iDamage = duration / 10; - respdata[i].iID = plr->iID; - respdata[i].iHP = plr->HP; - respdata[i].iStamina = plr->Nanos[plr->activeNano].iStamina; - if (plr->iConditionBitFlag & CSB_BIT_FREEDOM) - respdata[i].bProtected = 1; - else { - if (!(plr->iConditionBitFlag & bitFlag)) { - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt); - pkt.eCSTB = timeBuffID; // eCharStatusTimeBuffID - pkt.eTBU = 1; // eTimeBuffUpdate - pkt.eTBT = 2; - pkt.iConditionBitFlag = plr->iConditionBitFlag |= bitFlag; - pkt.TimeBuff.iValue = amount * 5; - sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); - } - - respdata[i].bProtected = 0; - std::pair key = std::make_pair(sock, bitFlag); - time_t until = getTime() + (time_t)duration * 100; - Eggs::EggBuffs[key] = until; - } - respdata[i].iConditionBitFlag = plr->iConditionBitFlag; - - if (plr->HP <= 0) { - if (!MobAI::aggroCheck(mob, getTime())) - mob->transition(AIState::RETREAT, mob->target); - } - - return true; -} - -bool doHeal(Mob* mob, sSkillResult_Heal_HP* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - if (NPCManager::NPCs.find(targetID) == NPCManager::NPCs.end()) { - std::cout << "[WARN] doHeal: NPC ID not found" << std::endl; - return false; - } - - BaseNPC* npc = NPCManager::NPCs[targetID]; - if (npc->kind != EntityType::MOB) { - std::cout << "[WARN] doHeal: NPC is not a mob" << std::endl; - return false; - } - - Mob* targetMob = (Mob*)npc; - - int healedAmount = amount * targetMob->maxHealth / 1000; - targetMob->hp += healedAmount; - if (targetMob->hp > targetMob->maxHealth) - targetMob->hp = targetMob->maxHealth; - - respdata[i].eCT = 4; - respdata[i].iID = targetMob->id; - respdata[i].iHP = targetMob->hp; - respdata[i].iHealHP = healedAmount; - - return true; -} - -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->hp += healedAmount; - if (mob->hp > mob->maxHealth) - mob->hp = mob->maxHealth; - - respdata[i].eCT = 4; - respdata[i].iID = mob->id; - respdata[i].iHP = mob->hp; - respdata[i].iHealHP = healedAmount; - - return true; -} - -bool doDamage(Mob* mob, sSkillResult_Damage* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player* plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doDamage: player ID not found" << std::endl; - return false; - } - - int damage = amount * PC_MAXHEALTH((int)mob->data["m_iNpcLevel"]) / 1500; - - if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE) - damage = 0; - - respdata[i].eCT = 1; - respdata[i].iDamage = damage; - respdata[i].iID = plr->iID; - respdata[i].iHP = plr->HP -= damage; - - if (plr->HP <= 0) { - if (!MobAI::aggroCheck(mob, getTime())) - mob->transition(AIState::RETREAT, mob->target); - } - - return true; -} - -bool doLeech(Mob* mob, sSkillResult_Heal_HP* healdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - // this sanity check is VERY important - if (i != 0) { - std::cout << "[WARN] Mob attempted to leech more than one player!" << std::endl; - return false; - } - - Player* plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doLeech: player ID not found" << std::endl; - return false; - } - - sSkillResult_Damage* damagedata = (sSkillResult_Damage*)(((uint8_t*)healdata) + sizeof(sSkillResult_Heal_HP)); - - int healedAmount = amount * PC_MAXHEALTH(plr->level) / 1000; - - mob->hp += healedAmount; - if (mob->hp > mob->maxHealth) - mob->hp = mob->maxHealth; - - healdata->eCT = 4; - healdata->iID = mob->id; - healdata->iHP = mob->hp; - healdata->iHealHP = healedAmount; - - int damage = healedAmount; - - if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE) - damage = 0; - - damagedata->eCT = 1; - damagedata->iDamage = damage; - damagedata->iID = plr->iID; - damagedata->iHP = plr->HP -= damage; - - if (plr->HP <= 0) { - if (!MobAI::aggroCheck(mob, getTime())) - mob->transition(AIState::RETREAT, mob->target); - } - - return true; -} - -bool doBatteryDrain(Mob* mob, sSkillResult_BatteryDrain* respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) { - Player* plr = nullptr; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targetID) { - plr = pair.second; - break; - } - } - - // player not found - if (plr == nullptr) { - std::cout << "[WARN] doBatteryDrain: player ID not found" << std::endl; - return false; - } - - respdata[i].eCT = 1; - respdata[i].iID = plr->iID; - - if (plr->iConditionBitFlag & CSB_BIT_PROTECT_BATTERY) { - respdata[i].bProtected = 1; - respdata[i].iDrainW = 0; - respdata[i].iDrainN = 0; - } - else { - respdata[i].bProtected = 0; - respdata[i].iDrainW = amount * (18 + (int)mob->data["m_iNpcLevel"]) / 36; - respdata[i].iDrainN = amount * (18 + (int)mob->data["m_iNpcLevel"]) / 36; - } - - respdata[i].iBatteryW = plr->batteryW -= (respdata[i].iDrainW < plr->batteryW) ? respdata[i].iDrainW : plr->batteryW; - respdata[i].iBatteryN = plr->batteryN -= (respdata[i].iDrainN < plr->batteryN) ? respdata[i].iDrainN : plr->batteryN; - respdata[i].iStamina = plr->Nanos[plr->activeNano].iStamina; - respdata[i].iConditionBitFlag = plr->iConditionBitFlag; - - return true; -} - -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->id; - mob->cbf |= bitFlag; - respdata[i].iConditionBitFlag = mob->cbf; - - return true; -} - -template - void power(EntityRef ref, std::vector targetData, - int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount, - int16_t skillType, int32_t bitFlag, int16_t timeBuffID) { - - /*** OLD NANO HANDLER *** - Player *plr = PlayerManager::getPlayer(sock); - - if (skillType == EST_RETROROCKET_SELF || skillType == EST_RECALL) // rocket and self recall does not need any trailing structs - targetData[0] = 0; - - size_t resplen; - // special case since leech is atypically encoded - if (skillType == EST_BLOODSUCKING) - resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + sizeof(sSkillResult_Heal_HP) + sizeof(sSkillResult_Damage); - else - resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + targetData[0] * sizeof(sPAYLOAD); - - // validate response packet - if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), targetData[0], sizeof(sPAYLOAD))) { - std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size" << std::endl; - return; - } - - uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; - memset(respbuf, 0, resplen); - - sP_FE2CL_NANO_SKILL_USE_SUCC *resp = (sP_FE2CL_NANO_SKILL_USE_SUCC*)respbuf; - sPAYLOAD *respdata = (sPAYLOAD*)(respbuf+sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC)); - - resp->iPC_ID = plr->iID; - resp->iSkillID = skillID; - resp->iNanoID = nanoID; - resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina; - resp->eST = skillType; - resp->iTargetCnt = targetData[0]; - - if (SkillTable[skillID].drainType == 2) { - if (SkillTable[skillID].targetType >= 2) - plr->iSelfConditionBitFlag |= bitFlag; - if (SkillTable[skillID].targetType == 3) - plr->iGroupConditionBitFlag |= bitFlag; - } - - for (int i = 0; i < targetData[0]; i++) - if (!work(sock, respdata, i, targetData[i+1], bitFlag, timeBuffID, duration, amount)) - return; - - sock->sendPacket((void*)&respbuf, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); - assert(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) == sizeof(sP_FE2CL_NANO_SKILL_USE)); - if (skillType == EST_RECALL_GROUP) { // in the case of group recall, nobody but group members need the packet - for (int i = 0; i < targetData[0]; i++) { - CNSocket *sock2 = PlayerManager::getSockFromID(targetData[i+1]); - sock2->sendPacket((void*)&respbuf, P_FE2CL_NANO_SKILL_USE, resplen); - } - } else - PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NANO_SKILL_USE, resplen); - - // Warping on recall - if (skillType == EST_RECALL || skillType == EST_RECALL_GROUP) { - if ((int32_t)plr->instanceID == plr->recallInstance) - PlayerManager::sendPlayerTo(sock, plr->recallX, plr->recallY, plr->recallZ, plr->recallInstance); - else { - INITSTRUCT(sP_FE2CL_REP_WARP_USE_RECALL_FAIL, response) - sock->sendPacket((void*)&response, P_FE2CL_REP_WARP_USE_RECALL_FAIL, sizeof(sP_FE2CL_REP_WARP_USE_RECALL_FAIL)); - } - } - */ - - /*** OLD MOB ABILITY HANDLER *** - size_t resplen; - // special case since leech is atypically encoded - if (skillType == EST_BLOODSUCKING) - resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP) + sizeof(sSkillResult_Damage); - else - resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + targetData[0] * sizeof(sPAYLOAD); - - // validate response packet - if (!validOutVarPacket(sizeof(sP_FE2CL_NPC_SKILL_HIT), targetData[0], sizeof(sPAYLOAD))) { - std::cout << "[WARN] bad sP_FE2CL_NPC_SKILL_HIT packet size" << std::endl; - return; - } - - uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; - memset(respbuf, 0, resplen); - - sP_FE2CL_NPC_SKILL_HIT* resp = (sP_FE2CL_NPC_SKILL_HIT*)respbuf; - sPAYLOAD* respdata = (sPAYLOAD*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); - - resp->iNPC_ID = mob->id; - resp->iSkillID = skillID; - resp->iValue1 = mob->hitX; - resp->iValue2 = mob->hitY; - resp->iValue3 = mob->hitZ; - resp->eST = skillType; - resp->iTargetCnt = targetData[0]; - - for (int i = 0; i < targetData[0]; i++) - if (!work(mob, respdata, i, targetData[i + 1], bitFlag, timeBuffID, duration, amount)) - return; - - NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen); - */ -} - -// nano power dispatch table -std::vector Powers = {};/* - Power(EST_STUN, CSB_BIT_STUN, ECSB_STUN, power), - Power(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, ECSB_BOUNDINGBALL, power), - Power(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, power), - Power(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, power), - Power(EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, power), - Power(EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, power), - Power(EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, power), - Power(EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, power), - Power(EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, power), - Power(EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, power), - Power(EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, power), - Power(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, power), - Power(EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, power), - Power(EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, power), - Power(EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, power), - Power(EST_RECALL, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_RECALL_GROUP, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_RETROROCKET_SELF, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_PHOENIX_GROUP, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT1, ECSB_STIMPAKSLOT1, power), - Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT2, ECSB_STIMPAKSLOT2, power), - Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT3, ECSB_STIMPAKSLOT3, power), - // - Power(EST_STUN, CSB_BIT_STUN, ECSB_STUN, power), - Power(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_RETURNHOMEHEAL, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, power), - Power(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_BATTERYDRAIN, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, power), - Power(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, power), - Power(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, power) -};*/ - }; // namespace diff --git a/src/Abilities.hpp b/src/Abilities.hpp index 760ec56..022ee64 100644 --- a/src/Abilities.hpp +++ b/src/Abilities.hpp @@ -3,29 +3,6 @@ #include "core/Core.hpp" #include "Combat.hpp" -typedef void (*PowerHandler)(EntityRef, std::vector, int16_t, int16_t, int16_t, int16_t, int16_t, int32_t, int16_t); - -struct Power { - int16_t skillType; - int32_t bitFlag; - int16_t timeBuffID; - PowerHandler handler; - - Power(int16_t s, int32_t b, int16_t t, PowerHandler h) : skillType(s), bitFlag(b), timeBuffID(t), handler(h) {} - - void handle(EntityRef ref, std::vector targetData, int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount) { - if (handler == nullptr) - return; - - handler(ref, targetData, nanoID, skillID, duration, amount, skillType, bitFlag, timeBuffID); - } - - /* overload for non-nano abilities */ - void handle(EntityRef ref, std::vector targetData, int16_t skillID, int16_t duration, int16_t amount) { - handle(ref, targetData, -1, skillID, duration, amount); - } -}; - struct SkillData { int skillType; int targetType; @@ -37,11 +14,5 @@ struct SkillData { }; namespace Abilities { - extern std::vector Powers; extern std::map SkillTable; - - void removeBuff(CNSocket* sock, std::vector targetData, int32_t bitFlag, int16_t timeBuffID, int16_t amount, bool groupPower); - int applyBuff(CNSocket* sock, int skillID, int eTBU, int eTBT, int32_t groupFlags); - - std::vector findTargets(Player* plr, int skillID, CNPacketData* data = nullptr); } diff --git a/src/Chunking.cpp b/src/Chunking.cpp index d26fe8d..6e96c0a 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -54,7 +54,7 @@ void Chunking::trackEntity(ChunkPos chunkPos, const EntityRef& ref) { chunks[chunkPos]->entities.insert(ref); - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::PLAYER) chunks[chunkPos]->nplayers++; } @@ -66,7 +66,7 @@ void Chunking::untrackEntity(ChunkPos chunkPos, const EntityRef& ref) { chunk->entities.erase(ref); // gone - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::PLAYER) chunks[chunkPos]->nplayers--; assert(chunks[chunkPos]->nplayers >= 0); @@ -89,19 +89,19 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef& ref) { Entity *other = otherRef.getEntity(); // notify all visible players of the existence of this Entity - if (alive && otherRef.type == EntityType::PLAYER) { + if (alive && otherRef.kind == EntityKind::PLAYER) { ent->enterIntoViewOf(otherRef.sock); } // notify this *player* of the existence of all visible Entities - if (ref.type == EntityType::PLAYER && other->isExtant()) { + if (ref.kind == EntityKind::PLAYER && other->isExtant()) { other->enterIntoViewOf(ref.sock); } // for mobs, increment playersInView - if (ref.type == EntityType::MOB && otherRef.type == EntityType::PLAYER) + if (ref.kind == EntityKind::MOB && otherRef.kind == EntityKind::PLAYER) ((Mob*)ent)->playersInView++; - if (otherRef.type == EntityType::MOB && ref.type == EntityType::PLAYER) + if (otherRef.kind == EntityKind::MOB && ref.kind == EntityKind::PLAYER) ((Mob*)other)->playersInView++; } } @@ -121,19 +121,19 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef& r Entity *other = otherRef.getEntity(); // notify all visible players of the departure of this Entity - if (alive && otherRef.type == EntityType::PLAYER) { + if (alive && otherRef.kind == EntityKind::PLAYER) { ent->disappearFromViewOf(otherRef.sock); } // notify this *player* of the departure of all visible Entities - if (ref.type == EntityType::PLAYER && other->isExtant()) { + if (ref.kind == EntityKind::PLAYER && other->isExtant()) { other->disappearFromViewOf(ref.sock); } // for mobs, decrement playersInView - if (ref.type == EntityType::MOB && otherRef.type == EntityType::PLAYER) + if (ref.kind == EntityKind::MOB && otherRef.kind == EntityKind::PLAYER) ((Mob*)ent)->playersInView--; - if (otherRef.type == EntityType::MOB && ref.type == EntityType::PLAYER) + if (otherRef.kind == EntityKind::MOB && ref.kind == EntityKind::PLAYER) ((Mob*)other)->playersInView--; } } @@ -155,7 +155,7 @@ static void emptyChunk(ChunkPos chunkPos) { // unspawn all of the mobs/npcs std::set refs(chunk->entities); for (const EntityRef& ref : refs) { - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::PLAYER) assert(0); // every call of this will check if the chunk is empty and delete it if so @@ -268,14 +268,14 @@ void Chunking::createInstance(uint64_t instanceID) { std::cout << "Creating instance " << instanceID << std::endl; for (ChunkPos &coords : templateChunks) { for (const EntityRef& ref : chunks[coords]->entities) { - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::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->kind == EntityType::MOB) { + if (baseNPC->kind == EntityKind::MOB) { if (((Mob*)baseNPC)->groupLeader != 0 && ((Mob*)baseNPC)->groupLeader != npcID) continue; // follower; don't copy individually diff --git a/src/Combat.cpp b/src/Combat.cpp index 1b83cdf..0082363 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -44,41 +44,8 @@ void Player::step(time_t currTime) { int CombatNPC::takeDamage(EntityRef src, int amt) { - /* REFACTOR: all of this logic is strongly coupled to mobs. - * come back to this when more of it is moved to CombatNPC. - * remove this cast when done */ - Mob* mob = (Mob*)this; - - // cannot kill mobs multiple times; cannot harm retreating mobs - if (mob->state != AIState::ROAMING && mob->state != AIState::COMBAT) { - return 0; // no damage - } - - if (mob->skillStyle >= 0) - return 0; // don't hurt a mob casting corruption - - if (mob->state == AIState::ROAMING) { - assert(mob->target == nullptr && src.type == EntityType::PLAYER); // players only for now - mob->transition(AIState::COMBAT, src); - - if (mob->groupLeader != 0) - MobAI::followToCombat(mob); - } - hp -= amt; - - // wake up sleeping monster - 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->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->hp <= 0) + if (hp <= 0) transition(AIState::DEAD, src); return amt; @@ -101,62 +68,29 @@ int32_t CombatNPC::getID() { } void CombatNPC::step(time_t currTime) { - if (playersInView < 0) - std::cout << "[WARN] Weird playerview value " << playersInView << std::endl; - - // skip movement and combat if disabled or not in view - if ((!MobAI::simulateMobs || playersInView == 0) && state != AIState::DEAD - && state != AIState::RETREAT) - return; - - switch (state) { - case AIState::INACTIVE: - // no-op - break; - case AIState::ROAMING: - roamingStep(currTime); - break; - case AIState::COMBAT: - combatStep(currTime); - break; - case AIState::RETREAT: - retreatStep(currTime); - break; - case AIState::DEAD: - deadStep(currTime); - break; + + if(stateHandlers.find(state) != stateHandlers.end()) + stateHandlers[state](this, currTime); + else { + std::cout << "[WARN] State " << (int)state << " has no handler; going inactive" << std::endl; + transition(AIState::INACTIVE, id); } } void CombatNPC::transition(AIState newState, EntityRef src) { + state = newState; - switch (newState) { - case AIState::INACTIVE: - onInactive(); - break; - case AIState::ROAMING: - onRoamStart(); - break; - case AIState::COMBAT: - /* TODO: fire any triggered events - for (NPCEvent& event : NPCManager::NPCEvents) - if (event.trigger == ON_COMBAT && event.npcType == type) - event.handler(src, this); - */ - onCombatStart(src); - break; - case AIState::RETREAT: - onRetreat(); - break; - case AIState::DEAD: - /* TODO: fire any triggered events - for (NPCEvent& event : NPCManager::NPCEvents) - if (event.trigger == ON_KILLED && event.npcType == type) - event.handler(src, this); - */ - onDeath(src); - break; + if (transitionHandlers.find(newState) != transitionHandlers.end()) + transitionHandlers[newState](this, src); + else { + std::cout << "[WARN] Transition to " << (int)state << " has no handler; going inactive" << std::endl; + transition(AIState::INACTIVE, id); } + /* TODO: fire any triggered events + for (NPCEvent& event : NPCManager::NPCEvents) + if (event.trigger == ON_KILLED && event.npcType == type) + event.handler(src, this); + */ } static std::pair getDamage(int attackPower, int defensePower, bool shouldCrit, @@ -256,7 +190,7 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) { BaseNPC* npc = NPCManager::NPCs[targets[i]]; - if (npc->kind != EntityType::MOB) { + if (npc->kind != EntityKind::MOB) { std::cout << "[WARN] pcAttackNpcs: NPC is not a mob" << std::endl; return; } @@ -450,7 +384,7 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { return; } - int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_PC_ATTACK_CHARs)); + sGM_PVPTarget* pktdata = (sGM_PVPTarget*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_PC_ATTACK_CHARs)); if (!validOutVarPacket(sizeof(sP_FE2CL_PC_ATTACK_CHARs_SUCC), pkt->iTargetCnt, sizeof(sAttackResult))) { std::cout << "[WARN] bad sP_FE2CL_PC_ATTACK_CHARs_SUCC packet size\n"; @@ -471,7 +405,6 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { for (int i = 0; i < pkt->iTargetCnt; i++) { ICombatant* target = nullptr; - sGM_PVPTarget* targdata = (sGM_PVPTarget*)(pktdata + i * 2); std::pair damage; if (pkt->iTargetCnt > 1) @@ -479,10 +412,10 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { else damage.first = plr->pointDamage; - if (targdata->eCT == 1) { // eCT == 1; attack player + if (pktdata[i].eCT == 1) { // eCT == 1; attack player for (auto& pair : PlayerManager::players) { - if (pair.second->iID == targdata->iID) { + if (pair.second->iID == pktdata[i].iID) { target = pair.second; break; } @@ -498,14 +431,14 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { } else { // eCT == 4; attack mob - if (NPCManager::NPCs.find(targdata->iID) == NPCManager::NPCs.end()) { + if (NPCManager::NPCs.find(pktdata[i].iID) == NPCManager::NPCs.end()) { // not sure how to best handle this std::cout << "[WARN] pcAttackChars: NPC ID not found" << std::endl; return; } - BaseNPC* npc = NPCManager::NPCs[targdata->iID]; - if (npc->kind != EntityType::MOB) { + BaseNPC* npc = NPCManager::NPCs[pktdata[i].iID]; + if (npc->kind != EntityKind::MOB) { std::cout << "[WARN] pcAttackChars: NPC is not a mob" << std::endl; return; } @@ -524,7 +457,7 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { damage.first = target->takeDamage(sock, damage.first); - respdata[i].eCT = targdata->eCT; + respdata[i].eCT = pktdata[i].eCT; respdata[i].iID = target->getID(); respdata[i].iDamage = damage.first; respdata[i].iHP = target->getCurrentHP(); @@ -707,7 +640,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) { } BaseNPC* npc = NPCManager::NPCs[pktdata[i]]; - if (npc->kind != EntityType::MOB) { + if (npc->kind != EntityKind::MOB) { std::cout << "[WARN] projectileHit: NPC is not a mob" << std::endl; return; } diff --git a/src/CustomCommands.cpp b/src/CustomCommands.cpp index 4c2bb9e..36e0108 100644 --- a/src/CustomCommands.cpp +++ b/src/CustomCommands.cpp @@ -283,10 +283,10 @@ static void unsummonWCommand(std::string full, std::vector& args, C return; } - if (NPCManager::NPCs.find(npc->id) != NPCManager::NPCs.end() && NPCManager::NPCs[npc->id]->kind == EntityType::MOB) { + if (NPCManager::NPCs.find(npc->id) != NPCManager::NPCs.end() && NPCManager::NPCs[npc->id]->kind == EntityKind::MOB) { int leadId = ((Mob*)npc)->groupLeader; if (leadId != 0) { - if (NPCManager::NPCs.find(leadId) == NPCManager::NPCs.end() || NPCManager::NPCs[leadId]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(leadId) == NPCManager::NPCs.end() || NPCManager::NPCs[leadId]->kind != EntityKind::MOB) { std::cout << "[WARN] unsummonW: leader not found!" << std::endl; } Mob* leadNpc = (Mob*)NPCManager::NPCs[leadId]; @@ -294,7 +294,7 @@ static void unsummonWCommand(std::string full, std::vector& args, C if (leadNpc->groupMember[i] == 0) break; - if (NPCManager::NPCs.find(leadNpc->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadNpc->groupMember[i]]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(leadNpc->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadNpc->groupMember[i]]->kind != EntityKind::MOB) { std::cout << "[WARN] unsommonW: leader can't find a group member!" << std::endl; continue; } @@ -324,7 +324,7 @@ static void toggleAiCommand(std::string full, std::vector& args, CN // return all mobs to their spawn points for (auto& pair : NPCManager::NPCs) { - if (pair.second->kind != EntityType::MOB) + if (pair.second->kind != EntityKind::MOB) continue; Mob* mob = (Mob*)pair.second; @@ -609,7 +609,7 @@ 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) { + if (team == 2 && i > 0 && npc->kind == EntityKind::MOB) { leadNpc->groupMember[i-1] = npc->id; Mob* mob = (Mob*)NPCManager::NPCs[npc->id]; mob->groupLeader = leadNpc->id; @@ -624,7 +624,7 @@ static void summonGroupCommand(std::string full, std::vector& args, 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) { + if (team == 2 && i > 0 && npc->kind == EntityKind::MOB) { leadNpc->groupMember[i-1] = npc->id; Mob* mob = (Mob*)NPCManager::NPCs[npc->id]; mob->groupLeader = leadNpc->id; @@ -639,7 +639,7 @@ static void summonGroupCommand(std::string full, std::vector& args, Chat::sendServerMessage(sock, "/summonGroup(W): placed mob with type: " + std::to_string(type) + ", id: " + std::to_string(npc->id)); - if (i == 0 && team == 2 && npc->kind == EntityType::MOB) { + if (i == 0 && team == 2 && npc->kind == EntityKind::MOB) { type = type2; leadNpc = (Mob*)NPCManager::NPCs[npc->id]; leadNpc->groupLeader = leadNpc->id; @@ -694,7 +694,7 @@ static void lairUnlockCommand(std::string full, std::vector& args, int lastDist = INT_MAX; for (Chunk *chnk : Chunking::getViewableChunks(plr->chunkPos)) { for (const EntityRef& ref : chnk->entities) { - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::PLAYER) continue; BaseNPC* npc = (BaseNPC*)ref.getEntity(); diff --git a/src/Eggs.cpp b/src/Eggs.cpp index f29d5cc..a3b0fa2 100644 --- a/src/Eggs.cpp +++ b/src/Eggs.cpp @@ -19,7 +19,8 @@ int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) { Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); int bitFlag = Groups::getGroupFlags(otherPlr); - int CBFlag = Abilities::applyBuff(sock, skillId, 1, 3, bitFlag); + // TODO ABILITIES + int CBFlag = 0;// Abilities::applyBuff(sock, skillId, 1, 3, bitFlag); size_t resplen; @@ -100,23 +101,24 @@ static void eggStep(CNServer* serv, time_t currTime) { Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); int groupFlags = Groups::getGroupFlags(otherPlr); - for (auto& pwr : Abilities::Powers) { - if (pwr.bitFlag == CBFlag) { // pick the power with the right flag and unbuff - INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); - resp.eCSTB = pwr.timeBuffID; - resp.eTBU = 2; - resp.eTBT = 3; // for egg buffs - plr->iConditionBitFlag &= ~CBFlag; - resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; - sock->sendPacket(resp, P_FE2CL_PC_BUFF_UPDATE); + // TODO ABILITIES + //for (auto& pwr : Abilities::Powers) { + // if (pwr.bitFlag == CBFlag) { // pick the power with the right flag and unbuff + // INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp); + // resp.eCSTB = pwr.timeBuffID; + // resp.eTBU = 2; + // resp.eTBT = 3; // for egg buffs + // plr->iConditionBitFlag &= ~CBFlag; + // resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; + // sock->sendPacket(resp, P_FE2CL_PC_BUFF_UPDATE); - INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, resp2); // send a buff timeout to other players - resp2.eCT = 1; - resp2.iID = plr->iID; - resp2.iConditionBitFlag = plr->iConditionBitFlag; - PlayerManager::sendToViewable(sock, resp2, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT); - } - } + // INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, resp2); // send a buff timeout to other players + // resp2.eCT = 1; + // resp2.iID = plr->iID; + // resp2.iConditionBitFlag = plr->iConditionBitFlag; + // PlayerManager::sendToViewable(sock, resp2, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT); + // } + //} // remove buff from the map it = EggBuffs.erase(it); } @@ -124,7 +126,7 @@ static void eggStep(CNServer* serv, time_t currTime) { // check dead eggs and eggs in inactive chunks for (auto npc : NPCManager::NPCs) { - if (npc.second->kind != EntityType::EGG) + if (npc.second->kind != EntityKind::EGG) continue; auto egg = (Egg*)npc.second; @@ -163,7 +165,7 @@ static void eggPickup(CNSocket* sock, CNPacketData* data) { return; } auto egg = (Egg*)eggRef.getEntity(); - if (egg->kind != EntityType::EGG) { + if (egg->kind != EntityKind::EGG) { std::cout << "[WARN] Player tried to open something other than an?!" << std::endl; return; } diff --git a/src/Entities.cpp b/src/Entities.cpp index ba6d136..ff63053 100644 --- a/src/Entities.cpp +++ b/src/Entities.cpp @@ -12,7 +12,7 @@ static_assert(std::is_standard_layout::value); static_assert(std::is_trivially_copyable::value); EntityRef::EntityRef(CNSocket *s) { - type = EntityType::PLAYER; + kind = EntityKind::PLAYER; sock = s; } @@ -20,11 +20,11 @@ EntityRef::EntityRef(int32_t i) { id = i; assert(NPCManager::NPCs.find(id) != NPCManager::NPCs.end()); - type = NPCManager::NPCs[id]->kind; + kind = NPCManager::NPCs[id]->kind; } bool EntityRef::isValid() const { - if (type == EntityType::PLAYER) + if (kind == EntityKind::PLAYER) return PlayerManager::players.find(sock) != PlayerManager::players.end(); return NPCManager::NPCs.find(id) != NPCManager::NPCs.end(); @@ -33,7 +33,7 @@ bool EntityRef::isValid() const { Entity *EntityRef::getEntity() const { assert(isValid()); - if (type == EntityType::PLAYER) + if (kind == EntityKind::PLAYER) return PlayerManager::getPlayer(sock); return NPCManager::NPCs[id]; diff --git a/src/Entities.hpp b/src/Entities.hpp index d1e46e5..41d9dfd 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -6,7 +6,7 @@ #include #include -enum class EntityType : uint8_t { +enum EntityKind { INVALID, PLAYER, SIMPLE_NPC, @@ -27,7 +27,7 @@ enum class AIState { class Chunk; struct Entity { - EntityType kind = EntityType::INVALID; + EntityKind kind = EntityKind::INVALID; int x = 0, y = 0, z = 0; uint64_t instanceID = 0; ChunkPos chunkPos = {}; @@ -44,7 +44,7 @@ struct Entity { }; struct EntityRef { - EntityType type; + EntityKind kind; union { CNSocket *sock; int32_t id; @@ -57,10 +57,10 @@ struct EntityRef { Entity *getEntity() const; bool operator==(const EntityRef& other) const { - if (type != other.type) + if (kind != other.kind) return false; - if (type == EntityType::PLAYER) + if (kind == EntityKind::PLAYER) return sock == other.sock; return id == other.id; @@ -68,21 +68,20 @@ struct EntityRef { // arbitrary ordering bool operator<(const EntityRef& other) const { - if (type == other.type) { - if (type == EntityType::PLAYER) + if (kind == other.kind) { + if (kind == EntityKind::PLAYER) return sock < other.sock; else return id < other.id; } - return type < other.type; + return kind < other.kind; } }; /* * Interfaces */ - class ICombatant { public: ICombatant() {} @@ -93,7 +92,6 @@ public: virtual bool isAlive() = 0; virtual int getCurrentHP() = 0; virtual int32_t getID() = 0; - virtual void step(time_t currTime) = 0; }; @@ -134,11 +132,17 @@ struct CombatNPC : public BaseNPC, public ICombatant { AIState state = AIState::INACTIVE; int playersInView = 0; // for optimizing away AI in empty chunks + std::map stateHandlers; + std::map transitionHandlers; + CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP) : BaseNPC(angle, iID, t, id), maxHealth(maxHP) { spawnX = x; spawnY = y; spawnZ = z; + + stateHandlers[AIState::INACTIVE] = {}; + transitionHandlers[AIState::INACTIVE] = {}; } virtual bool isExtant() override { return hp > 0; } @@ -148,19 +152,9 @@ struct CombatNPC : public BaseNPC, public ICombatant { virtual bool isAlive() override; virtual int getCurrentHP() override; virtual int32_t getID() override; - virtual void step(time_t currTime) override; - virtual void roamingStep(time_t currTime) {} // no-ops by default - virtual void combatStep(time_t currTime) {} - virtual void retreatStep(time_t currTime) {} - virtual void deadStep(time_t currTime) {} virtual void transition(AIState newState, EntityRef src); - virtual void onInactive() {} // no-ops by default - virtual void onRoamStart() {} - virtual void onCombatStart(EntityRef src) {} - virtual void onRetreat() {} - virtual void onDeath(EntityRef src) {} }; // Mob is in MobAI.hpp, Player is in Player.hpp @@ -173,7 +167,7 @@ struct Egg : public BaseNPC { Egg(uint64_t iID, int t, int32_t id, bool summon) : BaseNPC(0, iID, t, id) { summoned = summon; - kind = EntityType::EGG; + kind = EntityKind::EGG; } virtual bool isExtant() override { return !dead; } @@ -185,7 +179,7 @@ struct Egg : public BaseNPC { struct Bus : public BaseNPC { Bus(int angle, uint64_t iID, int t, int id) : BaseNPC(angle, iID, t, id) { - kind = EntityType::BUS; + kind = EntityKind::BUS; loopingPath = true; } diff --git a/src/Groups.cpp b/src/Groups.cpp index 59dfdf7..28d76e4 100644 --- a/src/Groups.cpp +++ b/src/Groups.cpp @@ -135,10 +135,11 @@ static void joinGroup(CNSocket* sock, CNPacketData* data) { // client doesnt read nano data here if (varPlr != plr) { // apply the new member's buffs to the group and the group's buffs to the new member - if (Abilities::SkillTable[varPlr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) + // TODO ABILITIES + /*if (Abilities::SkillTable[varPlr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) Abilities::applyBuff(sock, varPlr->Nanos[varPlr->activeNano].iSkillID, 1, 1, bitFlag); if (Abilities::SkillTable[plr->Nanos[plr->activeNano].iSkillID].targetType == 3) - Abilities::applyBuff(sockTo, plr->Nanos[plr->activeNano].iSkillID, 1, 1, bitFlag); + Abilities::applyBuff(sockTo, plr->Nanos[plr->activeNano].iSkillID, 1, 1, bitFlag);*/ } } @@ -221,7 +222,8 @@ static void groupUnbuff(Player* plr) { Player* otherPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); CNSocket* sock = PlayerManager::getSockFromID(plr->groupIDs[n]); - Abilities::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0); + // TODO ABILITIES + //Abilities::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0); } } } @@ -295,10 +297,11 @@ void Groups::groupKickPlayer(Player* plr) { moveDown = 1; otherPlr->groupIDs[i] = 0; } else { // remove the leaving member's buffs from the group and remove the group buffs from the leaving member. - if (Abilities::SkillTable[varPlr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) + // TODO ABILITIES + /*if (Abilities::SkillTable[varPlr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) Abilities::applyBuff(sock, varPlr->Nanos[varPlr->activeNano].iSkillID, 2, 1, 0); if (Abilities::SkillTable[plr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) - Abilities::applyBuff(sockTo, plr->Nanos[plr->activeNano].iSkillID, 2, 1, bitFlag); + Abilities::applyBuff(sockTo, plr->Nanos[plr->activeNano].iSkillID, 2, 1, bitFlag);*/ } } diff --git a/src/Missions.cpp b/src/Missions.cpp index 5a33354..890bec5 100644 --- a/src/Missions.cpp +++ b/src/Missions.cpp @@ -371,7 +371,7 @@ static void taskStart(CNSocket* sock, CNPacketData* data) { for (ChunkPos& chunkPos : Chunking::getChunksInMap(plr->instanceID)) { // check all NPCs in the instance Chunk* chunk = Chunking::chunks[chunkPos]; for (EntityRef ref : chunk->entities) { - if (ref.type != EntityType::PLAYER) { + if (ref.kind != EntityKind::PLAYER) { BaseNPC* npc = (BaseNPC*)ref.getEntity(); NPCPath* path = Transport::findApplicablePath(npc->id, npc->type, missionData->iTaskNum); if (path != nullptr) { diff --git a/src/MobAI.cpp b/src/MobAI.cpp index cc73de5..9a930ec 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -16,6 +16,52 @@ using namespace MobAI; bool MobAI::simulateMobs = settings::SIMULATEMOBS; +void Mob::step(time_t currTime) { + if (playersInView < 0) + std::cout << "[WARN] Weird playerview value " << playersInView << std::endl; + + // skip movement and combat if disabled or not in view + if ((!MobAI::simulateMobs || playersInView == 0) && state != AIState::DEAD + && state != AIState::RETREAT) + return; + + // call superclass step + CombatNPC::step(currTime); +} + +int Mob::takeDamage(EntityRef src, int amt) { + + // cannot kill mobs multiple times; cannot harm retreating mobs + if (state != AIState::ROAMING && state != AIState::COMBAT) { + return 0; // no damage + } + + if (skillStyle >= 0) + return 0; // don't hurt a mob casting corruption + + if (state == AIState::ROAMING) { + assert(target == nullptr && src.kind == EntityKind::PLAYER); // TODO: players only for now + transition(AIState::COMBAT, src); + + if (groupLeader != 0) + MobAI::followToCombat(this); + } + + // wake up sleeping monster + if (cbf & CSB_BIT_MEZ) { + cbf &= ~CSB_BIT_MEZ; + + INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); + pkt1.eCT = 2; + pkt1.iID = id; + pkt1.iConditionBitFlag = cbf; + NPCManager::sendToViewable(this, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); + } + + // call superclass takeDamage + return CombatNPC::takeDamage(src, amt); +} + /* * Dynamic lerp; distinct from Transport::lerp(). This one doesn't care about height and * only returns the first step, since the rest will need to be recalculated anyway if chasing player. @@ -58,13 +104,13 @@ void MobAI::clearDebuff(Mob *mob) { } void MobAI::followToCombat(Mob *mob) { - if (NPCManager::NPCs.find(mob->groupLeader) != NPCManager::NPCs.end() && NPCManager::NPCs[mob->groupLeader]->kind == EntityType::MOB) { + if (NPCManager::NPCs.find(mob->groupLeader) != NPCManager::NPCs.end() && NPCManager::NPCs[mob->groupLeader]->kind == EntityKind::MOB) { Mob* leadMob = (Mob*)NPCManager::NPCs[mob->groupLeader]; for (int i = 0; i < 4; i++) { if (leadMob->groupMember[i] == 0) break; - if (NPCManager::NPCs.find(leadMob->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadMob->groupMember[i]]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(leadMob->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadMob->groupMember[i]]->kind != EntityKind::MOB) { std::cout << "[WARN] roamingStep: leader can't find a group member!" << std::endl; continue; } @@ -84,7 +130,7 @@ void MobAI::followToCombat(Mob *mob) { } void MobAI::groupRetreat(Mob *mob) { - if (NPCManager::NPCs.find(mob->groupLeader) == NPCManager::NPCs.end() || NPCManager::NPCs[mob->groupLeader]->kind != EntityType::MOB) + if (NPCManager::NPCs.find(mob->groupLeader) == NPCManager::NPCs.end() || NPCManager::NPCs[mob->groupLeader]->kind != EntityKind::MOB) return; Mob* leadMob = (Mob*)NPCManager::NPCs[mob->groupLeader]; @@ -92,7 +138,7 @@ void MobAI::groupRetreat(Mob *mob) { if (leadMob->groupMember[i] == 0) break; - if (NPCManager::NPCs.find(leadMob->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadMob->groupMember[i]]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(leadMob->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[leadMob->groupMember[i]]->kind != EntityKind::MOB) { std::cout << "[WARN] roamingStep: leader can't find a group member!" << std::endl; continue; } @@ -127,7 +173,7 @@ bool MobAI::aggroCheck(Mob *mob, time_t currTime) { Chunk* chunk = *it; for (const EntityRef& ref : chunk->entities) { // TODO: support targetting other CombatNPCs - if (ref.type != EntityType::PLAYER) + if (ref.kind != EntityKind::PLAYER) continue; CNSocket *s = ref.sock; @@ -247,10 +293,11 @@ 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->id, 0, 0, 0}; + // TODO ABILITIES + /*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); + pwr.handle(sock, targetData2, plr->activeNano, skillID, 0, 200);*/ } else { respdata[i].iHitFlag = HF_BIT_STYLE_LOSE; respdata[i].iDamage = Abilities::SkillTable[skillID].powerIntensity[0] * PC_MAXHEALTH((int)mob->data["m_iNpcLevel"]) / 1500; @@ -304,7 +351,7 @@ static void useAbilities(Mob *mob, time_t currTime) { Chunk* chunk = *it; for (const EntityRef& ref : chunk->entities) { // TODO: see aggroCheck() - if (ref.type != EntityType::PLAYER) + if (ref.kind != EntityKind::PLAYER) continue; CNSocket *s= ref.sock; @@ -323,9 +370,10 @@ static void useAbilities(Mob *mob, time_t currTime) { } } - for (auto& pwr : Abilities::Powers) + // TODO ABILITIES + /*for (auto& pwr : Abilities::Powers) if (pwr.skillType == Abilities::SkillTable[skillID].skillType) - pwr.handle(mob->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]); + pwr.handle(mob->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]);*/ mob->skillStyle = -3; // eruption cooldown mob->nextAttack = currTime + 1000; return; @@ -343,13 +391,14 @@ static void useAbilities(Mob *mob, time_t currTime) { if (random < prob1) { // active skill hit int skillID = (int)mob->data["m_iActiveSkill1"]; - std::vector targetData = {1, plr->iID, 0, 0, 0}; - for (auto& pwr : Abilities::Powers) - if (pwr.skillType == Abilities::SkillTable[skillID].skillType) { - if (pwr.bitFlag != 0 && (plr->iConditionBitFlag & pwr.bitFlag)) - return; // prevent debuffing a player twice - pwr.handle(mob->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]); - } + // TODO ABILITIES + //std::vector targetData = {1, plr->iID, 0, 0, 0}; + //for (auto& pwr : Abilities::Powers) + // if (pwr.skillType == Abilities::SkillTable[skillID].skillType) { + // if (pwr.bitFlag != 0 && (plr->iConditionBitFlag & pwr.bitFlag)) + // return; // prevent debuffing a player twice + // pwr.handle(mob->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]); + // } mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100; return; } @@ -415,49 +464,58 @@ static void drainMobHP(Mob *mob, int amount) { mob->transition(AIState::DEAD, mob->target); } -void Mob::deadStep(time_t currTime) { +void MobAI::incNextMovement(Mob* mob, time_t currTime) { + if (currTime == 0) + currTime = getTime(); + + int delay = (int)mob->data["m_iDelayTime"] * 1000; + mob->nextMovement = currTime + delay / 2 + Rand::rand(delay / 2); +} + +void MobAI::deadStep(CombatNPC* npc, time_t currTime) { + Mob* self = (Mob*)npc; + // despawn the mob after a short delay - if (killedTime != 0 && !despawned && currTime - killedTime > 2000) { - despawned = true; + if (self->killedTime != 0 && !self->despawned && currTime - self->killedTime > 2000) { + self->despawned = true; INITSTRUCT(sP_FE2CL_NPC_EXIT, pkt); - pkt.iNPC_ID = id; + pkt.iNPC_ID = self->id; - NPCManager::sendToViewable(this, &pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); + NPCManager::sendToViewable(self, &pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); // if it was summoned, mark it for removal - if (summoned) { + if (self->summoned) { std::cout << "[INFO] Queueing killed summoned mob for removal" << std::endl; - NPCManager::queueNPCRemoval(id); + NPCManager::queueNPCRemoval(self->id); return; } // pre-set spawn coordinates if not marked for removal - x = spawnX; - y = spawnY; - z = spawnZ; + self->x = self->spawnX; + self->y = self->spawnY; + self->z = self->spawnZ; } // to guide their groupmates, group leaders still need to move despite being dead - if (groupLeader == id) - roamingStep(currTime); + if (self->groupLeader == self->id) + roamingStep(self, currTime); - if (killedTime != 0 && currTime - killedTime < regenTime * 100) + if (self->killedTime != 0 && currTime - self->killedTime < self->regenTime * 100) return; - std::cout << "respawning mob " << id << " with HP = " << maxHealth << std::endl; + std::cout << "respawning mob " << self->id << " with HP = " << self->maxHealth << std::endl; - hp = maxHealth; - state = AIState::ROAMING; + self->transition(AIState::ROAMING, self->id); // if mob is a group leader/follower, spawn where the group is. - if (groupLeader != 0) { - if (NPCManager::NPCs.find(groupLeader) != NPCManager::NPCs.end() && NPCManager::NPCs[groupLeader]->kind == EntityType::MOB) { - Mob* leaderMob = (Mob*)NPCManager::NPCs[groupLeader]; - x = leaderMob->x + offsetX; - y = leaderMob->y + offsetY; - z = leaderMob->z; + if (self->groupLeader != 0) { + if (NPCManager::NPCs.find(self->groupLeader) != NPCManager::NPCs.end() && NPCManager::NPCs[self->groupLeader]->kind == EntityKind::MOB) { + Mob* leaderMob = (Mob*)NPCManager::NPCs[self->groupLeader]; + self->x = leaderMob->x + self->offsetX; + self->y = leaderMob->y + self->offsetY; + self->z = leaderMob->z; } else { std::cout << "[WARN] deadStep: mob cannot find it's leader!" << std::endl; } @@ -465,162 +523,157 @@ void Mob::deadStep(time_t currTime) { INITSTRUCT(sP_FE2CL_NPC_NEW, pkt); - pkt.NPCAppearanceData = getAppearanceData(); + pkt.NPCAppearanceData = self->getAppearanceData(); // notify all nearby players - NPCManager::sendToViewable(this, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW)); + NPCManager::sendToViewable(self, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW)); } -void Mob::combatStep(time_t currTime) { - assert(target != nullptr); +void MobAI::combatStep(CombatNPC* npc, time_t currTime) { + Mob* self = (Mob*)npc; + assert(self->target != nullptr); // lose aggro if the player lost connection - if (PlayerManager::players.find(target) == PlayerManager::players.end()) { - if (!MobAI::aggroCheck(this, getTime())) - transition(AIState::RETREAT, target); + if (PlayerManager::players.find(self->target) == PlayerManager::players.end()) { + if (!MobAI::aggroCheck(self, getTime())) + self->transition(AIState::RETREAT, self->target); return; } - Player *plr = PlayerManager::getPlayer(target); + Player *plr = PlayerManager::getPlayer(self->target); // lose aggro if the player became invulnerable or died if (plr->HP <= 0 || (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)) { - if (!MobAI::aggroCheck(this, getTime())) - transition(AIState::RETREAT, target); + if (!MobAI::aggroCheck(self, getTime())) + self->transition(AIState::RETREAT, self->target); return; } // drain - if (skillStyle < 0 && (lastDrainTime == 0 || currTime - lastDrainTime >= 1000) - && cbf & CSB_BIT_BOUNDINGBALL) { - drainMobHP(this, maxHealth / 20); // lose 5% every second - lastDrainTime = currTime; + if (self->skillStyle < 0 && (self->lastDrainTime == 0 || currTime - self->lastDrainTime >= 1000) + && self->cbf & CSB_BIT_BOUNDINGBALL) { + drainMobHP(self, self->maxHealth / 20); // lose 5% every second + self->lastDrainTime = currTime; } // if drain killed the mob, return early - if (hp <= 0) + if (self->hp <= 0) return; // unbuffing - std::unordered_map::iterator it = unbuffTimes.begin(); - while (it != unbuffTimes.end()) { + std::unordered_map::iterator it = self->unbuffTimes.begin(); + while (it != self->unbuffTimes.end()) { if (currTime >= it->second) { - cbf &= ~it->first; + self->cbf &= ~it->first; INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); pkt1.eCT = 2; - pkt1.iID = id; - pkt1.iConditionBitFlag = cbf; - NPCManager::sendToViewable(this, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); + pkt1.iID = self->id; + pkt1.iConditionBitFlag = self->cbf; + NPCManager::sendToViewable(self, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); - it = unbuffTimes.erase(it); + it = self->unbuffTimes.erase(it); } else { it++; } } // skip attack if stunned or asleep - if (cbf & (CSB_BIT_STUN|CSB_BIT_MEZ)) { - skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up. + if (self->cbf & (CSB_BIT_STUN|CSB_BIT_MEZ)) { + self->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up. return; } - int distance = hypot(plr->x - x, plr->y - y); - int mobRange = (int)data["m_iAtkRange"] + (int)data["m_iRadius"]; + int distance = hypot(plr->x - self->x, plr->y - self->y); + int mobRange = (int)self->data["m_iAtkRange"] + (int)self->data["m_iRadius"]; - if (currTime >= nextAttack) { - if (skillStyle != -1 || distance <= mobRange || Rand::rand(20) == 0) // while not in attack range, 1 / 20 chance. - useAbilities(this, currTime); - if (target == nullptr) + if (currTime >= self->nextAttack) { + if (self->skillStyle != -1 || distance <= mobRange || Rand::rand(20) == 0) // while not in attack range, 1 / 20 chance. + useAbilities(self, currTime); + if (self->target == nullptr) return; } int distanceToTravel = INT_MAX; // movement logic: move when out of range but don't move while casting a skill - if (distance > mobRange && skillStyle == -1) { - if (nextMovement != 0 && currTime < nextMovement) + if (distance > mobRange && self->skillStyle == -1) { + if (self->nextMovement != 0 && currTime < self->nextMovement) return; - nextMovement = currTime + 400; - if (currTime >= nextAttack) - nextAttack = 0; + self->nextMovement = currTime + 400; + if (currTime >= self->nextAttack) + self->nextAttack = 0; // halve movement speed if snared - if (cbf & CSB_BIT_DN_MOVE_SPEED) - speed /= 2; + if (self->cbf & CSB_BIT_DN_MOVE_SPEED) + self->speed /= 2; int targetX = plr->x; int targetY = plr->y; - if (groupLeader != 0) { - targetX += offsetX*distance/(idleRange + 1); - targetY += offsetY*distance/(idleRange + 1); + if (self->groupLeader != 0) { + targetX += self->offsetX*distance/(self->idleRange + 1); + targetY += self->offsetY*distance/(self->idleRange + 1); } - distanceToTravel = std::min(distance-mobRange+1, speed*2/5); - auto targ = lerp(x, y, targetX, targetY, distanceToTravel); - if (distanceToTravel < speed*2/5 && currTime >= nextAttack) - nextAttack = 0; + distanceToTravel = std::min(distance-mobRange+1, self->speed*2/5); + auto targ = lerp(self->x, self->y, targetX, targetY, distanceToTravel); + if (distanceToTravel < self->speed*2/5 && currTime >= self->nextAttack) + self->nextAttack = 0; - NPCManager::updateNPCPosition(id, targ.first, targ.second, z, instanceID, angle); + NPCManager::updateNPCPosition(self->id, targ.first, targ.second, self->z, self->instanceID, self->angle); INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); - pkt.iNPC_ID = id; - pkt.iSpeed = speed; - pkt.iToX = x = targ.first; - pkt.iToY = y = targ.second; + pkt.iNPC_ID = self->id; + pkt.iSpeed = self->speed; + pkt.iToX = self->x = targ.first; + pkt.iToY = self->y = targ.second; pkt.iToZ = plr->z; pkt.iMoveStyle = 1; // notify all nearby players - NPCManager::sendToViewable(this, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE)); + NPCManager::sendToViewable(self, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE)); } /* attack logic * 2/5 represents 400 ms which is the time interval mobs use per movement logic step * if the mob is one move interval away, we should just start attacking anyways. */ - if (distance <= mobRange || distanceToTravel < speed*2/5) { - if (nextAttack == 0 || currTime >= nextAttack) { - nextAttack = currTime + (int)data["m_iDelayTime"] * 100; - Combat::npcAttackPc(this, currTime); + if (distance <= mobRange || distanceToTravel < self->speed*2/5) { + if (self->nextAttack == 0 || currTime >= self->nextAttack) { + self->nextAttack = currTime + (int)self->data["m_iDelayTime"] * 100; + Combat::npcAttackPc(self, currTime); } } // retreat if the player leaves combat range - int xyDistance = hypot(plr->x - roamX, plr->y - roamY); - distance = hypot(xyDistance, plr->z - roamZ); - if (distance >= data["m_iCombatRange"]) { - transition(AIState::RETREAT, target); + int xyDistance = hypot(plr->x - self->roamX, plr->y - self->roamY); + distance = hypot(xyDistance, plr->z - self->roamZ); + if (distance >= self->data["m_iCombatRange"]) { + self->transition(AIState::RETREAT, self->target); } } -void MobAI::incNextMovement(Mob *mob, time_t currTime) { - if (currTime == 0) - currTime = getTime(); +void MobAI::roamingStep(CombatNPC* npc, time_t currTime) { + Mob* self = (Mob*)npc; - int delay = (int)mob->data["m_iDelayTime"] * 1000; - mob->nextMovement = currTime + delay/2 + Rand::rand(delay/2); -} - -void Mob::roamingStep(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 (state != AIState::DEAD && (nextAttack == 0 || currTime >= nextAttack)) { - nextAttack = currTime + 500; - if (aggroCheck(this, currTime)) + if (self->state != AIState::DEAD && (self->nextAttack == 0 || currTime >= self->nextAttack)) { + self->nextAttack = currTime + 500; + if (aggroCheck(self, currTime)) return; } // no random roaming if the mob already has a set path - if (staticPath) + if (self->staticPath) return; - if (groupLeader != 0 && groupLeader != id) // don't roam by yourself without group leader + if (self->groupLeader != 0 && self->groupLeader != self->id) // don't roam by yourself without group leader return; /* @@ -628,155 +681,162 @@ void Mob::roamingStep(time_t currTime) { * Transport::stepNPCPathing() (which ticks at a higher frequency than nextMovement), * so we don't have to check if there's already entries in the queue since we know there won't be. */ - if (nextMovement != 0 && currTime < nextMovement) + if (self->nextMovement != 0 && currTime < self->nextMovement) return; - incNextMovement(this, currTime); + incNextMovement(self, currTime); - int xStart = spawnX - idleRange/2; - int yStart = spawnY - idleRange/2; + int xStart = self->spawnX - self->idleRange/2; + int yStart = self->spawnY - self->idleRange/2; // some mobs don't move (and we mustn't divide/modulus by zero) - if (idleRange == 0 || speed == 0) + if (self->idleRange == 0 || self->speed == 0) return; int farX, farY, distance; - int minDistance = idleRange / 2; + int minDistance = self->idleRange / 2; // pick a random destination - farX = xStart + Rand::rand(idleRange); - farY = yStart + Rand::rand(idleRange); + farX = xStart + Rand::rand(self->idleRange); + farY = yStart + Rand::rand(self->idleRange); - distance = std::abs(std::max(farX - x, farY - y)); + distance = std::abs(std::max(farX - self->x, farY - self->y)); if (distance == 0) distance += 1; // hack to avoid FPE // if it's too short a walk, go further in that direction - farX = x + (farX - x) * minDistance / distance; - farY = y + (farY - y) * minDistance / distance; + farX = self->x + (farX - self->x) * minDistance / distance; + farY = self->y + (farY - self->y) * minDistance / distance; // but don't got out of bounds - farX = std::clamp(farX, xStart, xStart + idleRange); - farY = std::clamp(farY, yStart, yStart + idleRange); + farX = std::clamp(farX, xStart, xStart + self->idleRange); + farY = std::clamp(farY, yStart, yStart + self->idleRange); // halve movement speed if snared - if (cbf & CSB_BIT_DN_MOVE_SPEED) - speed /= 2; + if (self->cbf & CSB_BIT_DN_MOVE_SPEED) + self->speed /= 2; std::queue queue; - Vec3 from = { x, y, z }; - Vec3 to = { farX, farY, z }; + Vec3 from = { self->x, self->y, self->z }; + Vec3 to = { farX, farY, self->z }; // add a route to the queue; to be processed in Transport::stepNPCPathing() - Transport::lerp(&queue, from, to, speed); - Transport::NPCQueues[id] = queue; + Transport::lerp(&queue, from, to, self->speed); + Transport::NPCQueues[self->id] = queue; - if (groupLeader != 0 && groupLeader == id) { + if (self->groupLeader != 0 && self->groupLeader == self->id) { // make followers follow this npc. for (int i = 0; i < 4; i++) { - if (groupMember[i] == 0) + if (self->groupMember[i] == 0) break; - if (NPCManager::NPCs.find(groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[groupMember[i]]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(self->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[self->groupMember[i]]->kind != EntityKind::MOB) { std::cout << "[WARN] roamingStep: leader can't find a group member!" << std::endl; continue; } std::queue queue2; - Mob* followerMob = (Mob*)NPCManager::NPCs[groupMember[i]]; + Mob* followerMob = (Mob*)NPCManager::NPCs[self->groupMember[i]]; from = { followerMob->x, followerMob->y, followerMob->z }; to = { farX + followerMob->offsetX, farY + followerMob->offsetY, followerMob->z }; - Transport::lerp(&queue2, from, to, speed); + Transport::lerp(&queue2, from, to, self->speed); Transport::NPCQueues[followerMob->id] = queue2; } } } -void Mob::retreatStep(time_t currTime) { - if (nextMovement != 0 && currTime < nextMovement) +void MobAI::retreatStep(CombatNPC* npc, time_t currTime) { + Mob* self = (Mob*)npc; + + if (self->nextMovement != 0 && currTime < self->nextMovement) return; - nextMovement = currTime + 400; + self->nextMovement = currTime + 400; // distance between spawn point and current location - int distance = hypot(x - roamX, y - roamY); + int distance = hypot(self->x - self->roamX, self->y - self->roamY); //if (distance > mob->data["m_iIdleRange"]) { if (distance > 10) { INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); - auto targ = lerp(x, y, roamX, roamY, (int)speed*4/5); + auto targ = lerp(self->x, self->y, self->roamX, self->roamY, (int)self->speed*4/5); - pkt.iNPC_ID = id; - pkt.iSpeed = (int)speed * 2; - pkt.iToX = x = targ.first; - pkt.iToY = y = targ.second; - pkt.iToZ = z = spawnZ; + pkt.iNPC_ID = self->id; + pkt.iSpeed = (int)self->speed * 2; + pkt.iToX = self->x = targ.first; + pkt.iToY = self->y = targ.second; + pkt.iToZ = self->z = self->spawnZ; pkt.iMoveStyle = 1; // notify all nearby players - NPCManager::sendToViewable(this, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE)); + NPCManager::sendToViewable(self, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE)); } // if we got there //if (distance <= mob->data["m_iIdleRange"]) { if (distance <= 10) { // retreat back to the spawn point - state = AIState::ROAMING; - hp = maxHealth; - killedTime = 0; - nextAttack = 0; - cbf = 0; - - // cast a return home heal spell, this is the right way(tm) - std::vector targetData = {1, 0, 0, 0, 0}; - for (auto& pwr : Abilities::Powers) - if (pwr.skillType == Abilities::SkillTable[110].skillType) - pwr.handle(id, targetData, 110, Abilities::SkillTable[110].durationTime[0], Abilities::SkillTable[110].powerIntensity[0]); - // clear outlying debuffs - clearDebuff(this); + self->transition(AIState::ROAMING, self->id); } } -void Mob::onInactive() { - // no-op +void MobAI::onRoamStart(CombatNPC* npc, EntityRef src) { + Mob* self = (Mob*)npc; + + self->hp = self->maxHealth; + self->killedTime = 0; + self->nextAttack = 0; + self->cbf = 0; + + // cast a return home heal spell, this is the right way(tm) + // TODO ABILITIES + /*std::vector targetData = { 1, 0, 0, 0, 0 }; + for (auto& pwr : Abilities::Powers) + if (pwr.skillType == Abilities::SkillTable[110].skillType) + pwr.handle(self->id, targetData, 110, Abilities::SkillTable[110].durationTime[0], Abilities::SkillTable[110].powerIntensity[0]);*/ + // clear outlying debuffs + clearDebuff(self); } -void Mob::onRoamStart() { - // stub -} +void MobAI::onCombatStart(CombatNPC* npc, EntityRef src) { + Mob* self = (Mob*)npc; -void Mob::onCombatStart(EntityRef src) { - assert(src.type == EntityType::PLAYER); - target = src.sock; - nextMovement = getTime(); - nextAttack = 0; + assert(src.kind == EntityKind::PLAYER); + self->target = src.sock; + self->nextMovement = getTime(); + self->nextAttack = 0; - roamX = x; - roamY = y; - roamZ = z; + self->roamX = self->x; + self->roamY = self->y; + self->roamZ = self->z; - int skillID = (int)data["m_iPassiveBuff"]; // cast passive - std::vector targetData = { 1, id, 0, 0, 0 }; + int skillID = (int)self->data["m_iPassiveBuff"]; // cast passive + // TODO ABILITIES + /*std::vector targetData = { 1, self->id, 0, 0, 0 }; for (auto& pwr : Abilities::Powers) if (pwr.skillType == Abilities::SkillTable[skillID].skillType) - pwr.handle(id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]); + pwr.handle(self->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]);*/ } -void Mob::onRetreat() { - target = nullptr; - MobAI::clearDebuff(this); - if (groupLeader != 0) - MobAI::groupRetreat(this); +void MobAI::onRetreat(CombatNPC* npc, EntityRef src) { + Mob* self = (Mob*)npc; + + self->target = nullptr; + MobAI::clearDebuff(self); + if (self->groupLeader != 0) + MobAI::groupRetreat(self); } -void Mob::onDeath(EntityRef src) { - target = nullptr; - cbf = 0; - skillStyle = -1; - unbuffTimes.clear(); - killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step? +void MobAI::onDeath(CombatNPC* npc, EntityRef src) { + Mob* self = (Mob*)npc; + + self->target = nullptr; + self->cbf = 0; + self->skillStyle = -1; + self->unbuffTimes.clear(); + self->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step? // check for the edge case where hitting the mob did not aggro it - if (src.type == EntityType::PLAYER && src.isValid()) { + if (src.kind == EntityKind::PLAYER && src.isValid()) { Player* plr = PlayerManager::getPlayer(src.sock); Items::DropRoll rolled; @@ -789,8 +849,8 @@ void Mob::onDeath(EntityRef src) { Combat::genQItemRolls(leader, qitemRolls); if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { - Items::giveMobDrop(src.sock, this, rolled, eventRolled); - Missions::mobKilled(src.sock, type, qitemRolls); + Items::giveMobDrop(src.sock, self, rolled, eventRolled); + Missions::mobKilled(src.sock, self->type, qitemRolls); } else { for (int i = 0; i < leader->groupCnt; i++) { @@ -805,21 +865,21 @@ void Mob::onDeath(EntityRef src) { if (dist > 5000) continue; - Items::giveMobDrop(sockTo, this, rolled, eventRolled); - Missions::mobKilled(sockTo, type, qitemRolls); + Items::giveMobDrop(sockTo, self, rolled, eventRolled); + Missions::mobKilled(sockTo, self->type, qitemRolls); } } } // delay the despawn animation - despawned = false; + self->despawned = false; - auto it = Transport::NPCQueues.find(id); + auto it = Transport::NPCQueues.find(self->id); if (it == Transport::NPCQueues.end() || it->second.empty()) return; // rewind or empty the movement queue - if (staticPath) { + if (self->staticPath) { /* * This is inelegant, but we wind forward in the path until we find the point that * corresponds with the Mob's spawn point. @@ -827,12 +887,12 @@ void Mob::onDeath(EntityRef src) { * IMPORTANT: The check in TableData::loadPaths() must pass or else this will loop forever. */ auto& queue = it->second; - for (auto point = queue.front(); point.x != spawnX || point.y != spawnY; point = queue.front()) { + for (auto point = queue.front(); point.x != self->spawnX || point.y != self->spawnY; point = queue.front()) { queue.pop(); queue.push(point); } } else { - Transport::NPCQueues.erase(id); + Transport::NPCQueues.erase(self->id); } } diff --git a/src/MobAI.hpp b/src/MobAI.hpp index dbb267b..cb6bfc8 100644 --- a/src/MobAI.hpp +++ b/src/MobAI.hpp @@ -4,6 +4,20 @@ #include "NPCManager.hpp" #include "Entities.hpp" +/* kill me */ +struct Mob; +namespace MobAI { + void deadStep(CombatNPC* self, time_t currTime); + void combatStep(CombatNPC* self, time_t currTime); + void roamingStep(CombatNPC* self, time_t currTime); + void retreatStep(CombatNPC* self, time_t currTime); + + void onRoamStart(CombatNPC* self, EntityRef src); + void onCombatStart(CombatNPC* self, EntityRef src); + void onRetreat(CombatNPC* self, EntityRef src); + void onDeath(CombatNPC* self, EntityRef src); +} + struct Mob : public CombatNPC { // general std::unordered_map unbuffTimes = {}; @@ -60,7 +74,18 @@ struct Mob : public CombatNPC { // NOTE: there appear to be discrepancies in the dump hp = maxHealth; - kind = EntityType::MOB; + kind = EntityKind::MOB; + + // AI + stateHandlers[AIState::DEAD] = MobAI::deadStep; + stateHandlers[AIState::COMBAT] = MobAI::combatStep; + stateHandlers[AIState::ROAMING] = MobAI::roamingStep; + stateHandlers[AIState::RETREAT] = MobAI::retreatStep; + + transitionHandlers[AIState::DEAD] = MobAI::onDeath; + transitionHandlers[AIState::COMBAT] = MobAI::onCombatStart; + transitionHandlers[AIState::ROAMING] = MobAI::onRoamStart; + transitionHandlers[AIState::RETREAT] = MobAI::onRetreat; } // constructor for /summon @@ -71,16 +96,8 @@ struct Mob : public CombatNPC { ~Mob() {} - virtual void roamingStep(time_t currTime) override; - virtual void combatStep(time_t currTime) override; - virtual void retreatStep(time_t currTime) override; - virtual void deadStep(time_t currTime) override; - - virtual void onInactive() override; - virtual void onRoamStart() override; - virtual void onCombatStart(EntityRef src) override; - virtual void onRetreat() override; - virtual void onDeath(EntityRef src) override; + virtual int takeDamage(EntityRef src, int amt) override; + virtual void step(time_t currTime) override; auto operator[](std::string s) { return data[s]; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 990d2d1..6b3b253 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -83,7 +83,7 @@ void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t s for (auto it = npc->viewableChunks.begin(); it != npc->viewableChunks.end(); it++) { Chunk* chunk = *it; for (const EntityRef& ref : chunk->entities) { - if (ref.type == EntityType::PLAYER) + if (ref.kind == EntityKind::PLAYER) ref.sock->sendPacket(buf, type, size); } } @@ -275,7 +275,7 @@ BaseNPC* NPCManager::getNearestNPC(std::set* chunks, int X, int Y, int Z for (auto c = chunks->begin(); c != chunks->end(); c++) { // haha get it Chunk* chunk = *c; for (auto ent = chunk->entities.begin(); ent != chunk->entities.end(); ent++) { - if (ent->type == EntityType::PLAYER) + if (ent->kind == EntityKind::PLAYER) continue; BaseNPC* npcTemp = (BaseNPC*)ent->getEntity(); @@ -351,7 +351,7 @@ void NPCManager::queueNPCRemoval(int32_t id) { static void step(CNServer *serv, time_t currTime) { for (auto& pair : NPCs) { - if (pair.second->kind != EntityType::COMBAT_NPC && pair.second->kind != EntityType::MOB) + if (pair.second->kind != EntityKind::COMBAT_NPC && pair.second->kind != EntityKind::MOB) continue; auto npc = (CombatNPC*)pair.second; diff --git a/src/Nanos.cpp b/src/Nanos.cpp index 0a8318d..dbab11a 100644 --- a/src/Nanos.cpp +++ b/src/Nanos.cpp @@ -87,11 +87,12 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) { // passive nano unbuffing if (Abilities::SkillTable[skillID].drainType == 2) { - std::vector targetData = Abilities::findTargets(plr, skillID); + // TODO ABILITIES + /*std::vector targetData = Abilities::findTargets(plr, skillID); for (auto& pwr : Abilities::Powers) if (pwr.skillType == Abilities::SkillTable[skillID].skillType) - Abilities::removeBuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, 0,(Abilities::SkillTable[skillID].targetType == 3)); + Abilities::removeBuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, 0,(Abilities::SkillTable[skillID].targetType == 3));*/ } if (nanoID >= NANO_COUNT || nanoID < 0) @@ -102,20 +103,20 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) { // passive nano buffing if (Abilities::SkillTable[skillID].drainType == 2) { - std::vector targetData = Abilities::findTargets(plr, skillID); - int boost = 0; if (getNanoBoost(plr)) boost = 1; - for (auto& pwr : Abilities::Powers) { - if (pwr.skillType == Abilities::SkillTable[skillID].skillType) { - resp.eCSTB___Add = 1; // the part that makes nano go ZOOMAZOOM - plr->nanoDrainRate = Abilities::SkillTable[skillID].batteryUse[boost*3]; + // TODO ABILITIES + //std::vector targetData = Abilities::findTargets(plr, skillID); + //for (auto& pwr : Abilities::Powers) { + // if (pwr.skillType == Abilities::SkillTable[skillID].skillType) { + // resp.eCSTB___Add = 1; // the part that makes nano go ZOOMAZOOM + // plr->nanoDrainRate = Abilities::SkillTable[skillID].batteryUse[boost*3]; - pwr.handle(sock, targetData, nanoID, skillID, 0, Abilities::SkillTable[skillID].powerIntensity[boost]); - } - } + // pwr.handle(sock, targetData, nanoID, skillID, 0, Abilities::SkillTable[skillID].powerIntensity[boost]); + // } + //} } if (!silent) // silent nano death but only for the summoning player @@ -296,8 +297,6 @@ static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) { std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl; ) - std::vector targetData = Abilities::findTargets(plr, skillID, data); - int boost = 0; if (getNanoBoost(plr)) boost = 1; @@ -306,9 +305,11 @@ static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) { if (plr->Nanos[plr->activeNano].iStamina < 0) plr->Nanos[plr->activeNano].iStamina = 0; + // TODO ABILITIES + /*std::vector targetData = Abilities::findTargets(plr, skillID, data); for (auto& pwr : Abilities::Powers) if (pwr.skillType == Abilities::SkillTable[skillID].skillType) - pwr.handle(sock, targetData, nanoID, skillID, Abilities::SkillTable[skillID].durationTime[boost], Abilities::SkillTable[skillID].powerIntensity[boost]); + pwr.handle(sock, targetData, nanoID, skillID, Abilities::SkillTable[skillID].durationTime[boost], Abilities::SkillTable[skillID].powerIntensity[boost]);*/ if (plr->Nanos[plr->activeNano].iStamina < 0) summonNano(sock, -1); diff --git a/src/Player.hpp b/src/Player.hpp index b0c970a..c10446a 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -85,7 +85,7 @@ struct Player : public Entity, public ICombatant { time_t lastShot = 0; std::vector buyback = {}; - Player() { kind = EntityType::PLAYER; } + Player() { kind = EntityKind::PLAYER; } virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 98f351b..e79bde9 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -353,7 +353,7 @@ void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, siz for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) { Chunk* chunk = *it; for (const EntityRef& ref : chunk->entities) { - if (ref.type != EntityType::PLAYER || ref.sock == sock) + if (ref.kind != EntityKind::PLAYER || ref.sock == sock) continue; ref.sock->sendPacket(buf, type, size); @@ -411,7 +411,8 @@ static void revivePlayer(CNSocket* sock, CNPacketData* data) { if (!(plr->iConditionBitFlag & CSB_BIT_PHOENIX)) return; // sanity check plr->Nanos[plr->activeNano].iStamina = 0; - Abilities::applyBuff(sock, plr->Nanos[plr->activeNano].iSkillID, 2, 1, 0); + // TODO ABILITIES + //Abilities::applyBuff(sock, plr->Nanos[plr->activeNano].iSkillID, 2, 1, 0); // fallthrough case ePCRegenType::HereByPhoenixGroup: // revived by group member's nano plr->HP = PC_MAXHEALTH(plr->level) / 2; diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 0ab2049..883cf74 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -43,7 +43,7 @@ namespace PlayerManager { for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) { Chunk* chunk = *it; for (const EntityRef& ref : chunk->entities) { - if (ref.type != EntityType::PLAYER || ref.sock == sock) + if (ref.kind != EntityKind::PLAYER || ref.sock == sock) continue; ref.sock->sendPacket(pkt, type); diff --git a/src/TableData.cpp b/src/TableData.cpp index 6b11c98..3461564 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -1226,7 +1226,7 @@ void TableData::flush() { continue; int x, y, z; - if (npc->kind == EntityType::MOB) { + if (npc->kind == EntityKind::MOB) { Mob *m = (Mob*)npc; x = m->spawnX; y = m->spawnY; @@ -1259,7 +1259,7 @@ void TableData::flush() { int x, y, z; std::vector followers; - if (npc->kind == EntityType::MOB) { + if (npc->kind == EntityKind::MOB) { Mob* m = (Mob*)npc; x = m->spawnX; y = m->spawnY; @@ -1271,7 +1271,7 @@ void TableData::flush() { // add follower data to vector; go until OOB or until follower ID is 0 for (int i = 0; i < 4 && m->groupMember[i] > 0; i++) { - if (NPCManager::NPCs.find(m->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[m->groupMember[i]]->kind != EntityType::MOB) { + if (NPCManager::NPCs.find(m->groupMember[i]) == NPCManager::NPCs.end() || NPCManager::NPCs[m->groupMember[i]]->kind != EntityKind::MOB) { std::cout << "[WARN] Follower with ID " << m->groupMember[i] << " not found; skipping\n"; continue; } diff --git a/src/Transport.cpp b/src/Transport.cpp index 9fe2ccc..0374e00 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -257,13 +257,13 @@ static void stepNPCPathing() { } // skip if not simulating mobs - if (npc->kind == EntityType::MOB && !MobAI::simulateMobs) { + if (npc->kind == EntityKind::MOB && !MobAI::simulateMobs) { it++; continue; } // do not roam if not roaming - if (npc->kind == EntityType::MOB && ((Mob*)npc)->state != AIState::ROAMING) { + if (npc->kind == EntityKind::MOB && ((Mob*)npc)->state != AIState::ROAMING) { it++; continue; } @@ -280,7 +280,7 @@ static void stepNPCPathing() { // TODO: move walking logic into Entity stack switch (npc->kind) { - case EntityType::BUS: + case EntityKind::BUS: INITSTRUCT(sP_FE2CL_TRANSPORTATION_MOVE, busMove); busMove.eTT = 3; @@ -293,7 +293,7 @@ static void stepNPCPathing() { NPCManager::sendToViewable(npc, &busMove, P_FE2CL_TRANSPORTATION_MOVE, sizeof(sP_FE2CL_TRANSPORTATION_MOVE)); break; - case EntityType::MOB: + case EntityKind::MOB: MobAI::incNextMovement((Mob*)npc); /* fallthrough */ default: @@ -385,7 +385,7 @@ NPCPath* Transport::findApplicablePath(int32_t id, int32_t type, int taskID) { void Transport::constructPathNPC(int32_t id, NPCPath* path) { BaseNPC* npc = NPCManager::NPCs[id]; - if (npc->kind == EntityType::MOB) + if (npc->kind == EntityKind::MOB) ((Mob*)(npc))->staticPath = true; npc->loopingPath = path->isLoop;