[refactor] Extract Abilities.cpp from {Nano,Mob}Manager.cpp

I've kept all the functions in their original namespaces for now, since
putting them all into the same one will cause collissions, and this is
all getting rewritten soon anyway.
This commit is contained in:
dongresource 2021-03-13 21:22:29 +01:00
parent e92a5a2f8b
commit ae279100d7
12 changed files with 844 additions and 812 deletions

View File

@ -41,6 +41,7 @@ CXXSRC=\
src/MissionManager.cpp\
src/MobManager.cpp\
src/NanoManager.cpp\
src/Abilities.cpp\
src/ItemManager.cpp\
src/NPCManager.cpp\
src/PlayerManager.cpp\
@ -83,6 +84,7 @@ CXXHDR=\
src/MissionManager.hpp\
src/MobManager.hpp\
src/NanoManager.hpp\
src/Abilities.hpp\
src/ItemManager.hpp\
src/NPCManager.hpp\
src/Player.hpp\

772
src/Abilities.cpp Normal file
View File

@ -0,0 +1,772 @@
#include "Abilities.hpp"
#include "PlayerManager.hpp"
#include "Player.hpp"
#include "NPCManager.hpp"
#include "NanoManager.hpp"
#include "GroupManager.hpp"
/*
* TODO: This file is in desperate need of deduplication and rewriting.
*/
std::map<int32_t, SkillData> NanoManager::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> NanoManager::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 NanoManager::nanoUnbuff(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 = GroupManager::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 NanoManager::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 : NanoPowers) {
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;
}
#pragma region Nano Powers
namespace NanoManager {
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
std::cout << "[WARN] doDebuff: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
MobManager::hitMob(sock, mob, 0);
respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].bProtected = 1;
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
&& !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
mob->appearanceData.iConditionBitFlag |= bitFlag;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doDamageNDebuff: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
MobManager::hitMob(sock, mob, 0); // just to gain aggro
respdata[i].eCT = 4;
respdata[i].iDamage = duration / 10;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
respdata[i].bProtected = 1;
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
&& !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
mob->appearanceData.iConditionBitFlag |= bitFlag;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doDamage: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
Player *plr = PlayerManager::getPlayer(sock);
int damage = MobManager::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->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doLeech: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
int damage = MobManager::hitMob(sock, mob, amount * 2);
damagedata->eCT = 4;
damagedata->iDamage = damage;
damagedata->iID = mob->appearanceData.iNPC_ID;
damagedata->iHP = mob->appearanceData.iHP;
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;
}
template<class sPAYLOAD,
bool (*work)(CNSocket*,sPAYLOAD*,int,int32_t,int32_t,int16_t,int16_t,int16_t)>
void nanoPower(CNSocket *sock, 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) {
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));
}
}
}
// nano power dispatch table
std::vector<NanoPower> NanoPowers = {
NanoPower(EST_STUN, CSB_BIT_STUN, ECSB_STUN, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Heal_HP, doHeal>),
NanoPower(EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, ECSB_BOUNDINGBALL, nanoPower<sSkillResult_Buff, doDebuff>),
NanoPower(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Damage, doDamage>),
NanoPower(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Heal_HP, doLeech>),
NanoPower(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_RECALL, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Move, doMove>),
NanoPower(EST_RECALL_GROUP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Move, doMove>),
NanoPower(EST_RETROROCKET_SELF, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PHOENIX_GROUP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Resurrect, doResurrect>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT1, ECSB_STIMPAKSLOT1, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT2, ECSB_STIMPAKSLOT2, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT3, ECSB_STIMPAKSLOT3, nanoPower<sSkillResult_Buff, doBuff>)
};
}; // namespace
#pragma endregion
#pragma region Mob Powers
namespace MobManager {
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;
NPCManager::EggBuffs[key] = until;
}
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
if (plr->HP <= 0) {
mob->target = nullptr;
mob->state = MobState::RETREAT;
if (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
std::cout << "[WARN] doDebuff: mob ID not found" << std::endl;
return false;
}
Mob* targetMob = MobManager::Mobs[targetID];
int healedAmount = amount * targetMob->maxHealth / 1000;
targetMob->appearanceData.iHP += healedAmount;
if (targetMob->appearanceData.iHP > targetMob->maxHealth)
targetMob->appearanceData.iHP = targetMob->maxHealth;
respdata[i].eCT = 4;
respdata[i].iID = targetMob->appearanceData.iNPC_ID;
respdata[i].iHP = targetMob->appearanceData.iHP;
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->appearanceData.iHP += healedAmount;
if (mob->appearanceData.iHP > mob->maxHealth)
mob->appearanceData.iHP = mob->maxHealth;
respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
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 (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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->appearanceData.iHP += healedAmount;
if (mob->appearanceData.iHP > mob->maxHealth)
mob->appearanceData.iHP = mob->maxHealth;
healdata->eCT = 4;
healdata->iID = mob->appearanceData.iNPC_ID;
healdata->iHP = mob->appearanceData.iHP;
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 (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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->appearanceData.iNPC_ID;
mob->appearanceData.iConditionBitFlag |= bitFlag;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
return true;
}
template<class sPAYLOAD,
bool (*work)(Mob*,sPAYLOAD*,int,int32_t,int32_t,int16_t,int16_t,int16_t)>
void mobPower(Mob *mob, std::vector<int> targetData,
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->appearanceData.iNPC_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<MobPower> MobPowers = {
MobPower(EST_STUN, CSB_BIT_STUN, ECSB_STUN, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doHeal>),
MobPower(EST_RETURNHOMEHEAL, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doReturnHeal>),
MobPower(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Damage, doDamage>),
MobPower(EST_BATTERYDRAIN, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_BatteryDrain, doBatteryDrain>),
MobPower(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doLeech>),
MobPower(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, mobPower<sSkillResult_Buff, doBuff>)
};
}; // namespace
#pragma endregion

62
src/Abilities.hpp Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include "CNProtocol.hpp"
#include "MobManager.hpp"
typedef void (*PowerHandler)(CNSocket*, std::vector<int>, int16_t, int16_t, int16_t, int16_t, int16_t, int32_t, int16_t);
struct NanoPower {
int16_t skillType;
int32_t bitFlag;
int16_t timeBuffID;
PowerHandler handler;
NanoPower(int16_t s, int32_t b, int16_t t, PowerHandler h) : skillType(s), bitFlag(b), timeBuffID(t), handler(h) {}
void handle(CNSocket *sock, std::vector<int> targetData, int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount) {
if (handler == nullptr)
return;
handler(sock, targetData, nanoID, skillID, duration, amount, skillType, bitFlag, timeBuffID);
}
};
typedef void (*MobPowerHandler)(Mob*, std::vector<int>, int16_t, int16_t, int16_t, int16_t, int32_t, int16_t);
struct MobPower {
int16_t skillType;
int32_t bitFlag;
int16_t timeBuffID;
MobPowerHandler handler;
MobPower(int16_t s, int32_t b, int16_t t, MobPowerHandler h) : skillType(s), bitFlag(b), timeBuffID(t), handler(h) {}
void handle(Mob *mob, std::vector<int> targetData, int16_t skillID, int16_t duration, int16_t amount) {
if (handler == nullptr)
return;
handler(mob, targetData, skillID, duration, amount, skillType, bitFlag, timeBuffID);
}
};
struct SkillData {
int skillType;
int targetType;
int drainType;
int effectArea;
int batteryUse[4];
int durationTime[4];
int powerIntensity[4];
};
namespace NanoManager {
extern std::vector<NanoPower> NanoPowers;
extern std::map<int32_t, SkillData> SkillTable;
void nanoUnbuff(CNSocket* sock, std::vector<int> 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);
}
namespace MobManager {
extern std::vector<MobPower> MobPowers;
}

View File

@ -3,6 +3,7 @@
#include "PlayerManager.hpp"
#include "GroupManager.hpp"
#include "NanoManager.hpp"
#include "Abilities.hpp"
#include <iostream>
#include <chrono>

View File

@ -5,6 +5,7 @@
#include "NanoManager.hpp"
#include "NPCManager.hpp"
#include "Player.hpp"
#include "Abilities.hpp"
#include <string.h> // for memset()
#include <assert.h>
@ -621,4 +622,4 @@ void ItemManager::init() {
// Bank
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler);
}
}

View File

@ -7,6 +7,7 @@
#include "GroupManager.hpp"
#include "TransportManager.hpp"
#include "RacingManager.hpp"
#include "Abilities.hpp"
#include <cmath>
#include <limits.h>
@ -1752,294 +1753,3 @@ void MobManager::enterCombat(CNSocket *sock, Mob *mob) {
if (event.trigger == ON_COMBAT && event.npcType == mob->appearanceData.iNPCType)
event.handler(sock, mob);
}
#pragma region Mob Powers
namespace MobManager {
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;
NPCManager::EggBuffs[key] = until;
}
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
if (plr->HP <= 0) {
mob->target = nullptr;
mob->state = MobState::RETREAT;
if (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
std::cout << "[WARN] doDebuff: mob ID not found" << std::endl;
return false;
}
Mob* targetMob = MobManager::Mobs[targetID];
int healedAmount = amount * targetMob->maxHealth / 1000;
targetMob->appearanceData.iHP += healedAmount;
if (targetMob->appearanceData.iHP > targetMob->maxHealth)
targetMob->appearanceData.iHP = targetMob->maxHealth;
respdata[i].eCT = 4;
respdata[i].iID = targetMob->appearanceData.iNPC_ID;
respdata[i].iHP = targetMob->appearanceData.iHP;
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->appearanceData.iHP += healedAmount;
if (mob->appearanceData.iHP > mob->maxHealth)
mob->appearanceData.iHP = mob->maxHealth;
respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
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 (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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->appearanceData.iHP += healedAmount;
if (mob->appearanceData.iHP > mob->maxHealth)
mob->appearanceData.iHP = mob->maxHealth;
healdata->eCT = 4;
healdata->iID = mob->appearanceData.iNPC_ID;
healdata->iHP = mob->appearanceData.iHP;
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 (!aggroCheck(mob, getTime())) {
clearDebuff(mob);
if (mob->groupLeader != 0)
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->appearanceData.iNPC_ID;
mob->appearanceData.iConditionBitFlag |= bitFlag;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
return true;
}
template<class sPAYLOAD,
bool (*work)(Mob*,sPAYLOAD*,int,int32_t,int32_t,int16_t,int16_t,int16_t)>
void mobPower(Mob *mob, std::vector<int> targetData,
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->appearanceData.iNPC_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<MobPower> MobPowers = {
MobPower(EST_STUN, CSB_BIT_STUN, ECSB_STUN, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doHeal>),
MobPower(EST_RETURNHOMEHEAL, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doReturnHeal>),
MobPower(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Damage, doDamage>),
MobPower(EST_BATTERYDRAIN, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_BatteryDrain, doBatteryDrain>),
MobPower(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, mobPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
MobPower(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, mobPower<sSkillResult_Heal_HP, doLeech>),
MobPower(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, mobPower<sSkillResult_Buff, doBuff>)
};
}; // namespace
#pragma endregion

View File

@ -122,24 +122,6 @@ struct Bullet {
int bulletType;
};
typedef void (*MobPowerHandler)(Mob*, std::vector<int>, int16_t, int16_t, int16_t, int16_t, int32_t, int16_t);
struct MobPower {
int16_t skillType;
int32_t bitFlag;
int16_t timeBuffID;
MobPowerHandler handler;
MobPower(int16_t s, int32_t b, int16_t t, MobPowerHandler h) : skillType(s), bitFlag(b), timeBuffID(t), handler(h) {}
void handle(Mob *mob, std::vector<int> targetData, int16_t skillID, int16_t duration, int16_t amount) {
if (handler == nullptr)
return;
handler(mob, targetData, skillID, duration, amount, skillType, bitFlag, timeBuffID);
}
};
namespace MobManager {
extern std::map<int32_t, Mob*> Mobs;
extern std::queue<int32_t> RemovalQueue;
@ -147,7 +129,6 @@ namespace MobManager {
extern std::map<int32_t, MobDrop> MobDrops;
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
extern bool simulateMobs;
extern std::vector<MobPower> MobPowers;
void init();
void step(CNServer*, time_t);

View File

@ -9,6 +9,7 @@
#include "GroupManager.hpp"
#include "RacingManager.hpp"
#include "Vendor.hpp"
#include "Abilities.hpp"
#include <cmath>
#include <algorithm>

View File

@ -6,12 +6,12 @@
#include "MobManager.hpp"
#include "MissionManager.hpp"
#include "GroupManager.hpp"
#include "Abilities.hpp"
#include <cmath>
std::map<int32_t, NanoData> NanoManager::NanoTable;
std::map<int32_t, NanoTuning> NanoManager::NanoTunings;
std::map<int32_t, SkillData> NanoManager::SkillTable;
void NanoManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
@ -112,7 +112,6 @@ void NanoManager::nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
summonNano(sock, pkt->iNanoSlotNum);
// Send to client
DEBUGLOG(
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl;
)
@ -440,73 +439,6 @@ void NanoManager::resetNanoSkill(CNSocket* sock, int16_t nanoID) {
plr->Nanos[nanoID] = nano;
}
void NanoManager::nanoUnbuff(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 = GroupManager::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 NanoManager::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 : NanoPowers) {
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;
}
// 0=A 1=B 2=C -1=Not found
int NanoManager::nanoStyle(int nanoID) {
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
@ -514,58 +446,6 @@ int NanoManager::nanoStyle(int nanoID) {
return NanoTable[nanoID].style;
}
/*
* 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> NanoManager::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;
}
bool NanoManager::getNanoBoost(Player* plr) {
for (int i = 0; i < 3; i++)
if (plr->equippedNanos[i] == plr->activeNano)
@ -574,351 +454,3 @@ bool NanoManager::getNanoBoost(Player* plr) {
return false;
}
#pragma endregion
#pragma region Nano Powers
namespace NanoManager {
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
std::cout << "[WARN] doDebuff: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
MobManager::hitMob(sock, mob, 0);
respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].bProtected = 1;
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
&& !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
mob->appearanceData.iConditionBitFlag |= bitFlag;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doDamageNDebuff: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
MobManager::hitMob(sock, mob, 0); // just to gain aggro
respdata[i].eCT = 4;
respdata[i].iDamage = duration / 10;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
respdata[i].bProtected = 1;
if (mob->skillStyle < 0 && mob->state != MobState::RETREAT
&& !(mob->appearanceData.iConditionBitFlag & CSB_BIT_FREEDOM)) { // only debuff if the enemy is not retreating, casting corruption or in freedom
mob->appearanceData.iConditionBitFlag |= bitFlag;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doDamage: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
Player *plr = PlayerManager::getPlayer(sock);
int damage = MobManager::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->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
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 (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
// not sure how to best handle this
std::cout << "[WARN] doLeech: mob ID not found" << std::endl;
return false;
}
Mob* mob = MobManager::Mobs[targetID];
int damage = MobManager::hitMob(sock, mob, amount * 2);
damagedata->eCT = 4;
damagedata->iDamage = damage;
damagedata->iID = mob->appearanceData.iNPC_ID;
damagedata->iHP = mob->appearanceData.iHP;
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;
}
template<class sPAYLOAD,
bool (*work)(CNSocket*,sPAYLOAD*,int,int32_t,int32_t,int16_t,int16_t,int16_t)>
void nanoPower(CNSocket *sock, 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) {
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));
}
}
}
// nano power dispatch table
std::vector<NanoPower> NanoPowers = {
NanoPower(EST_STUN, CSB_BIT_STUN, ECSB_STUN, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Heal_HP, doHeal>),
NanoPower(EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, ECSB_BOUNDINGBALL, nanoPower<sSkillResult_Buff, doDebuff>),
NanoPower(EST_SNARE, CSB_BIT_DN_MOVE_SPEED, ECSB_DN_MOVE_SPEED, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_DAMAGE, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Damage, doDamage>),
NanoPower(EST_BLOODSUCKING, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Heal_HP, doLeech>),
NanoPower(EST_SLEEP, CSB_BIT_MEZ, ECSB_MEZ, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
NanoPower(EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_RECALL, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Move, doMove>),
NanoPower(EST_RECALL_GROUP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Move, doMove>),
NanoPower(EST_RETROROCKET_SELF, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_PHOENIX_GROUP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Resurrect, doResurrect>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT1, ECSB_STIMPAKSLOT1, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT2, ECSB_STIMPAKSLOT2, nanoPower<sSkillResult_Buff, doBuff>),
NanoPower(EST_NANOSTIMPAK, CSB_BIT_STIMPAKSLOT3, ECSB_STIMPAKSLOT3, nanoPower<sSkillResult_Buff, doBuff>)
};
}; // namespace
#pragma endregion

View File

@ -6,24 +6,6 @@
#include "Player.hpp"
#include "CNShardServer.hpp"
typedef void (*PowerHandler)(CNSocket*, std::vector<int>, int16_t, int16_t, int16_t, int16_t, int16_t, int32_t, int16_t);
struct NanoPower {
int16_t skillType;
int32_t bitFlag;
int16_t timeBuffID;
PowerHandler handler;
NanoPower(int16_t s, int32_t b, int16_t t, PowerHandler h) : skillType(s), bitFlag(b), timeBuffID(t), handler(h) {}
void handle(CNSocket *sock, std::vector<int> targetData, int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount) {
if (handler == nullptr)
return;
handler(sock, targetData, nanoID, skillID, duration, amount, skillType, bitFlag, timeBuffID);
}
};
struct NanoData {
int style;
};
@ -33,21 +15,9 @@ struct NanoTuning {
int reqItems;
};
struct SkillData {
int skillType;
int targetType;
int drainType;
int effectArea;
int batteryUse[4];
int durationTime[4];
int powerIntensity[4];
};
namespace NanoManager {
extern std::vector<NanoPower> NanoPowers;
extern std::map<int32_t, NanoData> NanoTable;
extern std::map<int32_t, NanoTuning> NanoTunings;
extern std::map<int32_t, SkillData> SkillTable;
void init();
void nanoSummonHandler(CNSocket* sock, CNPacketData* data);
@ -66,8 +36,6 @@ namespace NanoManager {
void summonNano(CNSocket* sock, int slot, bool silent = false);
void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill);
void resetNanoSkill(CNSocket* sock, int16_t nanoID);
void nanoUnbuff(CNSocket* sock, std::vector<int> 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);
int nanoStyle(int nanoID);
std::vector<int> findTargets(Player* plr, int skillID, CNPacketData* data = nullptr);
bool getNanoBoost(Player* plr);

View File

@ -11,6 +11,7 @@
#include "MobManager.hpp"
#include "RacingManager.hpp"
#include "BuiltinCommands.hpp"
#include "Abilities.hpp"
#include "settings.hpp"

View File

@ -9,6 +9,7 @@
#include "NanoManager.hpp"
#include "RacingManager.hpp"
#include "Vendor.hpp"
#include "Abilities.hpp"
#include "JSON.hpp"