mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-05-10 18:41:07 +00:00

Was getting frustrated by the inconsistency in our include statements, which were causing me problems. As a result, I went through and manually re-organized every include statement in non-core files. I'm just gonna copy my rant from Discord: FOR HEADER FILES (.hpp): - everything you use IN THE HEADER must be EXPLICITLY INCLUDED with the exception of things that fall under Core.hpp - you may NOT include ANYTHING ELSE FOR SOURCE FILES (.cpp): - you can #include whatever you want as long as the partner header is included first - anything that gets included by another include is fair game - redundant includes are ok because they'll be harmless AS LONG AS our header files stay lean. the point of this is NOT to optimize the number of includes used all around or make things more efficient necessarily. it's to improve readability & coherence and make it easier to avoid cyclical issues
387 lines
13 KiB
C++
387 lines
13 KiB
C++
#include "Nanos.hpp"
|
|
|
|
#include "servers/CNShardServer.hpp"
|
|
|
|
#include "PlayerManager.hpp"
|
|
#include "Missions.hpp"
|
|
#include "Abilities.hpp"
|
|
#include "NPCManager.hpp"
|
|
|
|
#include <cmath>
|
|
|
|
using namespace Nanos;
|
|
|
|
std::map<int32_t, NanoData> Nanos::NanoTable;
|
|
std::map<int32_t, NanoTuning> Nanos::NanoTunings;
|
|
|
|
#pragma region Helper methods
|
|
void Nanos::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm) {
|
|
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)
|
|
Missions::updateFusionMatter(sock, -(int)Missions::AvatarGrowth[plr->level-1]["m_iReqBlob_NanoCreate"]);
|
|
#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(resp, P_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, resp2, P_FE2CL_REP_PC_CHANGE_LEVEL);
|
|
}
|
|
|
|
void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
|
|
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
|
|
|
|
if (nanoID >= NANO_COUNT || nanoID < 0)
|
|
return; // sanity check
|
|
|
|
plr->activeNano = nanoID;
|
|
|
|
int16_t skillID = plr->Nanos[nanoID].iSkillID;
|
|
if (Abilities::SkillTable.count(skillID) > 0 && Abilities::SkillTable[skillID].drainType == SkillDrainType::PASSIVE)
|
|
resp.eCSTB___Add = 1; // passive buff effect
|
|
|
|
if (!silent) // silent nano death but only for the summoning player
|
|
sock->sendPacket(resp, P_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, pkt1, P_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(resp, P_FE2CL_REP_NANO_TUNE_SUCC);
|
|
return; // stop execution, don't run consumption logic
|
|
}
|
|
}
|
|
|
|
#ifndef ACADEMY
|
|
if (plr->fusionmatter < (int)Missions::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"]) // sanity check
|
|
return;
|
|
#endif
|
|
|
|
plr->fusionmatter -= (int)Missions::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];
|
|
}
|
|
}
|
|
|
|
sock->sendPacket(resp, P_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
|
|
int Nanos::nanoStyle(int nanoID) {
|
|
if (nanoID < 1 || nanoID >= (int)NanoTable.size())
|
|
return -1;
|
|
return NanoTable[nanoID].style;
|
|
}
|
|
|
|
bool Nanos::getNanoBoost(Player* plr) {
|
|
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) {
|
|
auto nano = (sP_CL2FE_REQ_NANO_EQUIP*)data->buf;
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_EQUIP_SUCC, resp);
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
// sanity checks
|
|
if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
|
|
return;
|
|
|
|
if (nano->iNanoID < 0 || nano->iNanoID >= NANO_COUNT)
|
|
return;
|
|
|
|
resp.iNanoID = nano->iNanoID;
|
|
resp.iNanoSlotNum = nano->iNanoSlotNum;
|
|
|
|
// Update player
|
|
plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID;
|
|
|
|
// 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(pkt, P_FE2CL_PC_BUFF_UPDATE);
|
|
}
|
|
|
|
// unsummon nano if replaced
|
|
if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum])
|
|
summonNano(sock, -1);
|
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_NANO_EQUIP_SUCC);
|
|
}
|
|
|
|
static void nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto nano = (sP_CL2FE_REQ_NANO_UNEQUIP*)data->buf;
|
|
INITSTRUCT(sP_FE2CL_REP_NANO_UNEQUIP_SUCC, resp);
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
// sanity check
|
|
if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
|
|
return;
|
|
|
|
resp.iNanoSlotNum = nano->iNanoSlotNum;
|
|
|
|
// unsummon nano if removed
|
|
if (plr->equippedNanos[nano->iNanoSlotNum] == plr->activeNano)
|
|
summonNano(sock, -1);
|
|
|
|
// update player
|
|
plr->equippedNanos[nano->iNanoSlotNum] = 0;
|
|
|
|
sock->sendPacket(resp, P_FE2CL_REP_NANO_UNEQUIP_SUCC);
|
|
}
|
|
|
|
static void nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto pkt = (sP_CL2FE_REQ_NANO_ACTIVE*)data->buf;
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
summonNano(sock, pkt->iNanoSlotNum);
|
|
|
|
DEBUGLOG(
|
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl;
|
|
)
|
|
}
|
|
|
|
static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
|
|
|
// validate request check
|
|
sP_CL2FE_REQ_NANO_SKILL_USE* pkt = (sP_CL2FE_REQ_NANO_SKILL_USE*)data->buf;
|
|
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;
|
|
}
|
|
|
|
int16_t skillID = plr->Nanos[plr->activeNano].iSkillID;
|
|
SkillData* skillData = &Abilities::SkillTable[skillID];
|
|
|
|
DEBUGLOG(
|
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
|
)
|
|
|
|
int boost = 0;
|
|
if (getNanoBoost(plr))
|
|
boost = 1;
|
|
|
|
plr->Nanos[plr->activeNano].iStamina -= Abilities::SkillTable[skillID].batteryUse[boost*3];
|
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
|
|
|
// TODO ABILITIES
|
|
std::vector<EntityRef> targetData = Abilities::matchTargets(skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1));
|
|
/*for (auto& pwr : Abilities::Powers)
|
|
if (pwr.skillType == Abilities::SkillTable[skillID].skillType)
|
|
pwr.handle(sock, targetData, nanoID, skillID, Abilities::SkillTable[skillID].durationTime[boost], Abilities::SkillTable[skillID].powerIntensity[boost]);*/
|
|
|
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
summonNano(sock, -1);
|
|
}
|
|
|
|
static void nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
|
|
setNanoSkill(sock, skill);
|
|
}
|
|
|
|
static void nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto skillGM = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
|
|
setNanoSkill(sock, skillGM);
|
|
}
|
|
|
|
static void nanoRecallRegisterHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto 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->x;
|
|
response.iY = plr->recallY = npc->y;
|
|
response.iZ = plr->recallZ = npc->z;
|
|
sock->sendPacket(response, P_FE2CL_REP_REGIST_RXCOM);
|
|
}
|
|
|
|
static void nanoRecallHandler(CNSocket* sock, CNPacketData* data) {
|
|
auto recallData = (sP_CL2FE_REQ_WARP_USE_RECALL*)data->buf;
|
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
Player* otherPlr = PlayerManager::getPlayerFromID(recallData->iGroupMemberID);
|
|
if (otherPlr == nullptr)
|
|
return;
|
|
|
|
// 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;
|
|
|
|
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(response, P_FE2CL_REP_WARP_USE_RECALL_FAIL);
|
|
}
|
|
}
|
|
|
|
static void nanoPotionHandler(CNSocket* sock, CNPacketData* data) {
|
|
Player* player = PlayerManager::getPlayer(sock);
|
|
|
|
// sanity checks
|
|
if (player->activeNano == -1 || player->batteryN == 0)
|
|
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(response, P_FE2CL_REP_CHARGE_NANO_STAMINA);
|
|
// now update serverside
|
|
player->batteryN -= difference;
|
|
player->Nanos[nano.iID].iStamina += difference;
|
|
|
|
}
|
|
|
|
void Nanos::init() {
|
|
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_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);
|
|
}
|