From e325f7a40bc80c990af86887f8095c14d61a30ed Mon Sep 17 00:00:00 2001 From: gsemaj Date: Tue, 25 Jul 2023 13:49:40 -0400 Subject: [PATCH] Implement buff handling for CombatNPC --- src/Abilities.cpp | 80 +++++++++++++++------ src/Buffs.cpp | 48 ++++++++++++- src/Buffs.hpp | 3 + src/Combat.cpp | 88 +++++++++++++++++------ src/Entities.hpp | 4 +- src/MobAI.cpp | 127 ++++++++++++---------------------- src/NPCManager.cpp | 2 +- src/Player.hpp | 2 +- src/servers/CNShardServer.hpp | 1 + 9 files changed, 224 insertions(+), 131 deletions(-) diff --git a/src/Abilities.cpp b/src/Abilities.cpp index 63586a9..a3b3f86 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -1,9 +1,12 @@ #include "Abilities.hpp" +#include "servers/CNShardServer.hpp" + #include "NPCManager.hpp" #include "PlayerManager.hpp" #include "Buffs.hpp" #include "Nanos.hpp" +#include "MobAI.hpp" using namespace Abilities; @@ -55,7 +58,7 @@ static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombat duration = skill->durationTime[power]; strength = skill->values[0][power]; BuffStack debuff = { - duration, // ticks + (duration * 100) / MS_PER_COMBAT_TICK, // ticks strength, // value source->getRef(), // source BuffClass::NANO, // buff class @@ -64,9 +67,10 @@ static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombat target->addBuff(timeBuffId, [](EntityRef self, Buff* buff, int status, BuffStack* stack) { Buffs::timeBuffUpdate(self, buff, status, stack); + if(status == ETBU_DEL) Buffs::timeBuffTimeout(self); }, [](EntityRef self, Buff* buff, time_t currTime) { - // no-op + Buffs::timeBuffTick(self, buff); }, &debuff); } @@ -82,27 +86,53 @@ static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombat } static SkillResult handleSkillLeech(SkillData* skill, int power, ICombatant* source, ICombatant* target) { - // TODO abilities - return SkillResult(); + EntityRef sourceRef = source->getRef(); + int heal = skill->values[0][power]; + int healed = source->heal(sourceRef, heal); + int damage = heal * 2; + int dealt = target->takeDamage(sourceRef, damage); + + sSkillResult_Leech result{}; + + result.Damage.eCT = target->getCharType(); + result.Damage.iID = target->getID(); + result.Damage.bProtected = dealt <= 0; + result.Damage.iDamage = dealt; + result.Damage.iHP = target->getCurrentHP(); + + result.Heal.eCT = result.Damage.eCT; + result.Heal.iID = result.Damage.iID; + result.Heal.iHealHP = healed; + result.Heal.iHP = source->getCurrentHP(); + + return SkillResult(sizeof(sSkillResult_Leech), &result); } static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) { int duration = skill->durationTime[power]; int strength = skill->values[0][power]; BuffStack passiveBuff = { - skill->drainType == SkillDrainType::PASSIVE ? 1 : duration, // ticks + skill->drainType == SkillDrainType::PASSIVE ? 1 : (duration * 100) / MS_PER_COMBAT_TICK, // ticks strength, // value source->getRef(), // source source == target ? BuffClass::NANO : BuffClass::GROUP_NANO, // buff class }; int timeBuffId = Abilities::getCSTBFromST(skill->skillType); + SkillDrainType drainType = skill->drainType; if(!target->addBuff(timeBuffId, - [](EntityRef self, Buff* buff, int status, BuffStack* stack) { - Buffs::timeBuffUpdate(self, buff, status, stack); + [drainType](EntityRef self, Buff* buff, int status, BuffStack* stack) { + if(buff->id == ECSB_BOUNDINGBALL) { + // drain + ICombatant* combatant = dynamic_cast(self.getEntity()); + combatant->takeDamage(buff->getLastSource(), 0); // aggro + } + if(drainType == SkillDrainType::ACTIVE && status == ETBU_DEL) + Buffs::timeBuffTimeout(self); }, [](EntityRef self, Buff* buff, time_t currTime) { - // no-op + if(buff->id == ECSB_BOUNDINGBALL) + Buffs::tickDrain(self, buff); // drain }, &passiveBuff)) return SkillResult(); // no result if already buffed @@ -335,6 +365,12 @@ void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector pkt->iSkillID = skillID; pkt->eST = (int32_t)skill->skillType; pkt->iTargetCnt = (int32_t)results.size(); + if(npc.kind == EntityKind::MOB) { + Mob* mob = dynamic_cast(entity); + pkt->iValue1 = mob->hitX; + pkt->iValue2 = mob->hitY; + pkt->iValue3 = mob->hitZ; + } attachSkillResults(results, (uint8_t*)(pkt + 1)); NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen); @@ -411,12 +447,6 @@ int Abilities::getCSTBFromST(SkillType skillType) { case SkillType::PROTECTINFECTION: result = ECSB_PROTECT_INFECTION; break; - case SkillType::SNARE: - result = ECSB_DN_MOVE_SPEED; - break; - case SkillType::SLEEP: - result = ECSB_MEZ; - break; case SkillType::MINIMAPENEMY: result = ECSB_MINIMAP_ENEMY; break; @@ -429,15 +459,9 @@ int Abilities::getCSTBFromST(SkillType skillType) { case SkillType::REWARDCASH: result = ECSB_REWARD_CASH; break; - case SkillType::INFECTIONDAMAGE: - result = ECSB_INFECTION; - break; case SkillType::FREEDOM: result = ECSB_FREEDOM; break; - case SkillType::BOUNDINGBALL: - result = ECSB_BOUNDINGBALL; - break; case SkillType::INVULNERABLE: result = ECSB_INVULNERABLE; break; @@ -446,6 +470,22 @@ int Abilities::getCSTBFromST(SkillType skillType) { break; case SkillType::NANOSTIMPAK: result = ECSB_STIMPAKSLOT1; + // shift as necessary + break; + case SkillType::SNARE: + result = ECSB_DN_MOVE_SPEED; + break; + case SkillType::STUN: + result = ECSB_STUN; + break; + case SkillType::SLEEP: + result = ECSB_MEZ; + break; + case SkillType::INFECTIONDAMAGE: + result = ECSB_INFECTION; + break; + case SkillType::BOUNDINGBALL: + result = ECSB_BOUNDINGBALL; break; default: break; diff --git a/src/Buffs.cpp b/src/Buffs.cpp index 072001f..d407fac 100644 --- a/src/Buffs.cpp +++ b/src/Buffs.cpp @@ -95,6 +95,12 @@ int Buff::getValue(BuffValueSelector selector) { return value; } +EntityRef Buff::getLastSource() { + if(stacks.empty()) + return self; + return stacks.back().source; +} + bool Buff::isStale() { return stacks.empty(); } @@ -137,15 +143,55 @@ void Buffs::timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* st self.sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); } +void Buffs::timeBuffTick(EntityRef self, Buff* buff) { + if(self.kind != EntityKind::COMBAT_NPC && self.kind != EntityKind::MOB) + return; // not implemented + Entity* entity = self.getEntity(); + ICombatant* combatant = dynamic_cast(entity); + + INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK, pkt); + pkt.eCT = combatant->getCharType(); + pkt.iID = combatant->getID(); + pkt.iTB_ID = buff->id; + NPCManager::sendToViewable(entity, &pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK)); +} + void Buffs::timeBuffTimeout(EntityRef self) { if(self.kind != EntityKind::PLAYER && self.kind != EntityKind::COMBAT_NPC && self.kind != EntityKind::MOB) return; // not a combatant Entity* entity = self.getEntity(); ICombatant* combatant = dynamic_cast(entity); INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt); // send a buff timeout to other players - pkt.eCT = combatant->getCharType(); + int32_t eCharType = combatant->getCharType(); + pkt.eCT = eCharType == 4 ? 2 : eCharType; // convention not followed by client here pkt.iID = combatant->getID(); pkt.iConditionBitFlag = combatant->getCompositeCondition(); NPCManager::sendToViewable(entity, &pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); } + +void Buffs::tickDrain(EntityRef self, Buff* buff) { + if(self.kind != EntityKind::COMBAT_NPC && self.kind != EntityKind::MOB) + return; // not implemented + Entity* entity = self.getEntity(); + ICombatant* combatant = dynamic_cast(entity); + int damage = combatant->takeDamage(buff->getLastSource(), combatant->getMaxHP() / 100); + + size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Damage); + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; + memset(respbuf, 0, resplen); + + sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf; + pkt->iID = self.id; + pkt->eCT = combatant->getCharType(); + pkt->iTB_ID = ECSB_BOUNDINGBALL; + + sSkillResult_Damage *drain = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK)); + drain->iDamage = damage; + drain->iHP = combatant->getCurrentHP(); + drain->eCT = pkt->eCT; + drain->iID = pkt->iID; + + NPCManager::sendToViewable(self.getEntity(), (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); +} #pragma endregion diff --git a/src/Buffs.hpp b/src/Buffs.hpp index 3247143..8f395e1 100644 --- a/src/Buffs.hpp +++ b/src/Buffs.hpp @@ -66,6 +66,7 @@ public: BuffClass maxClass(); int getValue(BuffValueSelector selector); + EntityRef getLastSource(); /* * In general, a Buff object won't exist @@ -86,5 +87,7 @@ public: namespace Buffs { void timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* stack); + void timeBuffTick(EntityRef self, Buff* buff); void timeBuffTimeout(EntityRef self); + void tickDrain(EntityRef self, Buff* buff); } diff --git a/src/Combat.cpp b/src/Combat.cpp index 93e3542..aa8b912 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -24,13 +24,11 @@ bool Player::addBuff(int buffId, BuffCallback onUpdate, BuffCal if(!isAlive()) return false; - EntityRef self = PlayerManager::getSockFromID(iID); - if(!hasBuff(buffId)) { - buffs[buffId] = new Buff(buffId, self, onUpdate, onTick, stack); + buffs[buffId] = new Buff(buffId, getRef(), onUpdate, onTick, stack); return true; } - + buffs[buffId]->updateCallbacks(onUpdate, onTick); buffs[buffId]->addStack(stack); return false; @@ -51,9 +49,9 @@ void Player::removeBuff(int buffId) { } } -void Player::removeBuff(int buffId, int buffClass) { +void Player::removeBuff(int buffId, BuffClass buffClass) { if(hasBuff(buffId)) { - buffs[buffId]->clear((BuffClass)buffClass); + buffs[buffId]->clear(buffClass); // buff might not be stale since another buff class might remain if(buffs[buffId]->isStale()) { delete buffs[buffId]; @@ -63,14 +61,13 @@ void Player::removeBuff(int buffId, int buffClass) { } void Player::clearBuffs(bool force) { - for(auto buff : buffs) { - if(!force) { - removeBuff(buff.first); - } else { - delete buff.second; - } + auto it = buffs.begin(); + while(it != buffs.end()) { + Buff* buff = (*it).second; + if(!force) buff->clear(); + delete buff; + it = buffs.erase(it); } - buffs.clear(); } bool Player::hasBuff(int buffId) { @@ -171,31 +168,75 @@ void Player::step(time_t currTime) { // buffs for(auto buffEntry : buffs) { buffEntry.second->combatTick(currTime); + if(!isAlive()) + break; // unsafe to keep ticking if we're dead } } #pragma endregion #pragma region CombatNPC bool CombatNPC::addBuff(int buffId, BuffCallback onUpdate, BuffCallback onTick, BuffStack* stack) { /* stubbed */ + if(!isAlive()) + return false; + + if(!hasBuff(buffId)) { + buffs[buffId] = new Buff(buffId, getRef(), onUpdate, onTick, stack); + return true; + } + + buffs[buffId]->updateCallbacks(onUpdate, onTick); + buffs[buffId]->addStack(stack); return false; } Buff* CombatNPC::getBuff(int buffId) { /* stubbed */ + if(hasBuff(buffId)) { + return buffs[buffId]; + } return nullptr; } -void CombatNPC::removeBuff(int buffId) { /* stubbed */ } - -void CombatNPC::removeBuff(int buffId, int buffClass) { /* stubbed */ } - -void CombatNPC::clearBuffs(bool force) { /* stubbed */ } - -bool CombatNPC::hasBuff(int buffId) { /* stubbed */ - return false; +void CombatNPC::removeBuff(int buffId) { + if(hasBuff(buffId)) { + buffs[buffId]->clear(); + delete buffs[buffId]; + buffs.erase(buffId); + } } -int CombatNPC::getCompositeCondition() { /* stubbed */ - return 0; +void CombatNPC::removeBuff(int buffId, BuffClass buffClass) { + if(hasBuff(buffId)) { + buffs[buffId]->clear(buffClass); + // buff might not be stale since another buff class might remain + if(buffs[buffId]->isStale()) { + delete buffs[buffId]; + buffs.erase(buffId); + } + } +} + +void CombatNPC::clearBuffs(bool force) { + auto it = buffs.begin(); + while(it != buffs.end()) { + Buff* buff = (*it).second; + if(!force) buff->clear(); + delete buff; + it = buffs.erase(it); + } +} + +bool CombatNPC::hasBuff(int buffId) { + auto buff = buffs.find(buffId); + return buff != buffs.end() && !buff->second->isStale(); +} + +int CombatNPC::getCompositeCondition() { + int conditionBitFlag = 0; + for(auto buff : buffs) { + if(!buff.second->isStale() && buff.second->id > 0) + conditionBitFlag |= CSB_FROM_ECSB(buff.first); + } + return conditionBitFlag; } int CombatNPC::takeDamage(EntityRef src, int amt) { @@ -916,6 +957,7 @@ static void playerTick(CNServer *serv, time_t currTime) { auto it = plr->buffs.begin(); while(it != plr->buffs.end()) { Buff* buff = (*it).second; + //buff->combatTick() gets called in Player::step buff->tick(currTime); if(buff->isStale()) { // garbage collect diff --git a/src/Entities.hpp b/src/Entities.hpp index 9dffe5d..2867801 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -47,7 +47,7 @@ public: virtual bool addBuff(int, BuffCallback, BuffCallback, BuffStack*) = 0; virtual Buff* getBuff(int) = 0; virtual void removeBuff(int) = 0; - virtual void removeBuff(int, int) = 0; + virtual void removeBuff(int, BuffClass) = 0; virtual void clearBuffs(bool) = 0; virtual bool hasBuff(int) = 0; virtual int getCompositeCondition() = 0; @@ -125,7 +125,7 @@ struct CombatNPC : public BaseNPC, public ICombatant { virtual bool addBuff(int buffId, BuffCallback onUpdate, BuffCallback onTick, BuffStack* stack) override; virtual Buff* getBuff(int buffId) override; virtual void removeBuff(int buffId) override; - virtual void removeBuff(int buffId, int buffClass) override; + virtual void removeBuff(int buffId, BuffClass buffClass) override; virtual void clearBuffs(bool force) override; virtual bool hasBuff(int buffId) override; virtual int getCompositeCondition() override; diff --git a/src/MobAI.cpp b/src/MobAI.cpp index 53b8e2b..1d5b8f2 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -295,11 +295,13 @@ 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 - // 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);*/ + SkillData skill = Abilities::SkillTable[skillID]; + skill.durationTime[0] = 0; + skill.values[0][0] = 200; // have to set + skill.values[0][1] = 200; // all of these + skill.values[0][2] = 200; // because the player might + skill.values[0][3] = 200; // have a boost + Abilities::useNanoSkill(sock, &skill, *plr->getActiveNano(), { mob }); } else { respdata[i].iHitFlag = HF_BIT_STYLE_LOSE; respdata[i].iDamage = Abilities::SkillTable[skillID].values[0][0] * PC_MAXHEALTH((int)mob->data["m_iNpcLevel"]) / 1500; @@ -326,12 +328,6 @@ static void dealCorruption(Mob *mob, std::vector targetData, int skillID, i } static void useAbilities(Mob *mob, time_t currTime) { - /* - * targetData approach - * first integer is the count - * second to fifth integers are IDs, these can be either player iID or mob's iID - * whether the skill targets players or mobs is determined by the skill packet being fired - */ Player *plr = PlayerManager::getPlayer(mob->target); if (mob->skillStyle >= 0) { // corruption hit @@ -346,7 +342,7 @@ static void useAbilities(Mob *mob, time_t currTime) { if (mob->skillStyle == -2) { // eruption hit int skillID = (int)mob->data["m_iMegaType"]; - std::vector targetData = {0, 0, 0, 0, 0}; + std::vector targets{}; // find the players within range of eruption for (auto it = mob->viewableChunks.begin(); it != mob->viewableChunks.end(); it++) { @@ -356,26 +352,22 @@ static void useAbilities(Mob *mob, time_t currTime) { if (ref.kind != EntityKind::PLAYER) continue; - CNSocket *s= ref.sock; + CNSocket *s = ref.sock; Player *plr = PlayerManager::getPlayer(s); - if (plr->HP <= 0) + if (!plr->isAlive()) continue; int distance = hypot(mob->hitX - plr->x, mob->hitY - plr->y); if (distance < Abilities::SkillTable[skillID].effectArea) { - targetData[0] += 1; - targetData[targetData[0]] = plr->iID; - if (targetData[0] > 3) // make sure not to have more than 4 + targets.push_back(plr); + if (targets.size() > 3) // make sure not to have more than 4 break; } } } - // 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]);*/ + Abilities::useNPCSkill(mob->id, skillID, targets); mob->skillStyle = -3; // eruption cooldown mob->nextAttack = currTime + 1000; return; @@ -393,14 +385,11 @@ static void useAbilities(Mob *mob, time_t currTime) { if (random < prob1) { // active skill hit int skillID = (int)mob->data["m_iActiveSkill1"]; - // 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]); - // } + SkillData* skill = &Abilities::SkillTable[skillID]; + int debuffID = Abilities::getCSTBFromST(skill->skillType); + if(plr->hasBuff(debuffID)) + return; // prevent debuffing a player twice + Abilities::useNPCSkill(mob->getRef(), skillID, { plr }); mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100; return; } @@ -441,32 +430,6 @@ static void useAbilities(Mob *mob, time_t currTime) { return; } -// TODO abiilities -static void drainMobHP(Mob *mob, int amount) { - size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Damage); - assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; - - memset(respbuf, 0, resplen); - - 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->id; - pkt->eCT = 4; // mob - pkt->iTB_ID = ECSB_BOUNDINGBALL; - - drain->eCT = 4; - drain->iID = mob->id; - drain->iDamage = amount; - drain->iHP = mob->hp -= amount; - - NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); - - if (mob->hp <= 0) - mob->transition(AIState::DEAD, mob->target); -} - void MobAI::incNextMovement(Mob* mob, time_t currTime) { if (currTime == 0) currTime = getTime(); @@ -553,20 +516,23 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { return; } - // drain TODO abilities - if (self->skillStyle < 0 && (self->lastDrainTime == 0 || currTime - self->lastDrainTime >= 1000) - && self->hasBuff(ECSB_BOUNDINGBALL)) { - drainMobHP(self, self->maxHealth / 20); // lose 5% every second - self->lastDrainTime = currTime; - } - - // if drain killed the mob, return early - if (self->hp <= 0) - return; - // tick buffs - for(auto buffEntry : self->buffs) { - buffEntry.second->combatTick(currTime); + auto it = npc->buffs.begin(); + while(it != npc->buffs.end()) { + Buff* buff = (*it).second; + buff->combatTick(currTime); + + // if mob state changed, end the step + if(self->state != AIState::COMBAT) + return; + + buff->tick(currTime); + if(buff->isStale()) { + // garbage collect + it = npc->buffs.erase(it); + delete buff; + } + else it++; } // skip attack if stunned or asleep @@ -586,6 +552,7 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { } int distanceToTravel = INT_MAX; + int speed = self->speed; // movement logic: move when out of range but don't move while casting a skill if (distance > mobRange && self->skillStyle == -1) { if (self->nextMovement != 0 && currTime < self->nextMovement) @@ -596,7 +563,7 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { // halve movement speed if snared if (self->hasBuff(ECSB_DN_MOVE_SPEED)) - self->speed /= 2; + speed /= 2; int targetX = plr->x; int targetY = plr->y; @@ -605,9 +572,9 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { targetY += self->offsetY*distance/(self->idleRange + 1); } - distanceToTravel = std::min(distance-mobRange+1, self->speed*2/5); + distanceToTravel = std::min(distance-mobRange+1, speed*2/5); auto targ = lerp(self->x, self->y, targetX, targetY, distanceToTravel); - if (distanceToTravel < self->speed*2/5 && currTime >= self->nextAttack) + if (distanceToTravel < speed*2/5 && currTime >= self->nextAttack) self->nextAttack = 0; NPCManager::updateNPCPosition(self->id, targ.first, targ.second, self->z, self->instanceID, self->angle); @@ -615,7 +582,7 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); pkt.iNPC_ID = self->id; - pkt.iSpeed = self->speed; + pkt.iSpeed = speed; pkt.iToX = self->x = targ.first; pkt.iToY = self->y = targ.second; pkt.iToZ = plr->z; @@ -776,11 +743,8 @@ void MobAI::onRoamStart(CombatNPC* npc, EntityRef src) { self->nextAttack = 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]);*/ + Abilities::useNPCSkill(npc->getRef(), 110, { npc }); + // clear outlying debuffs clearDebuff(self); } @@ -797,12 +761,9 @@ void MobAI::onCombatStart(CombatNPC* npc, EntityRef src) { self->roamY = self->y; self->roamZ = self->z; - 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(self->id, targetData, skillID, Abilities::SkillTable[skillID].durationTime[0], Abilities::SkillTable[skillID].powerIntensity[0]);*/ + int skillID = (int)self->data["m_iPassiveBuff"]; + if(skillID != 0) // cast passive + Abilities::useNPCSkill(npc->getRef(), skillID, { npc }); } void MobAI::onRetreat(CombatNPC* npc, EntityRef src) { diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index f1a5e13..cba6656 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -376,5 +376,5 @@ void NPCManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); - REGISTER_SHARD_TIMER(step, 200); + REGISTER_SHARD_TIMER(step, MS_PER_COMBAT_TICK); } diff --git a/src/Player.hpp b/src/Player.hpp index 5ca112c..14019ab 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -92,7 +92,7 @@ struct Player : public Entity, public ICombatant { virtual bool addBuff(int buffId, BuffCallback onUpdate, BuffCallback onTick, BuffStack* stack) override; virtual Buff* getBuff(int buffId) override; virtual void removeBuff(int buffId) override; - virtual void removeBuff(int buffId, int buffClass) override; + virtual void removeBuff(int buffId, BuffClass buffClass) override; virtual void clearBuffs(bool force) override; virtual bool hasBuff(int buffId) override; virtual int getCompositeCondition() override; diff --git a/src/servers/CNShardServer.hpp b/src/servers/CNShardServer.hpp index c585428..7ed4e3f 100644 --- a/src/servers/CNShardServer.hpp +++ b/src/servers/CNShardServer.hpp @@ -8,6 +8,7 @@ #define REGISTER_SHARD_PACKET(pactype, handlr) CNShardServer::ShardPackets[pactype] = handlr; #define REGISTER_SHARD_TIMER(handlr, delta) CNShardServer::Timers.push_back(TimerEvent(handlr, delta)); #define MS_PER_PLAYER_TICK 500 +#define MS_PER_COMBAT_TICK 200 class CNShardServer : public CNServer { private: