mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 06:50:04 +00:00
[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:
parent
e92a5a2f8b
commit
ae279100d7
2
Makefile
2
Makefile
@ -41,6 +41,7 @@ CXXSRC=\
|
|||||||
src/MissionManager.cpp\
|
src/MissionManager.cpp\
|
||||||
src/MobManager.cpp\
|
src/MobManager.cpp\
|
||||||
src/NanoManager.cpp\
|
src/NanoManager.cpp\
|
||||||
|
src/Abilities.cpp\
|
||||||
src/ItemManager.cpp\
|
src/ItemManager.cpp\
|
||||||
src/NPCManager.cpp\
|
src/NPCManager.cpp\
|
||||||
src/PlayerManager.cpp\
|
src/PlayerManager.cpp\
|
||||||
@ -83,6 +84,7 @@ CXXHDR=\
|
|||||||
src/MissionManager.hpp\
|
src/MissionManager.hpp\
|
||||||
src/MobManager.hpp\
|
src/MobManager.hpp\
|
||||||
src/NanoManager.hpp\
|
src/NanoManager.hpp\
|
||||||
|
src/Abilities.hpp\
|
||||||
src/ItemManager.hpp\
|
src/ItemManager.hpp\
|
||||||
src/NPCManager.hpp\
|
src/NPCManager.hpp\
|
||||||
src/Player.hpp\
|
src/Player.hpp\
|
||||||
|
772
src/Abilities.cpp
Normal file
772
src/Abilities.cpp
Normal 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
62
src/Abilities.hpp
Normal 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;
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include "PlayerManager.hpp"
|
#include "PlayerManager.hpp"
|
||||||
#include "GroupManager.hpp"
|
#include "GroupManager.hpp"
|
||||||
#include "NanoManager.hpp"
|
#include "NanoManager.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "NanoManager.hpp"
|
#include "NanoManager.hpp"
|
||||||
#include "NPCManager.hpp"
|
#include "NPCManager.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include <string.h> // for memset()
|
#include <string.h> // for memset()
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "GroupManager.hpp"
|
#include "GroupManager.hpp"
|
||||||
#include "TransportManager.hpp"
|
#include "TransportManager.hpp"
|
||||||
#include "RacingManager.hpp"
|
#include "RacingManager.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@ -1752,294 +1753,3 @@ void MobManager::enterCombat(CNSocket *sock, Mob *mob) {
|
|||||||
if (event.trigger == ON_COMBAT && event.npcType == mob->appearanceData.iNPCType)
|
if (event.trigger == ON_COMBAT && event.npcType == mob->appearanceData.iNPCType)
|
||||||
event.handler(sock, mob);
|
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
|
|
||||||
|
@ -122,24 +122,6 @@ struct Bullet {
|
|||||||
int bulletType;
|
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 {
|
namespace MobManager {
|
||||||
extern std::map<int32_t, Mob*> Mobs;
|
extern std::map<int32_t, Mob*> Mobs;
|
||||||
extern std::queue<int32_t> RemovalQueue;
|
extern std::queue<int32_t> RemovalQueue;
|
||||||
@ -147,7 +129,6 @@ namespace MobManager {
|
|||||||
extern std::map<int32_t, MobDrop> MobDrops;
|
extern std::map<int32_t, MobDrop> MobDrops;
|
||||||
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
|
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
|
||||||
extern bool simulateMobs;
|
extern bool simulateMobs;
|
||||||
extern std::vector<MobPower> MobPowers;
|
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void step(CNServer*, time_t);
|
void step(CNServer*, time_t);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "GroupManager.hpp"
|
#include "GroupManager.hpp"
|
||||||
#include "RacingManager.hpp"
|
#include "RacingManager.hpp"
|
||||||
#include "Vendor.hpp"
|
#include "Vendor.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
#include "MobManager.hpp"
|
#include "MobManager.hpp"
|
||||||
#include "MissionManager.hpp"
|
#include "MissionManager.hpp"
|
||||||
#include "GroupManager.hpp"
|
#include "GroupManager.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
std::map<int32_t, NanoData> NanoManager::NanoTable;
|
std::map<int32_t, NanoData> NanoManager::NanoTable;
|
||||||
std::map<int32_t, NanoTuning> NanoManager::NanoTunings;
|
std::map<int32_t, NanoTuning> NanoManager::NanoTunings;
|
||||||
std::map<int32_t, SkillData> NanoManager::SkillTable;
|
|
||||||
|
|
||||||
void NanoManager::init() {
|
void NanoManager::init() {
|
||||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
|
||||||
@ -112,7 +112,6 @@ void NanoManager::nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
summonNano(sock, pkt->iNanoSlotNum);
|
summonNano(sock, pkt->iNanoSlotNum);
|
||||||
|
|
||||||
// Send to client
|
|
||||||
DEBUGLOG(
|
DEBUGLOG(
|
||||||
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl;
|
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;
|
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
|
// 0=A 1=B 2=C -1=Not found
|
||||||
int NanoManager::nanoStyle(int nanoID) {
|
int NanoManager::nanoStyle(int nanoID) {
|
||||||
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
|
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
|
||||||
@ -514,58 +446,6 @@ int NanoManager::nanoStyle(int nanoID) {
|
|||||||
return NanoTable[nanoID].style;
|
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) {
|
bool NanoManager::getNanoBoost(Player* plr) {
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
if (plr->equippedNanos[i] == plr->activeNano)
|
if (plr->equippedNanos[i] == plr->activeNano)
|
||||||
@ -574,351 +454,3 @@ bool NanoManager::getNanoBoost(Player* plr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#pragma endregion
|
#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
|
|
||||||
|
@ -6,24 +6,6 @@
|
|||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "CNShardServer.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 {
|
struct NanoData {
|
||||||
int style;
|
int style;
|
||||||
};
|
};
|
||||||
@ -33,21 +15,9 @@ struct NanoTuning {
|
|||||||
int reqItems;
|
int reqItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkillData {
|
|
||||||
int skillType;
|
|
||||||
int targetType;
|
|
||||||
int drainType;
|
|
||||||
int effectArea;
|
|
||||||
int batteryUse[4];
|
|
||||||
int durationTime[4];
|
|
||||||
int powerIntensity[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace NanoManager {
|
namespace NanoManager {
|
||||||
extern std::vector<NanoPower> NanoPowers;
|
|
||||||
extern std::map<int32_t, NanoData> NanoTable;
|
extern std::map<int32_t, NanoData> NanoTable;
|
||||||
extern std::map<int32_t, NanoTuning> NanoTunings;
|
extern std::map<int32_t, NanoTuning> NanoTunings;
|
||||||
extern std::map<int32_t, SkillData> SkillTable;
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void nanoSummonHandler(CNSocket* sock, CNPacketData* data);
|
void nanoSummonHandler(CNSocket* sock, CNPacketData* data);
|
||||||
@ -66,8 +36,6 @@ namespace NanoManager {
|
|||||||
void summonNano(CNSocket* sock, int slot, bool silent = false);
|
void summonNano(CNSocket* sock, int slot, bool silent = false);
|
||||||
void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill);
|
void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill);
|
||||||
void resetNanoSkill(CNSocket* sock, int16_t nanoID);
|
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);
|
int nanoStyle(int nanoID);
|
||||||
std::vector<int> findTargets(Player* plr, int skillID, CNPacketData* data = nullptr);
|
std::vector<int> findTargets(Player* plr, int skillID, CNPacketData* data = nullptr);
|
||||||
bool getNanoBoost(Player* plr);
|
bool getNanoBoost(Player* plr);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "MobManager.hpp"
|
#include "MobManager.hpp"
|
||||||
#include "RacingManager.hpp"
|
#include "RacingManager.hpp"
|
||||||
#include "BuiltinCommands.hpp"
|
#include "BuiltinCommands.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "NanoManager.hpp"
|
#include "NanoManager.hpp"
|
||||||
#include "RacingManager.hpp"
|
#include "RacingManager.hpp"
|
||||||
#include "Vendor.hpp"
|
#include "Vendor.hpp"
|
||||||
|
#include "Abilities.hpp"
|
||||||
|
|
||||||
#include "JSON.hpp"
|
#include "JSON.hpp"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user