2021-03-17 19:07:40 +00:00
|
|
|
#include "servers/CNShardServer.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Nanos.hpp"
|
2020-08-20 15:43:37 +00:00
|
|
|
#include "PlayerManager.hpp"
|
2020-09-18 07:10:30 +00:00
|
|
|
#include "NPCManager.hpp"
|
2021-03-13 22:55:16 +00:00
|
|
|
#include "Combat.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Missions.hpp"
|
|
|
|
#include "Groups.hpp"
|
2021-03-13 20:22:29 +00:00
|
|
|
#include "Abilities.hpp"
|
2020-08-20 15:43:37 +00:00
|
|
|
|
2021-01-09 15:27:58 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
using namespace Nanos;
|
2021-03-16 21:06:10 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
std::map<int32_t, NanoData> Nanos::NanoTable;
|
|
|
|
std::map<int32_t, NanoTuning> Nanos::NanoTunings;
|
2020-09-25 21:05:36 +00:00
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
#pragma region Helper methods
|
2021-03-16 22:29:13 +00:00
|
|
|
void Nanos::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm) {
|
2021-03-16 21:06:10 +00:00
|
|
|
if (nanoID <= 0 || nanoID >= NANO_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
int level = plr->level;
|
|
|
|
|
|
|
|
#ifndef ACADEMY
|
|
|
|
level = nanoID < plr->level ? plr->level : nanoID;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
plr->level = level;
|
|
|
|
|
|
|
|
if (spendfm)
|
2021-03-16 22:29:13 +00:00
|
|
|
Missions::updateFusionMatter(sock, -(int)Missions::AvatarGrowth[plr->level-1]["m_iReqBlob_NanoCreate"]);
|
2021-03-16 21:06:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Send to client
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_NANO_CREATE_SUCC, resp);
|
|
|
|
resp.Nano.iID = nanoID;
|
|
|
|
resp.Nano.iStamina = 150;
|
|
|
|
resp.iQuestItemSlotNum = slot;
|
|
|
|
resp.iPC_Level = level;
|
|
|
|
resp.iPC_FusionMatter = plr->fusionmatter;
|
|
|
|
|
|
|
|
if (plr->activeNano > 0 && plr->activeNano == nanoID)
|
|
|
|
summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs
|
|
|
|
|
|
|
|
// Update player
|
|
|
|
plr->Nanos[nanoID] = resp.Nano;
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_CHANGE_LEVEL, resp2);
|
|
|
|
|
|
|
|
resp2.iPC_ID = plr->iID;
|
|
|
|
resp2.iPC_Level = level;
|
|
|
|
|
|
|
|
// Update other players' perception of the player's level
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
|
2021-03-16 21:06:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
|
|
|
|
resp.iActiveNanoSlotNum = slot;
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
if (slot > 2 || slot < -1)
|
|
|
|
return; // sanity check
|
|
|
|
|
|
|
|
int16_t nanoID = slot == -1 ? 0 : plr->equippedNanos[slot];
|
|
|
|
|
|
|
|
if (slot != -1 && plr->Nanos[nanoID].iSkillID == 0)
|
|
|
|
return; // prevent powerless nanos from summoning
|
|
|
|
|
|
|
|
plr->nanoDrainRate = 0;
|
|
|
|
int16_t skillID = plr->Nanos[plr->activeNano].iSkillID;
|
|
|
|
|
|
|
|
// passive nano unbuffing
|
|
|
|
if (SkillTable[skillID].drainType == 2) {
|
|
|
|
std::vector<int> targetData = findTargets(plr, skillID);
|
|
|
|
|
|
|
|
for (auto& pwr : NanoPowers)
|
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType)
|
|
|
|
nanoUnbuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, 0,(SkillTable[skillID].targetType == 3));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nanoID >= NANO_COUNT || nanoID < 0)
|
|
|
|
return; // sanity check
|
|
|
|
|
|
|
|
plr->activeNano = nanoID;
|
|
|
|
skillID = plr->Nanos[nanoID].iSkillID;
|
|
|
|
|
|
|
|
// passive nano buffing
|
|
|
|
if (SkillTable[skillID].drainType == 2) {
|
|
|
|
std::vector<int> targetData = findTargets(plr, skillID);
|
|
|
|
|
|
|
|
int boost = 0;
|
|
|
|
if (getNanoBoost(plr))
|
|
|
|
boost = 1;
|
|
|
|
|
|
|
|
for (auto& pwr : NanoPowers) {
|
|
|
|
if (pwr.skillType == SkillTable[skillID].skillType) {
|
|
|
|
resp.eCSTB___Add = 1; // the part that makes nano go ZOOMAZOOM
|
|
|
|
plr->nanoDrainRate = SkillTable[skillID].batteryUse[boost*3];
|
|
|
|
|
|
|
|
pwr.handle(sock, targetData, nanoID, skillID, 0, SkillTable[skillID].powerIntensity[boost]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!silent) // silent nano death but only for the summoning player
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_ACTIVE_SUCC, sizeof(sP_FE2CL_REP_NANO_ACTIVE_SUCC));
|
|
|
|
|
|
|
|
// Send to other players, these players can't handle silent nano deaths so this packet needs to be sent.
|
|
|
|
INITSTRUCT(sP_FE2CL_NANO_ACTIVE, pkt1);
|
|
|
|
pkt1.iPC_ID = plr->iID;
|
|
|
|
pkt1.Nano = plr->Nanos[nanoID];
|
|
|
|
PlayerManager::sendToViewable(sock, (void*)&pkt1, P_FE2CL_NANO_ACTIVE, sizeof(sP_FE2CL_NANO_ACTIVE));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) {
|
|
|
|
if (skill->iNanoID >= NANO_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
|
|
|
if (plr->activeNano > 0 && plr->activeNano == skill->iNanoID)
|
|
|
|
summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs
|
|
|
|
|
|
|
|
sNano nano = plr->Nanos[skill->iNanoID];
|
|
|
|
nano.iSkillID = skill->iTuneID;
|
|
|
|
plr->Nanos[skill->iNanoID] = nano;
|
|
|
|
|
|
|
|
// Send to client
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_TUNE_SUCC, resp);
|
|
|
|
resp.iNanoID = skill->iNanoID;
|
|
|
|
resp.iSkillID = skill->iTuneID;
|
|
|
|
resp.iPC_FusionMatter = plr->fusionmatter;
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef ACADEMY
|
2021-03-16 22:29:13 +00:00
|
|
|
if (plr->fusionmatter < (int)Missions::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"]) // sanity check
|
2021-03-16 21:06:10 +00:00
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
plr->fusionmatter -= (int)Missions::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"];
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
|
|
|
|
|
|
|
|
DEBUGLOG(
|
|
|
|
std::cout << PlayerManager::getPlayerName(plr) << " set skill id " << skill->iTuneID << " for nano: " << skill->iNanoID << std::endl;
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0=A 1=B 2=C -1=Not found
|
2021-03-16 22:29:13 +00:00
|
|
|
int Nanos::nanoStyle(int nanoID) {
|
2021-03-16 21:06:10 +00:00
|
|
|
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
|
|
|
|
return -1;
|
|
|
|
return NanoTable[nanoID].style;
|
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
bool Nanos::getNanoBoost(Player* plr) {
|
2021-03-16 21:06:10 +00:00
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
if (plr->equippedNanos[i] == plr->activeNano)
|
|
|
|
if (plr->iConditionBitFlag & (CSB_BIT_STIMPAKSLOT1 << i))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
static void nanoEquipHandler(CNSocket* sock, CNPacketData* data) {
|
2020-08-22 18:02:08 +00:00
|
|
|
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-12-01 19:18:01 +00:00
|
|
|
if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
|
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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) {
|
2020-08-22 18:02:08 +00:00
|
|
|
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-12-01 19:18:01 +00:00
|
|
|
if (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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoGMGiveHandler(CNSocket* sock, CNPacketData* data) {
|
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
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
|
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-08-25 18:30:20 +00:00
|
|
|
summonNano(sock, pkt->iNanoSlotNum);
|
2020-08-23 00:52:54 +00:00
|
|
|
|
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
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void 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
|
|
|
|
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-27 04:46:29 +00:00
|
|
|
std::vector<int> targetData = findTargets(plr, skillID, data);
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
int boost = 0;
|
2020-11-23 23:42:34 +00:00
|
|
|
if (getNanoBoost(plr))
|
|
|
|
boost = 1;
|
2020-11-08 00:26:44 +00:00
|
|
|
|
|
|
|
plr->Nanos[plr->activeNano].iStamina -= SkillTable[skillID].batteryUse[boost*3];
|
2020-11-29 01:43:42 +00:00
|
|
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
2020-11-08 00:26:44 +00:00
|
|
|
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-11-29 01:43:42 +00:00
|
|
|
|
|
|
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
|
|
summonNano(sock, -1);
|
2020-08-22 18:02:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
|
2020-08-22 18:02:08 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) {
|
2020-09-09 01:15:25 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data) {
|
2020-11-08 00:26:44 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoRecallHandler(CNSocket* sock, CNPacketData* data) {
|
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);
|
2020-12-26 19:09:33 +00:00
|
|
|
if (otherPlr == nullptr)
|
|
|
|
return;
|
2020-11-08 00:26:44 +00:00
|
|
|
|
2020-12-28 15:12:23 +00:00
|
|
|
// ensure the group member is still in the same IZ
|
|
|
|
if (otherPlr->instanceID != plr->instanceID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// do not allow hypothetical recall points in lairs to mess with the respawn logic
|
|
|
|
if (PLAYERID(plr->instanceID) != 0)
|
|
|
|
return;
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void nanoPotionHandler(CNSocket* sock, CNPacketData* data) {
|
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
|
2020-12-01 19:18:01 +00:00
|
|
|
if (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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
void Nanos::init() {
|
2021-03-16 21:06:10 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
|
|
|
|
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);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO_SKILL, nanoSkillSetGMHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_SKILL_USE, nanoSkillUseHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_RXCOM, nanoRecallRegisterHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_WARP_USE_RECALL, nanoRecallHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_CHARGE_NANO_STAMINA, nanoPotionHandler);
|
2020-11-23 23:42:34 +00:00
|
|
|
}
|