2020-08-20 15:43:37 +00:00
|
|
|
#include "CNShardServer.hpp"
|
|
|
|
#include "CNStructs.hpp"
|
|
|
|
#include "NanoManager.hpp"
|
|
|
|
#include "PlayerManager.hpp"
|
2020-09-18 07:10:30 +00:00
|
|
|
#include "NPCManager.hpp"
|
2020-09-18 16:37:26 +00:00
|
|
|
#include "MobManager.hpp"
|
2020-09-26 01:48:45 +00:00
|
|
|
#include "MissionManager.hpp"
|
2020-10-09 00:01:35 +00:00
|
|
|
#include "GroupManager.hpp"
|
2020-08-20 15:43:37 +00:00
|
|
|
|
2020-09-25 21:05:36 +00:00
|
|
|
std::map<int32_t, NanoData> NanoManager::NanoTable;
|
2020-10-24 20:28:35 +00:00
|
|
|
std::map<int32_t, NanoTuning> NanoManager::NanoTunings;
|
2020-11-07 23:22:48 +00:00
|
|
|
std::map<int32_t, SkillData> NanoManager::SkillTable;
|
2020-09-25 21:05:36 +00:00
|
|
|
|
2020-08-20 15:43:37 +00:00
|
|
|
void NanoManager::init() {
|
2020-08-21 00:37:34 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
|
2020-08-22 18:02:08 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_EQUIP, nanoEquipHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_UNEQUIP, nanoUnEquipHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO, nanoGMGiveHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_TUNE, nanoSkillSetHandler);
|
2020-09-09 01:15:25 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO_SKILL, nanoSkillSetGMHandler);
|
2020-08-22 18:02:08 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_SKILL_USE, nanoSkillUseHandler);
|
2020-11-08 00:26:44 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_RXCOM, nanoRecallRegisterHandler);
|
2020-09-18 07:10:30 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_WARP_USE_RECALL, nanoRecallHandler);
|
2020-09-25 05:35:27 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_CHARGE_NANO_STAMINA, nanoPotionHandler);
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoEquipHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_NANO_EQUIP))
|
|
|
|
return; // malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_NANO_EQUIP* nano = (sP_CL2FE_REQ_NANO_EQUIP*)data->buf;
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_EQUIP_SUCC, resp);
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
// sanity checks
|
2020-11-23 22:03:08 +00:00
|
|
|
if (plr == nullptr || nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0 || nano->iNanoID > 36)
|
2020-08-23 03:15:27 +00:00
|
|
|
return;
|
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.iNanoID = nano->iNanoID;
|
|
|
|
resp.iNanoSlotNum = nano->iNanoSlotNum;
|
|
|
|
|
2020-08-23 00:52:54 +00:00
|
|
|
// Update player
|
2020-08-24 22:02:07 +00:00
|
|
|
plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID;
|
2020-08-23 00:52:54 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// Unbuff gumballs
|
|
|
|
int value1 = CSB_BIT_STIMPAKSLOT1 << nano->iNanoSlotNum;
|
|
|
|
if (plr->iConditionBitFlag & value1) {
|
|
|
|
int value2 = ECSB_STIMPAKSLOT1 + nano->iNanoSlotNum;
|
|
|
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt);
|
|
|
|
pkt.eCSTB = value2; // eCharStatusTimeBuffID
|
|
|
|
pkt.eTBU = 2; // eTimeBuffUpdate
|
|
|
|
pkt.eTBT = 1; // eTimeBuffType 1 means nano
|
|
|
|
pkt.iConditionBitFlag = plr->iConditionBitFlag &= ~value1;
|
|
|
|
sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
|
|
|
}
|
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
// unsummon nano if replaced
|
|
|
|
if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum])
|
|
|
|
summonNano(sock, -1);
|
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_EQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_EQUIP_SUCC));
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_NANO_UNEQUIP))
|
|
|
|
return; // malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_NANO_UNEQUIP* nano = (sP_CL2FE_REQ_NANO_UNEQUIP*)data->buf;
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_UNEQUIP_SUCC, resp);
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-08-22 23:31:09 +00:00
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
// sanity check
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr || nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
|
2020-08-23 03:15:27 +00:00
|
|
|
return;
|
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.iNanoSlotNum = nano->iNanoSlotNum;
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
// unsummon nano if removed
|
|
|
|
if (plr->equippedNanos[nano->iNanoSlotNum] == plr->activeNano)
|
|
|
|
summonNano(sock, -1);
|
|
|
|
|
2020-08-23 00:52:54 +00:00
|
|
|
// update player
|
2020-08-24 22:02:07 +00:00
|
|
|
plr->equippedNanos[nano->iNanoSlotNum] = 0;
|
2020-08-23 00:52:54 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_UNEQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_UNEQUIP_SUCC));
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoGMGiveHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// Cmd: /nano <nanoID>
|
2020-08-22 18:02:08 +00:00
|
|
|
sP_CL2FE_REQ_PC_GIVE_NANO* nano = (sP_CL2FE_REQ_PC_GIVE_NANO*)data->buf;
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-08-22 18:02:08 +00:00
|
|
|
// Add nano to player
|
|
|
|
addNano(sock, nano->iNanoID, 0);
|
|
|
|
|
|
|
|
DEBUGLOG(
|
2020-10-31 20:31:25 +00:00
|
|
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to add nano id: " << nano->iNanoID << std::endl;
|
2020-08-22 18:02:08 +00:00
|
|
|
)
|
2020-08-20 15:43:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
|
2020-08-21 00:37:34 +00:00
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_NANO_ACTIVE))
|
2020-08-20 23:50:30 +00:00
|
|
|
return; // malformed packet
|
|
|
|
|
2020-08-23 00:52:54 +00:00
|
|
|
sP_CL2FE_REQ_NANO_ACTIVE* pkt = (sP_CL2FE_REQ_NANO_ACTIVE*)data->buf;
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-08-20 15:43:37 +00:00
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
summonNano(sock, pkt->iNanoSlotNum);
|
2020-08-23 00:52:54 +00:00
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
// Send to client
|
2020-08-22 18:02:08 +00:00
|
|
|
DEBUGLOG(
|
2020-10-31 20:31:25 +00:00
|
|
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl;
|
2020-08-22 18:02:08 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-09-28 18:11:13 +00:00
|
|
|
|
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
int16_t nanoID = plr->activeNano;
|
|
|
|
int16_t skillID = plr->Nanos[nanoID].iSkillID;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-10-04 23:54:08 +00:00
|
|
|
DEBUGLOG(
|
2020-10-31 20:31:25 +00:00
|
|
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
2020-10-04 23:54:08 +00:00
|
|
|
)
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
int *targetData = findTargets(plr, skillID, data);
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
int boost = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
if (plr->equippedNanos[i] == plr->activeNano)
|
|
|
|
if (plr->iConditionBitFlag & (CSB_BIT_STIMPAKSLOT1 << i))
|
|
|
|
boost = 1;
|
|
|
|
|
|
|
|
plr->Nanos[plr->activeNano].iStamina -= SkillTable[skillID].batteryUse[boost*3];
|
|
|
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
|
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (auto& pwr : NanoPowers)
|
2020-11-08 00:26:44 +00:00
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType)
|
2020-11-12 00:59:52 +00:00
|
|
|
pwr.handle(sock, targetData, nanoID, skillID, SkillTable[skillID].durationTime[boost], SkillTable[skillID].powerIntensity[boost]);
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_NANO_TUNE))
|
|
|
|
return; // malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_NANO_TUNE* skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
|
2020-10-24 20:28:35 +00:00
|
|
|
setNanoSkill(sock, skill);
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 01:15:25 +00:00
|
|
|
void NanoManager::nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO_SKILL))
|
|
|
|
return; // malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_NANO_TUNE* skillGM = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
|
2020-10-24 20:28:35 +00:00
|
|
|
setNanoSkill(sock, skillGM);
|
2020-09-09 01:15:25 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
void NanoManager::nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_REGIST_RXCOM))
|
|
|
|
return;
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_REGIST_RXCOM* recallData = (sP_CL2FE_REQ_REGIST_RXCOM*)data->buf;
|
|
|
|
|
|
|
|
if (NPCManager::NPCs.find(recallData->iNPCID) == NPCManager::NPCs.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
BaseNPC *npc = NPCManager::NPCs[recallData->iNPCID];
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_REGIST_RXCOM, response);
|
|
|
|
response.iMapNum = plr->recallInstance = (int32_t)npc->instanceID; // Never going to recall into a Fusion Lair
|
|
|
|
response.iX = plr->recallX = npc->appearanceData.iX;
|
|
|
|
response.iY = plr->recallY = npc->appearanceData.iY;
|
|
|
|
response.iZ = plr->recallZ = npc->appearanceData.iZ;
|
|
|
|
sock->sendPacket((void*)&response, P_FE2CL_REP_REGIST_RXCOM, sizeof(sP_FE2CL_REP_REGIST_RXCOM));
|
|
|
|
}
|
|
|
|
|
2020-09-18 07:10:30 +00:00
|
|
|
void NanoManager::nanoRecallHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_WARP_USE_RECALL))
|
|
|
|
return;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
sP_CL2FE_REQ_WARP_USE_RECALL* recallData = (sP_CL2FE_REQ_WARP_USE_RECALL*)data->buf;
|
2020-09-18 07:10:30 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
Player* otherPlr = PlayerManager::getPlayerFromID(recallData->iGroupMemberID);
|
|
|
|
|
|
|
|
if ((int32_t)plr->instanceID == otherPlr->recallInstance)
|
|
|
|
PlayerManager::sendPlayerTo(sock, otherPlr->recallX, otherPlr->recallY, otherPlr->recallZ, otherPlr->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));
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-25 05:35:27 +00:00
|
|
|
void NanoManager::nanoPotionHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_CHARGE_NANO_STAMINA))
|
|
|
|
return;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-25 05:35:27 +00:00
|
|
|
Player* player = PlayerManager::getPlayer(sock);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
// sanity checks
|
|
|
|
if (player == nullptr || player->activeNano == -1 || player->batteryN == 0)
|
2020-09-25 05:35:27 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
sNano nano = player->Nanos[player->activeNano];
|
|
|
|
int difference = 150 - nano.iStamina;
|
|
|
|
if (player->batteryN < difference)
|
|
|
|
difference = player->batteryN;
|
|
|
|
|
|
|
|
if (difference == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_CHARGE_NANO_STAMINA, response);
|
|
|
|
response.iNanoID = nano.iID;
|
|
|
|
response.iNanoStamina = nano.iStamina + difference;
|
|
|
|
response.iBatteryN = player->batteryN - difference;
|
|
|
|
|
|
|
|
sock->sendPacket((void*)&response, P_FE2CL_REP_CHARGE_NANO_STAMINA, sizeof(sP_FE2CL_REP_CHARGE_NANO_STAMINA));
|
2020-10-19 17:26:14 +00:00
|
|
|
// now update serverside
|
2020-09-25 05:35:27 +00:00
|
|
|
player->batteryN -= difference;
|
|
|
|
player->Nanos[nano.iID].iStamina += difference;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-22 18:02:08 +00:00
|
|
|
#pragma region Helper methods
|
2020-11-08 00:26:44 +00:00
|
|
|
void NanoManager::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm) {
|
|
|
|
if (nanoID > 36)
|
2020-08-23 03:15:27 +00:00
|
|
|
return;
|
|
|
|
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
int level = nanoID < plr->level ? plr->level : nanoID;
|
2020-09-12 20:43:04 +00:00
|
|
|
|
2020-09-26 01:48:45 +00:00
|
|
|
/*
|
|
|
|
* Spend the necessary Fusion Matter.
|
|
|
|
* Note the use of the not-yet-incremented plr->level as opposed to level.
|
|
|
|
* Doing it the other way always leaves the FM at 0. Jade totally called it.
|
|
|
|
*/
|
2020-09-29 21:27:48 +00:00
|
|
|
plr->level = level;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-26 01:48:45 +00:00
|
|
|
if (spendfm)
|
2020-09-29 21:27:48 +00:00
|
|
|
MissionManager::updateFusionMatter(sock, -(int)MissionManager::AvatarGrowth[plr->level-1]["m_iReqBlob_NanoCreate"]);
|
2020-09-26 01:48:45 +00:00
|
|
|
|
2020-08-22 18:02:08 +00:00
|
|
|
// Send to client
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_NANO_CREATE_SUCC, resp);
|
2020-11-08 00:26:44 +00:00
|
|
|
resp.Nano.iID = nanoID;
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.Nano.iStamina = 150;
|
|
|
|
resp.iQuestItemSlotNum = slot;
|
2020-09-12 20:43:04 +00:00
|
|
|
resp.iPC_Level = level;
|
2020-09-26 01:48:45 +00:00
|
|
|
resp.iPC_FusionMatter = plr->fusionmatter;
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (plr->activeNano > 0 && plr->activeNano == nanoID)
|
2020-09-29 21:27:48 +00:00
|
|
|
summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-08-22 18:02:08 +00:00
|
|
|
// Update player
|
2020-11-08 00:26:44 +00:00
|
|
|
plr->Nanos[nanoID] = resp.Nano;
|
2020-09-12 20:43:04 +00:00
|
|
|
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_NANO_CREATE_SUCC, sizeof(sP_FE2CL_REP_PC_NANO_CREATE_SUCC));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iPC_Level in NANO_CREATE_SUCC sets the player's level.
|
|
|
|
* Other players must be notified of the change as well. Both P_FE2CL_REP_PC_NANO_CREATE and
|
|
|
|
* P_FE2CL_REP_PC_CHANGE_LEVEL appear to play the same animation, but only the latter affects
|
|
|
|
* the other player's displayed level.
|
|
|
|
*/
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-10 16:51:52 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_CHANGE_LEVEL, resp2);
|
|
|
|
|
|
|
|
resp2.iPC_ID = plr->iID;
|
2020-09-12 20:43:04 +00:00
|
|
|
resp2.iPC_Level = level;
|
2020-09-10 16:51:52 +00:00
|
|
|
|
2020-09-12 20:43:04 +00:00
|
|
|
// Update other players' perception of the player's level
|
2020-09-17 22:45:43 +00:00
|
|
|
PlayerManager::sendToViewable(sock, (void*)&resp2, P_FE2CL_REP_PC_CHANGE_LEVEL, sizeof(sP_FE2CL_REP_PC_CHANGE_LEVEL));
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
void NanoManager::summonNano(CNSocket *sock, int slot) {
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
|
|
|
|
resp.iActiveNanoSlotNum = slot;
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr || slot > 2 || slot < -1)
|
2020-09-12 18:21:36 +00:00
|
|
|
return; // sanity check
|
2020-08-25 18:30:20 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
plr->nanoDrainRate = 0;
|
|
|
|
int16_t skillID = plr->Nanos[plr->activeNano].iSkillID;
|
2020-08-25 18:30:20 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// passive nano unbuffing
|
|
|
|
if (SkillTable[skillID].drainType == 2) {
|
2020-11-12 00:59:52 +00:00
|
|
|
int *targetData = findTargets(plr, skillID);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
int boost = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
if (plr->equippedNanos[i] == plr->activeNano)
|
|
|
|
if (plr->iConditionBitFlag & (CSB_BIT_STIMPAKSLOT1 << i))
|
|
|
|
boost = 1;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (auto& pwr : NanoPowers)
|
2020-11-08 00:26:44 +00:00
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType)
|
|
|
|
nanoUnbuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, SkillTable[skillID].powerIntensity[boost],(SkillTable[skillID].targetType == 3));
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
int16_t nanoID = slot == -1 ? 0 : plr->equippedNanos[slot];
|
|
|
|
|
|
|
|
if (nanoID > 36 || nanoID < 0)
|
|
|
|
return; // sanity check
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
plr->activeNano = nanoID;
|
|
|
|
sNano nano = plr->Nanos[nanoID];
|
|
|
|
skillID = nano.iSkillID;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// passive nano buffing
|
|
|
|
if (SkillTable[skillID].drainType == 2) {
|
2020-11-12 00:59:52 +00:00
|
|
|
int *targetData = findTargets(plr, skillID);
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
int boost = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
if (plr->equippedNanos[i] == plr->activeNano)
|
|
|
|
if (plr->iConditionBitFlag & (CSB_BIT_STIMPAKSLOT1 << i))
|
|
|
|
boost = 1;
|
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (auto& pwr : NanoPowers) {
|
2020-11-08 00:26:44 +00:00
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType) {
|
|
|
|
resp.eCSTB___Add = 1; // the part that makes nano go ZOOMAZOOM
|
|
|
|
plr->nanoDrainRate = SkillTable[skillID].batteryUse[boost*3];
|
2020-11-12 00:59:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
pwr.handle(sock, targetData, nanoID, skillID, 0, SkillTable[skillID].powerIntensity[boost]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-18 07:10:30 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_ACTIVE_SUCC, sizeof(sP_FE2CL_REP_NANO_ACTIVE_SUCC));
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-08-25 18:30:20 +00:00
|
|
|
// Send to other players
|
|
|
|
INITSTRUCT(sP_FE2CL_NANO_ACTIVE, pkt1);
|
|
|
|
pkt1.iPC_ID = plr->iID;
|
2020-11-08 00:26:44 +00:00
|
|
|
pkt1.Nano = plr->Nanos[nanoID];
|
2020-09-17 22:45:43 +00:00
|
|
|
PlayerManager::sendToViewable(sock, (void*)&pkt1, P_FE2CL_NANO_ACTIVE, sizeof(sP_FE2CL_NANO_ACTIVE));
|
2020-08-25 18:30:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-24 20:28:35 +00:00
|
|
|
void NanoManager::setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) {
|
|
|
|
if (skill->iNanoID > 36)
|
2020-08-23 03:15:27 +00:00
|
|
|
return;
|
|
|
|
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-09-28 18:11:13 +00:00
|
|
|
|
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-10-24 20:28:35 +00:00
|
|
|
if (plr->activeNano > 0 && plr->activeNano == skill->iNanoID)
|
2020-09-29 21:27:48 +00:00
|
|
|
summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-10-24 20:28:35 +00:00
|
|
|
sNano nano = plr->Nanos[skill->iNanoID];
|
|
|
|
nano.iSkillID = skill->iTuneID;
|
|
|
|
plr->Nanos[skill->iNanoID] = nano;
|
2020-08-22 18:02:08 +00:00
|
|
|
|
|
|
|
// Send to client
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_TUNE_SUCC, resp);
|
2020-10-24 20:28:35 +00:00
|
|
|
resp.iNanoID = skill->iNanoID;
|
|
|
|
resp.iSkillID = skill->iTuneID;
|
2020-09-26 01:48:45 +00:00
|
|
|
resp.iPC_FusionMatter = plr->fusionmatter;
|
2020-10-24 20:28:35 +00:00
|
|
|
resp.aItem[9] = plr->Inven[0]; // quick fix to make sure item in slot 0 doesn't get yeeted by default
|
|
|
|
|
|
|
|
|
|
|
|
// check if there's any garbage in the item slot array (this'll happen when a nano station isn't used)
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
if (skill->aiNeedItemSlotNum[i] < 0 || skill->aiNeedItemSlotNum[i] >= AINVEN_COUNT) {
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
|
|
|
|
return; // stop execution, don't run consumption logic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plr->fusionmatter < (int)MissionManager::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"]) // sanity check
|
|
|
|
return;
|
|
|
|
|
|
|
|
plr->fusionmatter -= (int)MissionManager::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"];
|
|
|
|
|
|
|
|
int reqItemCount = NanoTunings[skill->iTuneID].reqItemCount;
|
|
|
|
int reqItemID = NanoTunings[skill->iTuneID].reqItems;
|
|
|
|
int i = 0;
|
|
|
|
while (reqItemCount > 0 && i < 10) {
|
|
|
|
|
|
|
|
sItemBase& item = plr->Inven[skill->aiNeedItemSlotNum[i]];
|
|
|
|
if (item.iType == 7 && item.iID == reqItemID) {
|
|
|
|
if (item.iOpt > reqItemCount) {
|
|
|
|
item.iOpt -= reqItemCount;
|
|
|
|
reqItemCount = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
reqItemCount -= item.iOpt;
|
|
|
|
item.iID = 0;
|
|
|
|
item.iType = 0;
|
|
|
|
item.iOpt = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++; // next slot
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.iPC_FusionMatter = plr->fusionmatter; // update fusion matter in packet
|
|
|
|
// update items clientside
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
if (skill->aiNeedItemSlotNum[i]) { // non-zero check
|
|
|
|
resp.aItem[i] = plr->Inven[skill->aiNeedItemSlotNum[i]];
|
|
|
|
resp.aiItemSlotNum[i] = skill->aiNeedItemSlotNum[i];
|
|
|
|
}
|
|
|
|
}
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
|
2020-08-22 18:02:08 +00:00
|
|
|
|
|
|
|
DEBUGLOG(
|
2020-10-31 20:31:25 +00:00
|
|
|
std::cout << PlayerManager::getPlayerName(plr) << " set skill id " << skill->iTuneID << " for nano: " << skill->iNanoID << std::endl;
|
2020-08-22 18:02:08 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
void NanoManager::resetNanoSkill(CNSocket* sock, int16_t nanoID) {
|
|
|
|
if (nanoID > 36)
|
2020-08-23 03:15:27 +00:00
|
|
|
return;
|
2020-08-24 21:04:56 +00:00
|
|
|
|
2020-08-24 22:02:07 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-09-28 18:11:13 +00:00
|
|
|
|
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
sNano nano = plr->Nanos[nanoID];
|
2020-08-22 18:02:08 +00:00
|
|
|
|
2020-08-24 21:04:56 +00:00
|
|
|
// 0 is reset
|
2020-08-22 18:02:08 +00:00
|
|
|
nano.iSkillID = 0;
|
2020-11-08 00:26:44 +00:00
|
|
|
plr->Nanos[nanoID] = nano;
|
2020-08-21 00:37:34 +00:00
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
void NanoManager::nanoUnbuff(CNSocket* sock, int targetData[], int32_t bitFlag, int16_t timeBuffID, int16_t amount, bool groupPower) {
|
2020-11-08 00:26:44 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-09-18 07:10:30 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plr->iSelfConditionBitFlag &= ~bitFlag;
|
|
|
|
int groupFlags = 0;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (groupPower) {
|
|
|
|
plr->iGroupConditionBitFlag &= ~bitFlag;
|
|
|
|
Player *leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
|
|
|
if (leader != nullptr)
|
|
|
|
groupFlags = GroupManager::getGroupFlags(leader);
|
|
|
|
}
|
2020-09-18 16:37:26 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (int i = 0; i < targetData[0]; i++) {
|
|
|
|
Player* varPlr = PlayerManager::getPlayerFromID(targetData[i+1]);
|
2020-11-08 00:26:44 +00:00
|
|
|
if (!((groupFlags | varPlr->iSelfConditionBitFlag) & bitFlag)) {
|
2020-11-12 00:59:52 +00:00
|
|
|
CNSocket* sockTo = PlayerManager::getSockFromID(targetData[i+1]);
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp);
|
|
|
|
resp.eCSTB = timeBuffID; // eCharStatusTimeBuffID
|
|
|
|
resp.eTBU = 2; // eTimeBuffUpdate
|
|
|
|
resp.eTBT = 1; // eTimeBuffType 1 means nano
|
2020-11-13 23:06:18 +00:00
|
|
|
varPlr->iConditionBitFlag &= ~bitFlag;
|
|
|
|
resp.iConditionBitFlag = varPlr->iConditionBitFlag |= groupFlags | varPlr->iSelfConditionBitFlag;
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
if (amount > 0)
|
|
|
|
resp.TimeBuff.iValue = amount;
|
|
|
|
|
|
|
|
sockTo->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
|
|
|
}
|
|
|
|
}
|
2020-09-18 16:37:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 23:06:18 +00:00
|
|
|
bool NanoManager::applyBuff(CNSocket* sock, int skillID, int eTBU, int eTBT, int32_t groupFlags) {
|
2020-11-13 03:20:51 +00:00
|
|
|
if (SkillTable[skillID].drainType == 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int32_t bitFlag = 0;
|
2020-11-08 00:26:44 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (auto& pwr : NanoPowers) {
|
2020-11-08 00:26:44 +00:00
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType) {
|
2020-11-13 03:20:51 +00:00
|
|
|
bitFlag = pwr.bitFlag;
|
2020-11-08 00:26:44 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-11-13 03:20:51 +00:00
|
|
|
if (eTBU == 1 || !((groupFlags | plr->iSelfConditionBitFlag) & bitFlag)) {
|
2020-11-08 00:26:44 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp);
|
|
|
|
resp.eCSTB = pwr.timeBuffID;
|
|
|
|
resp.eTBU = eTBU;
|
|
|
|
resp.eTBT = eTBT;
|
2020-11-13 23:06:18 +00:00
|
|
|
|
|
|
|
if (skillID == 191) { // dealing with gumballs
|
|
|
|
resp.eCSTB = pwr.timeBuffID + 1;
|
|
|
|
bitFlag = bitFlag << 1;
|
|
|
|
} else if (skillID == 197) {
|
|
|
|
resp.eCSTB = pwr.timeBuffID + 2;
|
|
|
|
bitFlag = bitFlag << 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eTBU == 1)
|
|
|
|
plr->iConditionBitFlag |= bitFlag;
|
|
|
|
else
|
|
|
|
plr->iConditionBitFlag &= ~bitFlag;
|
|
|
|
|
|
|
|
resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag;
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
resp.TimeBuff.iValue = SkillTable[skillID].powerIntensity[0];
|
|
|
|
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
|
|
|
}
|
2020-11-13 03:20:51 +00:00
|
|
|
return true;
|
2020-11-08 00:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-13 03:20:51 +00:00
|
|
|
|
|
|
|
return false;
|
2020-11-08 00:26:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 0=A 1=B 2=C -1=Not found
|
|
|
|
int NanoManager::nanoStyle(int nanoID) {
|
|
|
|
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
|
|
|
|
return -1;
|
|
|
|
return NanoTable[nanoID].style;
|
|
|
|
}
|
2020-11-12 00:59:52 +00:00
|
|
|
|
|
|
|
int* NanoManager::findTargets(Player* plr, int skillID, CNPacketData* data) {
|
|
|
|
static int tD[5] = {0, 0, 0, 0, 0};
|
|
|
|
tD[0] = 0;
|
|
|
|
|
2020-11-13 03:20:51 +00:00
|
|
|
if (SkillTable[skillID].targetType <= 2 && data != nullptr) { // client gives us the targets
|
2020-11-12 00:59:52 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
tD[0] = otherPlr->groupCnt;
|
|
|
|
for (int i = 0; i < otherPlr->groupCnt; i++)
|
|
|
|
tD[i+1] = otherPlr->groupIDs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return tD;
|
|
|
|
}
|
2020-11-08 00:26:44 +00:00
|
|
|
#pragma endregion
|
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
#pragma region Nano Powers
|
2020-11-08 00:26:44 +00:00
|
|
|
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;
|
2020-09-18 16:37:26 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
Mob* mob = MobManager::Mobs[targetID];
|
2020-10-17 17:26:09 +00:00
|
|
|
MobManager::hitMob(sock, mob, 0);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-18 16:37:26 +00:00
|
|
|
respdata[i].eCT = 4;
|
|
|
|
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
2020-11-08 00:26:44 +00:00
|
|
|
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag;
|
|
|
|
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-09-18 16:37:26 +00:00
|
|
|
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
bool doBuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) {
|
2020-09-15 14:46:52 +00:00
|
|
|
Player *plr = nullptr;
|
|
|
|
|
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-11-17 23:16:16 +00:00
|
|
|
if (pair.second->iID == pktdata[i]) {
|
|
|
|
plr = pair.second;
|
2020-09-15 14:46:52 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-09-21 16:03:25 +00:00
|
|
|
// player not found
|
2020-11-08 00:26:44 +00:00
|
|
|
if (plr == nullptr) {
|
|
|
|
std::cout << "[WARN] doBuff: player ID not found" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
return false;
|
2020-11-08 00:26:44 +00:00
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-15 14:46:52 +00:00
|
|
|
respdata[i].eCT = 1;
|
|
|
|
respdata[i].iID = plr->iID;
|
2020-11-08 00:26:44 +00:00
|
|
|
respdata[i].iConditionBitFlag = bitFlag;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt);
|
|
|
|
pkt.eCSTB = timeBuffID; // eCharStatusTimeBuffID
|
|
|
|
pkt.eTBU = 1; // eTimeBuffUpdate
|
|
|
|
pkt.eTBT = 1; // eTimeBuffType 1 means nano
|
2020-11-12 00:59:52 +00:00
|
|
|
pkt.iConditionBitFlag = plr->iConditionBitFlag |= bitFlag;
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
if (amount > 0)
|
|
|
|
pkt.TimeBuff.iValue = amount;
|
|
|
|
|
|
|
|
sock->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];
|
|
|
|
|
|
|
|
int damage = MobManager::hitMob(sock, mob, 0); // using amount for something else
|
|
|
|
|
|
|
|
respdata[i].eCT = 4;
|
|
|
|
respdata[i].iDamage = damage;
|
|
|
|
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
|
|
|
respdata[i].iHP = mob->appearanceData.iHP;
|
|
|
|
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag;
|
|
|
|
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
|
|
|
|
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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) {
|
2020-10-04 23:54:08 +00:00
|
|
|
Player *plr = nullptr;
|
|
|
|
|
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-11-17 23:16:16 +00:00
|
|
|
if (pair.second->iID == pktdata[0]) {
|
|
|
|
plr = pair.second;
|
2020-10-04 23:54:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// player not found
|
2020-11-08 00:26:44 +00:00
|
|
|
if (plr == nullptr) {
|
|
|
|
std::cout << "[WARN] doHeal: player ID not found" << std::endl;
|
2020-10-04 23:54:08 +00:00
|
|
|
return false;
|
2020-11-08 00:26:44 +00:00
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-13 03:20:51 +00:00
|
|
|
int healedAmount = PC_MAXHEALTH(plr->level) * amount / 1500;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
plr->HP += healedAmount;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (plr->HP > PC_MAXHEALTH(plr->level))
|
|
|
|
plr->HP = PC_MAXHEALTH(plr->level);
|
2020-10-04 23:54:08 +00:00
|
|
|
|
|
|
|
respdata[i].eCT = 1;
|
|
|
|
respdata[i].iID = plr->iID;
|
|
|
|
respdata[i].iHP = plr->HP;
|
|
|
|
respdata[i].iHealHP = healedAmount;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-10-04 23:54:08 +00:00
|
|
|
std::cout << (int)plr->iID << " was healed" << std::endl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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()) {
|
2020-09-15 14:46:52 +00:00
|
|
|
// not sure how to best handle this
|
2020-11-08 00:26:44 +00:00
|
|
|
std::cout << "[WARN] doDamage: mob ID not found" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-11-08 00:26:44 +00:00
|
|
|
Mob* mob = MobManager::Mobs[targetID];
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-29 21:27:48 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
if (plr == nullptr)
|
|
|
|
return false;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-13 03:20:51 +00:00
|
|
|
int damage = MobManager::hitMob(sock, mob, PC_MAXHEALTH(plr->level) * amount / 2000 + mob->appearanceData.iHP * amount / 2000);
|
2020-09-22 20:22:10 +00:00
|
|
|
|
2020-09-15 14:46:52 +00:00
|
|
|
respdata[i].eCT = 4;
|
2020-09-25 00:00:26 +00:00
|
|
|
respdata[i].iDamage = damage;
|
2020-09-18 16:37:26 +00:00
|
|
|
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
|
|
|
respdata[i].iHP = mob->appearanceData.iHP;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-09-18 16:37:26 +00:00
|
|
|
std::cout << (int)mob->appearanceData.iNPC_ID << " was damaged" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: Leech is specially encoded.
|
|
|
|
*
|
2020-11-12 00:59:52 +00:00
|
|
|
* It manages to fit inside the nanoPower<>() mold with only a slight hack,
|
2020-09-15 14:46:52 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
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) {
|
2020-09-15 14:46:52 +00:00
|
|
|
// this sanity check is VERY important
|
|
|
|
if (i != 0) {
|
|
|
|
std::cout << "[WARN] Player attempted to leech more than one mob!" << std::endl;
|
|
|
|
return false;
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-15 14:46:52 +00:00
|
|
|
sSkillResult_Damage *damagedata = (sSkillResult_Damage*)(((uint8_t*)healdata) + sizeof(sSkillResult_Heal_HP));
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return false;
|
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
int healedAmount = amount;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-29 21:27:48 +00:00
|
|
|
plr->HP += healedAmount;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-29 21:27:48 +00:00
|
|
|
if (plr->HP > PC_MAXHEALTH(plr->level))
|
2020-09-15 14:46:52 +00:00
|
|
|
plr->HP = PC_MAXHEALTH(plr->level);
|
|
|
|
|
|
|
|
healdata->eCT = 1;
|
|
|
|
healdata->iID = plr->iID;
|
|
|
|
healdata->iHP = plr->HP;
|
2020-09-29 21:27:48 +00:00
|
|
|
healdata->iHealHP = healedAmount;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (MobManager::Mobs.find(targetID) == MobManager::Mobs.end()) {
|
2020-09-15 14:46:52 +00:00
|
|
|
// not sure how to best handle this
|
|
|
|
std::cout << "[WARN] doLeech: mob ID not found" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-08 00:26:44 +00:00
|
|
|
Mob* mob = MobManager::Mobs[targetID];
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
int damage = MobManager::hitMob(sock, mob, amount * 2);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-15 14:46:52 +00:00
|
|
|
damagedata->eCT = 4;
|
2020-09-25 00:00:26 +00:00
|
|
|
damagedata->iDamage = damage;
|
2020-09-18 16:37:26 +00:00
|
|
|
damagedata->iID = mob->appearanceData.iNPC_ID;
|
|
|
|
damagedata->iHP = mob->appearanceData.iHP;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-09-18 16:37:26 +00:00
|
|
|
std::cout << (int)mob->appearanceData.iNPC_ID << " was leeched" << std::endl;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
|
|
|
return true;
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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;
|
2020-09-18 07:10:30 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
|
|
|
if (pair.second.plr->iID == targetID) {
|
|
|
|
plr = pair.second.plr;
|
|
|
|
break;
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// player not found
|
|
|
|
if (plr == nullptr) {
|
|
|
|
std::cout << "[WARN] doResurrect: player ID not found" << std::endl;
|
|
|
|
return false;
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
respdata[i].eCT = 1;
|
|
|
|
respdata[i].iID = plr->iID;
|
|
|
|
respdata[i].iRegenHP = plr->HP;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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;
|
2020-10-04 23:54:08 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
|
|
|
if (pair.second.plr->iID == targetID) {
|
|
|
|
plr = pair.second.plr;
|
|
|
|
break;
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// player not found
|
|
|
|
if (plr == nullptr) {
|
|
|
|
std::cout << "[WARN] doMove: player ID not found" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
respdata[i].eCT = 1;
|
|
|
|
respdata[i].iID = plr->iID;
|
|
|
|
respdata[i].iMapNum = plr->recallInstance;
|
|
|
|
respdata[i].iMoveX = plr->recallX;
|
|
|
|
respdata[i].iMoveY = plr->recallY;
|
|
|
|
respdata[i].iMoveZ = plr->recallZ;
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-09-15 14:46:52 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
template<class sPAYLOAD,
|
|
|
|
bool (*work)(CNSocket*,sPAYLOAD*,int,int32_t,int32_t,int16_t,int16_t,int16_t)>
|
2020-11-12 00:59:52 +00:00
|
|
|
void nanoPower(CNSocket *sock, int targetData[],
|
2020-11-08 00:26:44 +00:00
|
|
|
int16_t nanoID, int16_t skillID, int16_t duration, int16_t amount,
|
|
|
|
int16_t skillType, int32_t bitFlag, int16_t timeBuffID) {
|
2020-09-18 07:10:30 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (plr == nullptr)
|
|
|
|
return;
|
2020-10-19 17:26:14 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
if (skillType == EST_RETROROCKET_SELF || skillType == EST_RECALL) // rocket and self recall does not need any trailing structs
|
2020-11-12 00:59:52 +00:00
|
|
|
targetData[0] = 0;
|
2020-09-28 18:11:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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
|
2020-11-12 00:59:52 +00:00
|
|
|
resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + targetData[0] * sizeof(sPAYLOAD);
|
2020-10-19 17:26:14 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// validate response packet
|
2020-11-12 00:59:52 +00:00
|
|
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), targetData[0], sizeof(sPAYLOAD))) {
|
2020-11-08 00:26:44 +00:00
|
|
|
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size" << std::endl;
|
2020-09-18 07:10:30 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-15 14:46:52 +00:00
|
|
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
2020-09-18 07:10:30 +00:00
|
|
|
memset(respbuf, 0, resplen);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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));
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-18 07:10:30 +00:00
|
|
|
resp->iPC_ID = plr->iID;
|
2020-11-08 00:26:44 +00:00
|
|
|
resp->iSkillID = skillID;
|
|
|
|
resp->iNanoID = nanoID;
|
2020-09-26 22:16:15 +00:00
|
|
|
resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
2020-11-08 00:26:44 +00:00
|
|
|
resp->eST = skillType;
|
2020-11-12 00:59:52 +00:00
|
|
|
resp->iTargetCnt = targetData[0];
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
if (SkillTable[skillID].drainType == 2) {
|
|
|
|
if (SkillTable[skillID].targetType >= 2)
|
|
|
|
plr->iSelfConditionBitFlag |= bitFlag;
|
|
|
|
if (SkillTable[skillID].targetType == 3)
|
|
|
|
plr->iGroupConditionBitFlag |= bitFlag;
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
CNSocket *workSock = sock;
|
2020-10-19 17:26:14 +00:00
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
for (int i = 0; i < targetData[0]; i++) {
|
2020-11-08 00:26:44 +00:00
|
|
|
if (SkillTable[skillID].targetType == 3)
|
2020-11-12 00:59:52 +00:00
|
|
|
workSock = PlayerManager::getSockFromID(targetData[i+1]);
|
2020-11-08 00:26:44 +00:00
|
|
|
if (skillType == EST_RECALL || skillType == EST_RECALL_GROUP)
|
2020-11-12 00:59:52 +00:00
|
|
|
targetData[i+1] = plr->iID;
|
|
|
|
if (!work(workSock, respdata, i, targetData[i+1], bitFlag, timeBuffID, duration, amount))
|
2020-10-09 00:01:35 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-10-19 17:26:14 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
sock->sendPacket((void*)&respbuf, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
2020-11-12 00:59:52 +00:00
|
|
|
assert(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) == sizeof(sP_FE2CL_NANO_SKILL_USE));
|
2020-11-08 00:26:44 +00:00
|
|
|
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NANO_SKILL_USE, resplen);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
// 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));
|
2020-10-04 23:54:08 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-18 07:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-12 00:59:52 +00:00
|
|
|
// nano power dispatch table
|
|
|
|
std::vector<NanoPower> NanoPowers = {
|
|
|
|
NanoPower(EST_STUN, CSB_BIT_STUN, ECSB_STUN, nanoPower<sSkillResult_Damage_N_Debuff, doDamageNDebuff>),
|
2020-11-13 23:06:18 +00:00
|
|
|
NanoPower(EST_HEAL_HP, CSB_BIT_NONE, ECSB_NONE, nanoPower<sSkillResult_Heal_HP, doHeal>),
|
2020-11-12 00:59:52 +00:00
|
|
|
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>),
|
2020-11-13 23:06:18 +00:00
|
|
|
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>)
|
2020-09-18 16:37:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}; // namespace
|
2020-11-08 00:26:44 +00:00
|
|
|
#pragma endregion
|