diff --git a/src/Abilities.cpp b/src/Abilities.cpp index cc2900e..cc99c10 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -93,19 +93,28 @@ static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* sour 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) { + [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 + } Buffs::timeBuffUpdate(self, buff, status, stack); + 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 diff --git a/src/Buffs.cpp b/src/Buffs.cpp index c5fd82a..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(); } @@ -142,6 +148,7 @@ void Buffs::timeBuffTick(EntityRef self, Buff* buff) { 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(); @@ -161,4 +168,30 @@ void Buffs::timeBuffTimeout(EntityRef self) { 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 95718af..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 @@ -88,4 +89,5 @@ 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/MobAI.cpp b/src/MobAI.cpp index 70a8d8e..bcd6a85 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -553,17 +553,6 @@ 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 auto it = npc->buffs.begin(); while(it != npc->buffs.end()) { @@ -578,6 +567,10 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) { else it++; } + // if debuffs killed the mob, return early + if (self->hp <= 0) + return; + // skip attack if stunned or asleep if (self->hasBuff(ECSB_STUN) || self->hasBuff(ECSB_MEZ)) { self->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up.