OpenFusion/src/Abilities.cpp

728 lines
27 KiB
C++

#include "Abilities.hpp"
#include "PlayerManager.hpp"
#include "Player.hpp"
#include "NPCManager.hpp"
#include "Nanos.hpp"
#include "Groups.hpp"
#include "Eggs.hpp"
/*
* TODO: This file is in desperate need of deduplication and rewriting.
*/
std::map<int32_t, SkillData> 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<int> Abilities::findTargets(Player* plr, int skillID, CNPacketData* data) {
std::vector<int> 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<int> 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;
Combat::hitMob(sock, mob, 0);
respdata[i].eCT = 4;
respdata[i].iID = mob->id;
respdata[i].bProtected = 1;
if (mob->skillStyle < 0 && mob->state != MobState::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;
Combat::hitMob(sock, mob, 0); // just to gain aggro
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 != MobState::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 = Combat::hitMob(sock, mob, 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 = Combat::hitMob(sock, mob, 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<CNSocket*, int32_t> 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) {
mob->target = nullptr;
mob->state = MobState::RETREAT;
if (!MobAI::aggroCheck(mob, getTime())) {
MobAI::clearDebuff(mob);
if (mob->groupLeader != 0)
MobAI::groupRetreat(mob);
}
}
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) {
mob->target = nullptr;
mob->state = MobState::RETREAT;
if (!MobAI::aggroCheck(mob, getTime())) {
MobAI::clearDebuff(mob);
if (mob->groupLeader != 0)
MobAI::groupRetreat(mob);
}
}
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) {
mob->target = nullptr;
mob->state = MobState::RETREAT;
if (!MobAI::aggroCheck(mob, getTime())) {
MobAI::clearDebuff(mob);
if (mob->groupLeader != 0)
MobAI::groupRetreat(mob);
}
}
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<class sPAYLOAD,
bool (*work)(EntityRef, sPAYLOAD*, int, int32_t, int32_t, int16_t, int16_t, int16_t)>
void power(EntityRef ref, std::vector<int> targetData,
int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount,
int16_t skillType, int32_t bitFlag, int16_t timeBuffID) {
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<Power> Powers = {};/*
Power(EST_STUN, CSB_BIT_STUN, ECSB_STUN, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Heal_HP, doHeal>),
Power(EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, ECSB_BOUNDINGBALL, power<sSkillResult_Buff, doDebuff>),
Power(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Damage, doDamage>),
Power(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Heal_HP, doLeech>),
Power(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, power<sSkillResult_Buff, doBuff>),
Power(EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, power<sSkillResult_Buff, doBuff>),
Power(EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, power<sSkillResult_Buff, doBuff>),
Power(EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, power<sSkillResult_Buff, doBuff>),
Power(EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, power<sSkillResult_Buff, doBuff>),
Power(EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, power<sSkillResult_Buff, doBuff>),
Power(EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, power<sSkillResult_Buff, doBuff>),
Power(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, power<sSkillResult_Buff, doBuff>),
Power(EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, power<sSkillResult_Buff, doBuff>),
Power(EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, power<sSkillResult_Buff, doBuff>),
Power(EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, power<sSkillResult_Buff, doBuff>),
Power(EST_RECALL, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Move, doMove>),
Power(EST_RECALL_GROUP, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Move, doMove>),
Power(EST_RETROROCKET_SELF, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Buff, doBuff>),
Power(EST_PHOENIX_GROUP, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Resurrect, doResurrect>),
Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT1, ECSB_STIMPAKSLOT1, power<sSkillResult_Buff, doBuff>),
Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT2, ECSB_STIMPAKSLOT2, power<sSkillResult_Buff, doBuff>),
Power(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT3, ECSB_STIMPAKSLOT3, power<sSkillResult_Buff, doBuff>),
//
Power(EST_STUN, CSB_BIT_STUN, ECSB_STUN, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Heal_HP, doHeal>),
Power(EST_RETURNHOMEHEAL, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Heal_HP, doReturnHeal>),
Power(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Damage, doDamage>),
Power(EST_BATTERYDRAIN, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_BatteryDrain, doBatteryDrain>),
Power(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, power<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
Power(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, power<sSkillResult_Heal_HP, doLeech>),
Power(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, power<sSkillResult_Buff, doBuff>)
};*/
}; // namespace